Shader动画

在Unity Shader中引入时间变量_Time,_SinTime,_CosTime,unity_DeltaTime等内置变量来获取在Shader中获取运行时间,就可以来实现各种动画效果

纹理动画

我们可以使用纹理动画来代替复杂的粒子系统等来模拟各种动画效果。

序列帧动画Image Sequence Animation

最常见的纹理动画之一就是序列帧动画

Shader "Custom/SequenceAnime" {
    Properties{
    _Color ("Color Tint" , Color) = (1,1,1,1)
    _MainTex ("Sequence Image" , 2D) = "white" {}
    _HorizontalAmount ("Horizontal Amount" , Float) = 4
    _VerticalAmount ("Vertical Amount" ,Float) = 4
    _Speed ("Anime Speed" ,Range(1,100)) = 30
    }
    SubShader{
        Tags{
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
        }
        Pass{
            Tags{"LightMode" = "ForwardBase"}
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            float4 _Color;
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _HorizontalAmount;
            float _VerticalAmount;
            float _Speed;

            struct a2v {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
            };

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

            v2f vert (a2v v){
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
                return o;
            }

            fixed4 frag (v2f i) : SV_TARGET{
                //使用_Time.y乘以速度来模拟时间,再用floor()函数来取得整数时间
                float time = floor(_Time.y * _Speed);
                //时间除以_HorizontalAmount,对结果向下取整为当前时间所处行数
                float row = floor(time / _HorizontalAmount);
                //时间除以_HorizontalAmount的余数,为当前时间所处列数
                float column = fmod(time, _HorizontalAmount);

                //纹理坐标的x、y分别除以_HorizontalAmount和_VerticalAmount,得到子关键帧的纹理坐标范围(此时进行采样输出的就是左下角关键帧)
                half2 uv = float2(i.uv.x / _HorizontalAmount , i.uv.y / _VerticalAmount);

                //使用上面计算出的当前列数对上面得到的子关键帧的纹理坐标范围进行偏移,得到当前子关键帧的纹理坐标
                uv.x += column / _HorizontalAmount;
                uv.y -= row / _VerticalAmount;
                //进行采样
                fixed4 c = tex2D (_MainTex ,uv);
                c.rgb *= _Color;
                return c;
            }
            ENDCG
        }
    }
    Fallback "Transparent/VertexLit"
}

示例序列帧:

序列帧动画效果:

在导入此类用途的图片资源时,无论是用于纹理贴图还是UI,需要在导入设置中设置其Wrap Mode为Repeat,因为Wrap Mode的设置决定了当纹理坐标超过了[0,1]时纹理如何被平铺

背景循环动画

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/InfiniteScroll" {
    Properties{
        _MainTex ("Main Tex", 2D) = "white"{}
        _DetailTex ("Detail Tex" ,2D) = "white"{}
        _ScrollX ("Mian Tex Speed" , Float) = 1.0
        _Scroll2X ("Detail Tex Speed" , Float ) = 1.0
        _Multiplier ("Layer Multiplier" , Float) = 1.0
    }
    SubShader{
        Tags{"Queue" = "Geometry" "RenderType" = "Opaque"}
        Pass{
            Tags{"LightMode" = "ForwardBase"}

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _DetailTex;
            float4 _DetailTex_ST;
            float _ScrollX;
            float _Scroll2X;
            float _Multiplier;

            struct a2v{
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
            };

            struct v2f{
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
            };

            v2f vert (a2v v){
                v2f o;
                o.pos = UnityObjectToClipPos( v.vertex);
                //通过TRANSFORM_TEX得到转换后的纹理坐标,对其x方向上再进行偏移,且偏移量随时间变量_Time.y而变化
                o.uv.xy = TRANSFORM_TEX(v.texcoord , _MainTex) + float2(frac(_ScrollX * _Time.y),0);
                o.uv.zw = TRANSFORM_TEX(v.texcoord,_DetailTex) + float2(frac(_Scroll2X * _Time.y) ,0);
                return o;
            }

            fixed4 frag (v2f i) : SV_TARGET{
                fixed4 mainLayer = tex2D(_MainTex , i.uv.xy);
                fixed4 detailLayer = tex2D(_DetailTex , i.uv.zw);

                fixed4 c = lerp(mainLayer,detailLayer,detailLayer.a);
                c.rgb *= _Multiplier;
                return c;

            }
            ENDCG
        }
    }
    Fallback "VertexLit"
}

