v-model绑定的值并没有在视图定义_在Unity 2019.2中扩展Shader Graph,实现自定义光照...

随着Unity 2019.1的发布,Shader Graph着色器视图资源包正式脱离预览阶段。在Unity 2019.2中,我们为Shader Graph着色器视图加入了更多新功能。

Unity 2019.2中Shader Graph新功能

自定义函数和子视图更新

为了在Shader Graph着色器视图中使用自定义代码,你可以使用全新的Custom Function节点自定义输入和输出,对其重新排序,并将自定义函数直接插入节点中,或者引用外部文件。

Sub Graphs子视图功能也进行了更新,你可以基于不同类型,可自定义名称和可重新排序的端口,为子视图自定义想要的输出数据。此外,子视图的Blackboard面板现在支持主视图支持的所有数据类型。

颜色模式和精度模式

你可以使用Shader Graph着色器视图轻松创建出强大而优化的着色器。

在Unity 2019.2中,你可以在视图中手动设置计算的精度,既可以设置整个视图的计算精度,也可以设置单个节点的计算精度。新的Color Modes颜色模式可以轻松快捷地可视化精度流,节点分类以及为特定用途显示自定义颜色。

了解更多关于新功能的信息,请访问Shader Graph着色器视图文档:

https://docs.unity3d.com/Packages/com.unity.shadergraph@6.9/manual/index.html

示例项目

为了帮助你熟悉使用新的自定义函数工作流程,我们提供了一个示例项目。该示例项目将向你展示如何使用Custom Function节点,以及如何为轻量级渲染管线LWRP编写自定义光照着色器。

964b69a9d8a2fae6e9ea7a413fd303c9.gif

请访问GitHub下载示例项目:

https://github.com/Unity-Technologies/ShaderGraph-Custom-Lighting

温馨提醒:打开示例项目,请确保使用Unity 2019.2和LWRP资源包6.9.1或更高版本。

从主光源获取数据

首先,我们需要从场景中的主光源获取信息。点击Create > Shader > Unlit Graph,创建新的Unlit Shader Graph着色器视图。在Create Node菜单中,找到新的Custom Function节点,单击右上角齿轮按钮,打开节点菜单。

在节点菜单中,我们可以添加输入和输出数据。我们添加二个输出接口,它们分别是Direction和Color,接口类型为Vector 3 。如果遇到“undeclared identifier”(未声明标识符)警告提示,不必担心,在我们添加代码后,警告会自动消失。

在Type下拉菜单中,选择String,把函数名称改为MainLight。现在,我们可以开始在文本框添加自定义代码。

bd298965728d8d3ffda371fa37062f92.png

首先,我们要使用#ifdef SHADERGRAPH_PREVIEW标识。

由于节点上的预览方框无法访问光线数据,所以我们需要告诉节点在视图内的预览框显示什么内容。#ifdef会让编译器在不同情况下使用不同代码。首先定义输出接口的回退值。

#if SHADERGRAPH_PREVIEW

       Direction = half3(0.5, 0.5, 0);

       Color = 1;

接下来,我们使用#else告诉编译器不在预览框内的时候要做什么。

我们会在此获取光线数据,使用LWRP资源包的内置函数GetMainLight()。我们可以使用获取到的信息来指定Direction和Color输出。

自定义函数代码如下。

#if SHADERGRAPH_PREVIEW

       Direction = half3(0.5, 0.5, 0);

       Color = 1;

#else

       Light light = GetMainLight();

       Direction = light.direction;

       Color = light.color;

#endif

现在,我们可以把该节点添加到节点分组中,从而标记它的行为。

右键单击节点,选择Create Group from Selection,然后重命名分组标题来表示节点的行为。我们在分组标题输入Get Main Light。

cfea15da6d1d4ed920cc93f9b6a2e432.png

得到光线数据后,我们可以计算着色效果。我们打算实现标准朗伯光照,所以首先要获取世界法线向量和光线方向的点积。

