TA-Shader-文件 Unity3D Shader 新手教程之三:更加真实的积雪
发布于
2018-4-7
6677
38
TA资源类型
TA资源类型: 算法思路 
shader资源类型: shader代码 
适用引擎: unity 
资源介绍: 如果你满足以下条件,我建议你阅读这篇教程:
你想知道如何在表面着色器中进行混色(blend colour)
你想实现一个更加真实的积雪效果

您需要 登录 才可以下载或查看,没有账号?注册

x

引论

我觉得有雪区域向无雪区域过渡的有些突兀,感觉更像白色的油漆涂在了岩石上,而不是积雪!为了使我们积雪shader的效果更加完美所以下一步需要做的是允许积雪和岩石纹理同时进行渲染,从而达到混色的效果。

我们只要对表面着色器的pixel处理方式进行一些修改就可以达到很好的积雪效果,而且这也将证明saturate函数是非常有用的。

准备工作

教程第二部分中的_SnowLevel(表示积雪程度的变量)决定了该像素是否应该赋以积雪的颜色,此处我们也会使用_SnowLevel来使积雪的边缘颜色变为半透明的白色。而不是之前完全的白色。当该像素的法向与雪落下的方向越一致(即两者夹角越小),则该像素的不透明度越高,换句话说,就是此处积雪更多,直到完全不透明,此处的像素颜色值将变为白色(事实上就是变成了积雪的颜色)。

实现该Shader

和教程第二部分的shader最大的差别在于,第二部分的shader中,每个像素的颜色值要不是积雪颜色,要不就是纹理颜色,而此部分shader将此非黑即白的判断改进成颜色值的变化过程。这导致我们将重写了shader的逻辑部分,并使用数学计算的方法来代替if的条件判断。

首先我们需要一个属性值(Property)来表示我们混合积雪颜色的程度。我们给该属性值取名为_Wetness(湿润度,如果该值越大,则混色中积雪颜色所占比例越低,这表明积雪越湿润,则雪的颜色越少,都化成水了):

    _Wetness ("Wetness", Range(0, 0.5)) = 0.3

接着我们需要一个变量在shader中表示该属性值

    float _Wetness;

我们使用下面这个公式计算difference变量。

    float difference = dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) - lerp(1,-1,_Snow);

其实第二部分的代码中也可以使用difference变量(比如当difference大于0就将该区域赋以积雪颜色,小于0就使用原先纹理颜色),关键在于对于difference的处理和应用不同。

    void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex);
                o.Normal = UnpackNormal (tex2D (_Bump, IN.uv_Bump));
                float difference = dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) - lerp(1,-1,_Snow);;
                difference = saturate(difference / _Wetness);
                o.Albedo = difference*_SnowColor.rgb + (1-difference) *c;
                o.Alpha = c.a;
            }

我们可以看到计算完difference后,首先将difference除以_Wetness(此处我们可以一窥_Wetness的具体用途),然后对difference使用saturate函数。saturate函数的具体用处如下:

    saturate函数将给定的值规范到0~1之间(大于1的置为1,小于0的置为0,其他不变)
    所以当difference小于0时,即此处无积雪,我们通过saturate函数置difference为0。
    如果_Wetness的默认值为0.3,并且法向和落雪方向夹角在73度~90度之间,则difference的值健在0~1之间,大于90度,则为0,小于73度则为1.

一个余弦值的范围是1到-1,差距为2,代表了0度到180度的变化(一直从同向变化到反向),所以要是difference在0~1之间,则点乘的结果(即cos值)必须在0~0.3之间,所以对应夹角为73度~90度(cos73=0.3,cos90=1)。

然后我们用difference乘以积雪颜色,difference代表积雪颜色在最终颜色所占的比例。我们再设置纹理本身颜色的比重为1-difference,相加得到最终颜色。其中夹角小于73度的区域将全部填充为积雪颜色,在73度~90度之间(也就是积雪的边缘)有一积雪过渡效果,大于90度的将使用纹理原来颜色。

    o.Albedo = difference*_SnowColor.rgb + (1-difference) *c;

修正顶点数据

如果我们的的雪十分湿润(wet),那么只使用原先的shader的话,我们会发现积雪少的区域,模型也会增厚,根本原因是我们的顶点变化方式没有随积雪混色方式(主要是添加了_Wetness参数)变化而变化,产生的效果将不真实。所以我们应该将参数_Wetness应用到计算中,使模型的增厚效果随_Wetness变化。

    void vert (inout appdata_full v) {
          if(dot(v.normal, _SnowDirection.xyz) >= lerp(1,-1, ((1-_Wetness) * _Snow*2)/3))
          {
                v.vertex.xyz += (_SnowDirection.xyz + v.normal) * _SnowDepth * _Snow;
          }
    }

可以看到,我们决定是否增厚模型的判断变成了是否大于

