前言

借由 Unity Shader 实战学习 Shader !

带完整注释,一起学习 💪


效果

从圆心慢慢荡漾开的水波,有高低起伏感

👉展示备份(国内节点)
👇效果展示 (科学上网)


代码

1
2
3
4
附件:
1. 高面数平面

链接:https://pan.baidu.com/s/1EObyLpQZDsQUXAxXS_QVtg 提取码:ij83
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
Shader "Unlit/AloeaShader04"
{
    Properties
    {
        _WaveAmp ("Wave Amplitude", Range(0, 0.1)) = 0.05
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            float _WaveAmp;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            // 顶点着色器和片段着色器需要使用同样的波动计算,以保证高度上的变化和颜色始终一致
            float getWave ( float2 uv ) {
                float2 uvsCenter = uv * 2 - 1; // 计算中心点, uv 的范围是 0 ~ 1
                float distance = length( uvsCenter ); // 此顶点距离中心点的距离
                // PlanA:主计算处,根据距离形成波动,叠加上动画效果
                float wave = cos((distance - _Time.y * 0.1 ) * 30);
                // PlanB: 数值约束到 0 ~ 1 抖动更小
                // float wave = cos((distance - _Time.y * 0.1) * 30) * 0.5 + 0.5;
                wave *= 1 - distance; // 渐变,距离中心越远数值越小(越接近黑色)
                return wave;
            }

            v2f vert (appdata v)
            {
                v2f o;

                v.vertex.y = getWave(v.uv) * _WaveAmp; // 乘以波动的系数

                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return getWave(i.uv);
            }
            ENDCG
        }
    }
}

细节

效果调试过程

步骤一:找圆心点

1
2
3
4
5
6
fixed4 frag (v2f i) : SV_Target
{
// i.uv.xy 的范围是 0 ~ 1,期望找到 (0.5, 0.5) 的位置为 0,结果范围 -1 ~ 1
// Q.为什么不是 i.uv - 0.5 ?也是可以的,这样结果范围是 -0.5 ~ 0.5,得到效果的波纹间距更大
return float4(i.uv * 2 - 1, 0, 0);
}

步骤二:绘制波动的圆心环

1
2
3
4
5
6
fixed4 frag (v2f i) : SV_Target
{
float2 uvsCenter = i.uv * 2 - 1; // 中心点
float distance = length( uvsCenter ); // 此顶点距离中心点的距离
float wave = cos((distance - _Time.y * 0.1 ) * 30) * 0.5 + 0.5; // cos 形成波动,结果范围 0 ~ 1,0 ~ 1 ~ 0 变化的过程就会形成视觉上的涟漪
return wave;

步骤三:顶点坐标位移赋予一样的波动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// wave 方法封装,顶点着色器和片段着色器都用到
float getWave ( float2 uv ) {
float2 uvsCenter = uv * 2 - 1; // 中心点
float distance = length( uvsCenter ); // 此顶点距离中心点的距离
float wave = cos((distance - _Time.y * 0.1 ) * 30) * 0.5 + 0.5; // cos 形成波动,结果范围 0 ~ 1,0 ~ 1 ~ 0 变化的过程就会形成视觉上的涟漪
return wave;
}

// 顶点着色器
v2f vert (appdata v)
{
v2f o;
v.vertex.y = getWave(v.uv) * 0.1; // y 轴方向顶点位移波动

o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}


步骤三:效果参数调优

1
2
3
4
5
6
7
8
9
float getWave ( float2 uv ) {
float2 uvsCenter = uv * 2 - 1;
float distance = length( uvsCenter );
// PlanA:主计算处,根据距离形成波动,叠加上动画效果
float wave = cos((distance - _Time.y * 0.1 ) * 30); // 数值范围改为 -1 ~ 1, 这样黑色的部分更多些(0以下都是黑色)
wave *= 1 - distance; // 渐变,距离中心越远数值越小(越接近黑色,高度越低)
return wave;

}

模型顶点数

在今天这个练习中,波动是在顶点上进行的,那么平面的顶点数越多,整个波动就越丝滑,顶点数越少,效果就很硬朗。


函数 length

length:相当于求向量 v 的长度。

二维向量

三维向量


文档:https://docs.unity3d.com/Packages/com.unity.shadergraph@6.9/manual/Length-Node.html



总结

  1. 关注变量的维度,比如 uv 是二维的(xy),color 是四维的( rgba )。
  2. 在这个案例中很直观地理解顶点着色器和片段着色器,一个用于处理顶点信息,也就是「形」,另一个用于处理表现信息,也就是「色」。
  3. 提取可复用的方法来增强代码的可维护性。
  4. 构建效果的过程就是关注数值变化的过程。