我们将点积结果传入Saturate节点,使它和光线颜色相乘,然后连接到Unlit Master节点的Color接口,我们预览图应该会更新一些自定义着色。

ab42f8589cb9d37a9e929b3c803dc85b.png

使用自定义函数的文件模式

我们了解了如何使用Custom Function节点获取光线数据,下面扩展我们的函数。该函数接下来会从主光源获取衰减值,方向和颜色。

由于这个函数更为复杂,因此我们要切换为文件模式,使用HLSL包含文件。这样可以在代码编辑器编写更复杂的函数,然后再加入到视图中,这也意味着我们有合适的位置来调试代码。

首先,我们在项目的Assets > Include文件夹中,打开CustomLighting包含文件。

现在我们只关注MainLight_half函数,代码如下。

void MainLight_half(float3 WorldPos, out half3 Direction, out half3 Color, out half DistanceAtten, out half ShadowAtten)

{

#if SHADERGRAPH_PREVIEW

   Direction = half3(0.5, 0.5, 0);

   Color = 1;

   DistanceAtten = 1;

   ShadowAtten = 1;

#else

#if SHADOWS_SCREEN

   half4 clipPos = TransformWorldToHClip(WorldPos);

   half4 shadowCoord = ComputeScreenPos(clipPos);

#else

   half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);

#endif

   Light mainLight = GetMainLight(shadowCoord);

   Direction = mainLight.direction;

   Color = mainLight.color;

   DistanceAtten = mainLight.distanceAttenuation;

   ShadowAtten = mainLight.shadowAttenuation;

#endif

}

MainLight_half函数有新的输入和输出数据,因此我们要回到Custom Function节点,添加相应数据。我们添加二个新输出数据:一个是DistanceAtten,表示距离衰减;另一个是ShadowAtten,表示阴影衰减。添加新的输入数据WorldPos,它表示世界位置。

有了相应的输入和输出数据后,我们可以引用之前的包含文件,把Type下拉菜单设为File。在Source部分,找到之前的包含文件,选中并引用该文件。现在我们需要告诉节点要使用哪个函数。在Name方框中,输入MainLight。

3d7f5011c7a267f3b781f2afd1c05624.png

我们发现该包含文件的函数名结尾有_half,但我们的名称选项中却没有_half。这是因为Shader Graph编译器会把精度格式附加给每个函数名。

由于我们正在定义函数,我们需要通过源代码,告诉编译器我们的函数使用什么精度格式。但是在节点中,我们只需要引用主要函数名称即可。我们可以创建函数的副本,让它使用float值,从而在float精度模式下编译。

 “精度”的颜色模式允许我们轻松跟踪视图中每个节点设置的精度,蓝色表示float浮点值,红色表示half半精度值。

我们可能会在其它位置使用该函数,让Custom Function节点可以重用的最简单方法,是把它包装到Sub Graph子视图中。选中节点和其分组,单击右键,选择Convert to Sub-graph。

我们把该子视图命名为Get Main Light。在子视图中,我们把需要的输出接口添加到子视图的输出节点,把节点的输出部分连接到子视图的输出部分。然后添加世界位置节点,把它连接到输入部分。

641528f03f725948e4069088c7bd26f2.png

保存子视图,回到Unlit着色器视图。我们要添加二个Multiply节点到现有的视图中。

首先,把二个衰减输出相乘,把乘积的输出结果再乘以光线颜色,把前面的结果乘以NdotL节点分组的结果,从而计算出基本着色中的衰减。

84200fb6e42ef4c747fea8d2d533914b.png

创建直接镜面着色器

我们制作的着色器适合无光泽对象,但如果我们想要光泽效果,应该怎么做?我们可以给着色器添加镜面计算。

我们会使用另一个Custom Function节点,把它包装到名称为Direct Specular的子视图。再次查看CustomLighting包含文件,我们现在要引用该文件的另一个函数。

void DirectSpecular_half(half3 Specular, half Smoothness, half3 Direction, half3 Color, half3 WorldNormal, half3 WorldView, out half3 Out)