lerp(1,-1, ((1-_Wetness) * _Snow*2)/3))

如果_Wetness为0,则一切都没变化,如果_Wetness变到了其最大范围0.5,则_Snow所占的比例不再是上文的2/3,而是1/3了 - 使模型增厚范围增加了50%。

下面是最终的效果图:

1.png

源代码

资源和代码在这里。

    Shader "Custom/Realistic Snow" {
        Properties {
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _Bump ("Bump", 2D) = "bump" {}
            _Snow ("Snow Level", Range(0,1) ) = 0
            _SnowColor ("Snow Color", Color) = (1.0,1.0,1.0,1.0)
            _SnowDirection ("Snow Direction", Vector) = (0,1,0)
            _SnowDepth ("Snow Depth", Range(0,0.2)) = 0.1
            _Wetness ("Wetness", Range(0, 0.5)) = 0.3
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 200
     
            CGPROGRAM
            #pragma surface surf Lambert vertex:vert
     
            sampler2D _MainTex;
            sampler2D _Bump;
            float _Snow;
            float4 _SnowColor;
            float4 _SnowDirection;
            float _SnowDepth;
            float _Wetness;
     
            struct Input {
                float2 uv_MainTex;
                float2 uv_Bump;
                float3 worldNormal;
                INTERNAL_DATA
            };
     
             void vert (inout appdata_full v) {
                       //将_SnowDirection转化到模型局部坐标系下
                       float4 sn = mul(UNITY_MATRIX_IT_MV, _SnowDirection);
     
                      if(dot(v.normal, sn.xyz) >= lerp(1,-1, (_Snow*2)/3))
                      {
                          v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
                      }
                    }
     
            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex);
                o.Normal = UnpackNormal (tex2D (_Bump, IN.uv_Bump));
                half difference = dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) - lerp(1,-1,_Snow);
                difference = saturate(difference / _Wetness);
                o.Albedo = difference*_SnowColor.rgb + (1-difference) *c;
                o.Alpha = c.a;
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

0.jpg
还没有设置签名!您可以在此展示你的链接,或者个人主页!

使用道具 举报 登录

回复 <
qq_小蜗牛_MjJ  发表于 2018-6-9 14:12:54  
2#
我们先定一个能达到的小目标,先赚它一亿元素币
回复 收起回复
使用道具
momocongzi  发表于 2018-6-23 08:45:53  
3#
路过
回复 收起回复
使用道具
小四  发表于 2018-10-18 14:26:22  
5#
酷炫的不得了
回复 收起回复
使用道具
海中鱼  发表于 2018-10-18 14:53:22  
6#

想要成大触,天天上元素!
回复 收起回复
使用道具
海中鱼  发表于 2018-10-18 14:53:25  
7#

想要成大触,天天上元素!
回复 收起回复
使用道具
海中鱼  发表于 2018-10-18 14:53:28  
8#

想要成大触,天天上元素!
回复 收起回复
使用道具
海中鱼  发表于 2018-10-18 14:53:31  
9#

想要成大触,天天上元素!
回复 收起回复
使用道具
海中鱼  发表于 2018-10-18 14:53:34  
10#

想要成大触,天天上元素!
回复 收起回复
使用道具
海中鱼  发表于 2018-10-18 14:53:36  
11#

想要成大触,天天上元素!
回复 收起回复
使用道具
海中鱼  发表于 2018-10-18 14:53:39  
12#

想要成大触,天天上元素!
回复 收起回复
使用道具
海中鱼  发表于 2018-10-18 14:53:43  
13#

想要成大触,天天上元素!
回复 收起回复
使用道具
panpanda  发表于 2018-10-18 15:11:00  
14#
我们先定一个能达到的小目标,先赚它一亿元素币
回复 收起回复
使用道具
迷路的小星扬  发表于 2018-10-18 20:29:27  
15#
666666666
回复 收起回复
使用道具
zryhhahah  发表于 2018-10-19 05:44:44  
16#
很好 很强大哦也
回复 收起回复
使用道具
ud_terry  发表于 2018-10-19 09:08:25  
17#
向大佬低头,大佬辛苦了
回复 收起回复
使用道具
akbinlin  发表于 2018-10-19 09:15:35  
18#
厉害了大佬感谢分享
回复 收起回复
使用道具
3378579350  发表于 2018-10-19 22:37:21  
19#
好东西必须得收藏!
回复 收起回复
使用道具
浪神  发表于 2018-10-20 09:34:43  
20#
天天上元素 永远成大触
回复 收起回复
使用道具
12下一页

快来发表你宝贵的意见吧!

利维亚的杰洛特 实名

通过了实名认证的内容创造者

  

主题
146
精华
35
超神
0
扩散
0
微金
2920
智慧
123
余额
41
在线时间
4761 小时

吃鸡头盔 吉普车 吃鸡甲 长枪 火元素 长剑

快速回复 返回顶部 返回列表