顶点动画

河流流动动画

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Custom/WaterWave"{
    Properties{
        _MainTex ("Main Tex" ,2D) = "white"{}
        _Color ("Color Tint" , Color) = (1,1,1,1)
        _Magnitude ("Distortion Magnitude" , Float) = 1
        _Frequency ("Distortion Frequency" ,Float) = 1
        _InvWaveLength ("Distortion Inverse Wave Length" ,Float) = 1
        _Speed ("Speed" ,Float ) = 1
    }
    SubShader{
        Tags{
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
            "DisableBatching" = "True"
        }
        Pass{
            Tags{"LightMode" = "ForwardBase"}
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            float _Magnitude;
            float _Frequency;
            float _InvWaveLength;
            float _Speed;

            struct a2v{
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD;
            };

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

            v2f vert(a2v v){
                v2f o;

                float4 offset;
                offset.yzw = float3(0,0,0);
                offset.x = sin(_Frequency * _Time.y + v.vertex * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength ) * _Magnitude;

                o.pos = UnityObjectToClipPos(v.vertex + offset);
                o.uv = TRANSFORM_TEX(v.texcoord , _MainTex);
                o.uv += float2(0,_Time.y * _Speed);
                return o;
            }

            fixed4 frag(v2f i) :SV_TARGET{
                fixed4 c = tex2D(_MainTex ,i.uv);
                c.rgb *= _Color.rgb;
                return c;
            }
            ENDCG
        }
    }
    Fallback "Transparent/VertexLit"
}

广告牌BillBoard

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'


Shader "Custom/BillBoard"{
    Properties{
        _MainTex ("Main Tex" ,2D) = "white"{}
        _Color ("Color Tint", Color) = (1,1,1,1)
        _VerticalRestraints ("Vertical Restraints", Float) = 1
    }
    SubShader{
        Tags{
            "Queue" = "Transparent"
            "IgnoreProjector" = "True"
            "RenderType" = "Transparent"
            "DisableBatching" = "True"
        }

        Pass{
            Tags{"LightMode" = "ForwardBase"}

            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float4 _Color;
            float _VerticalRestraints;

            struct a2v{
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
            };
            struct v2f{
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            v2f vert (a2v v){
                v2f o;
                //模型空间中心
                float3 centerPos = float3(0,0,0);
                //摄像机在模型空间中的位置
                float3 viewerPos = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos,1));
                //要使广告牌一直面对摄像机,即广告牌的法线方向一直为摄像机的方向
                float3 normalDir = viewerPos - centerPos;

                normalDir.y = normalDir.y * _VerticalRestraints;
                normalDir = normalize(normalDir);

                float3 upDir = abs(normalDir.y) > 0.999 ? float3(0,0,1) : float3(0,1,0);
                float3 rightDir = normalize(cross(upDir,normalDir));
                upDir = normalize(cross(normalDir,rightDir));
                //各顶点相对于模型空间原点坐标的偏移
                float3 centerOffs = v.vertex.xyz - centerPos;
                //由模型空间坐标原点、新的坐标轴矢量以及各轴偏移量得到此时顶点的位置坐标
                float3 localPos = centerPos + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z ;

                o.pos = UnityObjectToClipPos(float4(localPos,1));
                o.uv = TRANSFORM_TEX(v.texcoord , _MainTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_TARGET{
                fixed4 c = tex2D(_MainTex,i.uv);
                c.rgb *= _Color.rgb;
                return c;
            }
            ENDCG
        }
    }
    FallBack "Transparent/VertexLit"
}