{

#if SHADERGRAPH_PREVIEW

   Out = 0;

#else

   Smoothness = exp2(10 * Smoothness + 1);

   WorldNormal = normalize(WorldNormal);

   WorldView = SafeNormalize(WorldView);

   Out = LightingSpecular(Color, Direction, WorldNormal, WorldView, half4(Specular, 0), Smoothness);

#endif

}

该函数会执行简单的镜面计算,该函数的子视图也包含Blackboard上的输入数据。

0243e8368b0ced62b7f74094b8dc2b40.png

要确保新节点的输入和输出接口符合函数的输入和输出数据。给Blackboard添加属性的方法很简单:单击面板右上方的加号(+)图标,选择数据类型即可。

双击显示名称的椭圆框来重命名输入数据,把椭圆框拖到视图中,从而把它添加到视图。最后,更新子视图的输出接口,保存整个视图。

现在镜面计算已经设置好,我们可以回到Unlit着色器视图,通过Create Node菜单添加该功能。把Attenuation节点分组的输出连接到Direct Specular子视图的Color输入。

然后,把Get Main Light函数的Direction输出连接到镜面子视图。把NdotL和Attenuation的输出进行相乘,把乘积结果加上Direct Specular子视图的输出,然后把相加结果连接到Unlit Master节点的Color输出。

1bd683379551d449e60c1fe9d3a2c187.png

处理多个光源

LWRP的主光源是对物体来说最亮的定向光,通常这种光是阳光。为了提升低端硬件的性能,LWRP会分别计算主光源和其它光源。

要确保着色器正确计算场景中的所有光线,而不仅仅计算最亮的定向光,我们需要在函数创建一个循环。添加额外光线数据,我们会使用新的子视图来包装新的Custom Function节点,现在查看CustomLighting包含文件的AdditionalLight_float函数。

void AdditionalLights_hlaf(half3 SpecColor, half Smoothness, half3 WorldPosition, half3 WorldNormal, half3 WorldView, out half3 Diffuse, out half3 Specular)

{

   half3 diffuseColor = 0;

   half3 specularColor = 0;

#ifndef SHADERGRAPH_PREVIEW

   Smoothness = exp2(10 * Smoothness + 1);

   WorldNormal = normalize(WorldNormal);

   WorldView = SafeNormalize(WorldView);

   int pixelLightCount = GetAdditionalLightsCount();

   for (int i = 0; i < pixelLightCount; ++i)

   {

       Light light = GetAdditionalLight(i, WorldPosition);

       half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);

       diffuseColor += LightingLambert(attenuatedLightColor, light.direction, WorldNormal);

       specularColor += LightingSpecular(attenuatedLightColor, light.direction, WorldNormal, WorldView, half4(SpecColor, 0), Smoothness);

   }

#endif

   Diffuse = diffuseColor;

   Specular = specularColor;

}

和前面一样,我们要在Custom Function节点的文件引用部分使用AdditionalLights函数,并创建所有对应的输入和输出数据。在子视图中包装这个节点,并在该子视图的Blackboard面板公开Specular Color和Specular Smoothness。

我们把Position节点、Normal Vector节点和View Direction节点分别连接到子视图的World Position、World Normal和World Space View Direction。

在设置好函数后,我们要使用函数。首先,打开之前的Unlit主视图,把它折叠为子视图。选中视图的所有节点,右键单击Convert to Sub-graph。

移除最后一个Add节点,把输出连接到子视图的输出接口。建议同时创建Specular和Smoothness的输入属性。

84200fb6e42ef4c747fea8d2d533914b.png

现在,我们可以结合主光源和其它光源的计算结果。在Unlit主视图中,为Additional Light的计算创建一个新节点,使该节点的计算和Main Light的计算同步进行。

把Main Light的Diffuse输出和Additional Lights的Diffuse输出相加,把它们各自的Specular输出也进行相加,最后把二个结果再次相加。

bedad2c33f15e077be2927a703a27950.png

