1. 程式人生 > 實用技巧 >Shader筆記——6.Shader動畫

Shader筆記——6.Shader動畫

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"
}

背景迴圈動畫

// 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"
}

REF

文件:

https://docs.unity3d.com/Manual/class-RenderTexture.html

書籍:

OpenGL寶典、Unity Shader入門