案例-顶点动画-模拟水面

2018-05-15  本文已影响0人  JervieQin

准备

  1. 创建shader文件、将它拖到新建的材质球上。不赘述。
  2. 在场景中创建一个plane用于演示,不赘述。

编写VF shader

  1. 首先我们定义一些要用到的属性:
    //外部属性 ,用于初始传值
    Properties {
        _Color ("Color", Color) = (1,1,1,1)       //整体颜色
        _MainTex ("Main Tex", 2D) = "white" {}    //河流纹理 
        _Magnitude ("Wave Magnitude",Float) = 1   //波动幅度
        _Frequency("Wave Frequency",Float) = 1    //波动频率
        _InvWaveLength("Wave Length",Float) = 10  //波动长度倒数
        _Speed("Wave Speed",Float) = 0.5          //波动速度
    }
  1. 然后着手写VF着色器,首先定义一些标签。依次声明渲染物体类型是透明的,渲染队列为透明,忽略投影,关闭批处理。
    SubShader {
        //顶点动画需要关闭批处理
        //标签中的两个Transparent意义不同:RenderType中标记物体的渲染类型,而Queue中表示渲染透明几何体阶段时调用此shader(控制渲染时机)
        //详情:https://blog.csdn.net/treepulse/article/details/53484505 ; https://docs.unity3d.com/Manual/SL-SubShaderTags.html
        //另外注意,Tags分为subshader tags 和 pass tags ,前者适用于所有pass,后者只针对一个pass
        Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True" }
  1. 重要部分编写
        Pass{
            // pass tags 详情:https://docs.unity3d.com/Manual/SL-PassTags.html
            Tags{ "LightMode" = "ForwardBase"}//设置光照类型
            ZWrite off                        //关闭深度写入,避免层次覆盖
            Blend SrcAlpha OneMinusSrcAlpha   //开启颜色混合模式
            Cull off                          //让水流的每一个面都显示

        CGPROGRAM 
        #pragma vertex vert                  //vextex着色器阶段
        #pragma fragment frag                //fragment着色器阶段
        #include "UnityCG.cginc" 

            //对属性的声明,便于后续使用
            
            //采样器,用于才片元片元着色器输出阶段对纹理的采样
            //通常和tex2D配合使用
            //采样器详情:https://docs.unity3d.com/Manual/SL-SamplerStates.html
            sampler2D _MainTexer;   

            //属性对应的值变量
            float4 _MainTex_ST;         
            fixed4 _Color;
            float _Magnitude;
            float _Frequency;
            float _InvWaveLength;
            float _Speed;

        //定义输入顶点着色器阶段的数据结构  
        struct Input{
            float4 vertex : POSITION;       //顶点位置
            float4 texcoord : TEXCOORD0;    //纹理坐标
        };
        
        //定义顶点着色器阶段输出的数据结构
        struct v2f{

            //SV_前缀的变量代表system value,
            //DX10之后就推荐使用SV_POSITION作为vertex shader的输出和fragment shader的输入了,
            //注意vertex shader的输入还是使用POSITION!
            float4 pos:SV_POSITION;          
            float2 uv:TEXCOORD0;             //纹理坐标
        };

        //输出v2f到下一渲染阶段
        v2f vert(Input v){
            v2f o ;

            float4 offset;

            //因为只希望顶点在X方向移动,所以yzw方向设置为0
            offset.yzw = float3(0,0,0);

            /*
            偏移公式: 正弦化(频率 * 时间 + (顶点x坐标 + 顶点y坐标 + 顶点z坐标)  * 波长倒数)*振幅
                      为了让不同的位置有不同的有不同的位移,加上了模型空间下的位置分量
            float4 _Time : 表示自场景加载经过的时间 ,4个分量分别是 (t/20, t, t*2, t*3)
            另外:  float4 _SinTime/_CosTime :4个分量分别是 (t/8, t/4, t/2, t)
                    float4 unity_DeltaTime: 4个分量的值分别是(dt, 1/dt, smoothDt, 1/smoothDt)
            */
            offset.x = sin(_Frequency*_Time.y
                                              +v.vertex.x*_InvWaveLength
                                              +v.vertex.y *_InvWaveLength
                                              +v.vertex.z*_InvWaveLength)*_Magnitude;

            //o.pos = mul(UNITY_MATRIX_MVP,v.vertex + offset); 将顶点坐标转换成齐次裁剪空间,
            //mul函数相当于UnityObjectToClipPos函数 ,但是要通过两次矩阵运算,
            // 如果只是为了将顶点空间转换成齐次裁剪空间,推荐使用UnityObjectToClipPos函数。
            o.pos = UnityObjectToClipPos(v.vertex + offset);

            //TRANSFORM_TEX:#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            //定义中的 name##_ST,表示要先声明一个与第二个参数同名且以_ST结尾的“变量”。比如此处的 _MainTex_ST。
            //作用:将模型顶点的uv和Tiling、Offset两个变量进行运算(xy控制缩放,zw控制偏移),计算出实际显示用的顶点uv。
            o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
            o.uv +=float2(0,_Time.y * _Speed);

            return o;
        }

        //SV_TARGET是系统值,表示该函数返回的是用于下一个阶段OutPut Merger的颜色值
        //tex2D(s, t)    s:采样器(之前定义的sampler2D)   , t:uv坐标    输出纹理信息
        fixed4 frag(v2f i):SV_TARGET{
            //纹理采样
            fixed4 c = tex2D(_MainTexer,i.uv);  
            //设置输出颜色
            c.rgb *= _Color.rgb;

            return c;
        }
        ENDCG
        }
            }
    FallBack "Transparent/VertexLit"
}

给材质球贴上水纹理
完成。

上一篇下一篇

猜你喜欢

热点阅读