创建简单的卡通着色器

我们已经知道如何在LWRP项目中,从场景的所有光线获取数据,我们如何利用这些知识呢?对于着色器的自定义光照,最常见的一个用法是实现经典的卡通着色器。如果拥有所有光线数据,创建卡通着色器的过程会很简单。

首先,获取已完成的所有光线计算,把它们包装到子视图中。这样可以提高最终着色器的可读性。别忘了移除最后的Add节点,把Diffuse和Specular用作子视图输出节点的独立输出接口。

我们有很多方法创建卡通着色效果,但在本示例中,我们会使用光线强度查询渐变纹理的颜色,该方法通常称为渐变光照(Ramp Lighting)。

79a166aa35b4696a843811d5914f794e.png

我们在示例项目中加入了渐变光照所需的示例纹理资源,也可以通过采样渐变,在渐变光照中使用动态渐变效果。

第一步是把Diffuse和Specular的强度从RGB数值转换为HSV数值。这让我们可以通过使用光线颜色的强度即HSV数值,决定着色器上的亮度,并且可以帮助我们沿着资源的水平轴,采样不同位置的纹理。

在UV的Y通道使用静态数值,决定图像中从上到下哪个位置要进行采样。我们可以使用该静态值作为索引,在一个纹理资源中引用项目的多个光照渐变。

d91d20696833bf67c01e8808838959f5.png

设置UV数值后,使用Sample Texture 2D LOD节点来采样渐变纹理。Sample Texture 2D LOD节点很重要,如果我们使用常见的Sample Texture 2D节点,渐变将自动在场景中mimapped,这样较远的对象会有不同的光照行为。

使用Sample Texture 2D LOD节点可以手动确定mip等级。此外,由于渐变纹理的高度只有2个像素,我们要为纹理创建自定义Sampler State采样器状态。

为了确保正确地采样纹理,我们把Filter设为Point,把Wrap设为Clamp。我把这些设置作为属性公开在Blackboard面板,使用户可以在纹理资源发生变化时修改设置。

f836775f8d568d8226c78f45f791bac7.png

最后,我们把漫反射计算出的渐变采样乘以颜色属性Diffuse,从而改变对象的颜色。把镜面计算出的渐变采样加上Diffuse输出,然后把最终颜色连接到Master节点。

795caccfb9e47a349a3607693e90beab.png

扩展自定义光照

我们可以扩展这个简单的自定义光照设置,把它应用到各种场景的不同用例。在示例项目中,我们加入了完整的场景,该场景通过使用自定义光照设置的着色器进行配置。

该着色器也包含顶点动画,简单的次表面散射估算,以及使用深度的折射和着色效果。你可以下载示例项目,查看示例资源,从而了解更多高级方法。

3412cb35062c2179bcf12de83c9dffc9.png

小结

如果想要讨论Shader Graph着色器视图和使用该功能制作的着色器,欢迎访问官方论坛:

https://forum.unity.com/forums/shader-graph.346/

下载Unity Connect APP,请点击此处。 观看部分Unity官方视频,请关注B站帐户:Unity官方。

Connect.unity.com/g/discussion

推荐阅读

Shader Graph着色器视图示例项目介绍

Shader Graph着色器视图自定义节点API:Code Function Node

达哥课堂|使用Shader Graph制作飘动的巨龙

使用Shader Graph实现《塞尔达传说:旷野之息》风格的着色器

使用Shader Graph着色器视图制作动画材质

三款设计逼真环境的资源插件

Unity 2019.2正式版发布

《Afterlight》中泰坦星环境的实现方法

官方活动

成为Unity Buddy,享受专属福利

8月21日前加入Unity Connect,创建个人频道,成为与Unity社区同行的伙伴-Unity Buddy,享受专属福利。

Unity Connect评论区支持你认可的Buddy,有机会获得Buddy送出的Unity周边。

了解如何成为Unity Buddy,请点击此处。

543d65abe0fda2e384c4531869f9c436.png

喜欢本文,请点“在看”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值