shader-动画

动画效果往往都是把时间添加到变量中,跟随时间变化画面也随之变化,

纹理动画和图像滚动

序列帧动画:依次播放一些列关键帧图像,当速度达到一定数值时,看起来就是一个连续的动画,灵活性强。精髓在于我们在每个时刻计算该时刻正在播放关键帧的位置,对该关键帧进行纹理采样。序列帧图片在设置时 选择开启透明Alpha通道。通过_Time索引序列帧图像中的子图像。

图像滚动:通过_Time控制偏移量实现滚动

Shader "Unlit/1"
{
    Properties
    {
         _Color("_Color",Color)=(1,1,1,1)
        _MainTex ("Texture", 2D) = "white" {}
        _HorizontalAmount("_HorizontalAmount",Float)=4
        _VerticalAmount("_VerticalAmount",Float)=4
        _Speed("_Speed",Range(0,256))=1
    }
    SubShader
    {   //序列帧动画通常为透明纹理
        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            //序列帧图像通常包含透明通道,因此可以被当成是一个半透明对象。
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"

            fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			//序列帧的行数和列数
			float _HorizontalAmount;
			float _VerticalAmount;
			float _Speed;
			  
			struct a2v {  
			    float4 vertex : POSITION; 
			    float2 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
            {
                float time=floor(_Time.y * _Speed);
                 //floor向下取整 时间除以_HorizontalAmount结果作为行索引,
                float row=floor(time / _HorizontalAmount);
                float column=floor(time / _VerticalAmount);
                //对竖直方向使用减法,unity中的纹理坐标是竖直方向的顺序(从下往上逐渐增大)和序列帧纹理中的顺序(播放顺序从上到下)是相反的
                half2 uv=i.uv + half2(column,-row);
                //将源uv按行列等分,得到每个子图像纹理的坐标范围。
                uv.x /= _HorizontalAmount;
                uv.y /= _VerticalAmount;
                //对计算的结果进行采样
                fixed4 c = tex2D(_MainTex, uv);
				c.rgb *= _Color;
                
                return c;
            }
            ENDCG
        }
    }
    FallBack "Transparent/VertexLit"
}
Shader "Unlit/2"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _DetailTex ("2nd Layer (RGB)", 2D) = "white" {}
		_ScrollX ("Base layer Scroll Speed", Float) = 1.0
		_Scroll2X ("2nd layer Scroll Speed", Float) = 1.0
		_Multiplier ("Layer Multiplier", Float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        

        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"

            sampler2D _MainTex;
			sampler2D _DetailTex;
			float4 _MainTex_ST;
			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);
                //两张纹理存储到一个插值寄存器中,
                //_Time作为uv的偏移量
                o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex) + frac(float2(_ScrollX, 0.0) * _Time.y);
				o.uv.zw = TRANSFORM_TEX(v.texcoord, _DetailTex) + frac(float2(_Scroll2X, 0.0) * _Time.y);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 main= tex2D(_MainTex,i.uv.xy);
                fixed4 detail= tex2D(_DetailTex,i.uv.zw);
                //使用detail的透明通道混合两张纹理
                fixed4 c=lerp(main,detail,detail.a);
                c.rgb*=_Multiplier;
                return c;
            }
            ENDCG
        }
    }
    FallBack "VertexLit"
}

顶点动画

     顶点动画让模型动画起来。在模型空间计算

Shader "Unlit/3"
{
    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) = 10
 		_Speed ("Speed", Float) = 0.5
    }
    SubShader
    {
        //"DisableBatching"="True"
        //是否取消批处理 在使用批处理时会出现问题。批处理会合并所有相关的模型,而这些模型各自的模型空间就会丢失。
        Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True"}
        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            //关闭剔除,在每个面都能渲染。
            Cull Off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"

            sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Color;
			//控制振幅
			float _Magnitude;
			//控制频率
			float _Frequency;
			//控制波长
			float _InvWaveLength;
			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;
                float4 offset;
                offset.yzw=float3(0,0,0);
                //对x方向进行位移
                offset.x=sin(_Frequency*_Time.y+v.vertex.x*_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 col = tex2D(_MainTex, i.uv);
                col *= _Color;
                return col;
            }
            ENDCG
        }
    }
}

顶点动画阴影:

   要想阴影随着顶点变化而变化,需要是自定义实现LightMode=ShadowCaster pass

Shader "Unlit/3"
{
    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) = 10
 		_Speed ("Speed", Float) = 0.5
    }
    SubShader
    {
        //"DisableBatching"="True"
        //是否取消批处理 在使用批处理时会出现问题。批处理会合并所有相关的模型,而这些模型各自的模型空间就会丢失。
        Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True"}
        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            ZWrite Off
            Blend SrcAlpha OneMinusSrcAlpha
            //关闭剔除,在每个面都能渲染。
            Cull Off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

            #include "UnityCG.cginc"

            sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Color;
			//控制振幅
			float _Magnitude;
			//控制频率
			float _Frequency;
			//控制波长
			float _InvWaveLength;
			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;
                float4 offset;
                offset.yzw=float3(0,0,0);
                //对x方向进行位移
                offset.x=sin(_Frequency*_Time.y+v.vertex.x*_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 col = tex2D(_MainTex, i.uv);
                col *= _Color;
                return col;
            }
            ENDCG
        }
        
		Pass {
			Tags { "LightMode" = "ShadowCaster" }
			
			CGPROGRAM
			
			#pragma vertex vert
			#pragma fragment frag
			//shadowCaster 指令保证获取阴影相关变量正确赋值
			#pragma multi_compile_shadowcaster
			
			#include "UnityCG.cginc"
			
			float _Magnitude;
			float _Frequency;
			float _InvWaveLength;
			float _Speed;
			
			struct v2f { 
			//定义阴影投射需要的定义的变量
			    V2F_SHADOW_CASTER;
			};
			
			v2f vert(appdata_base v) {
				v2f o;
				
				float4 offset;
				offset.yzw = float3(0.0, 0.0, 0.0);
				offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
				v.vertex = v.vertex + offset;
				//计算坐标的转换。
				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
			//自动完成阴影投射的计算,结果输出到深度图和映射纹理中
			    SHADOW_CASTER_FRAGMENT(i)
			}
			ENDCG
		}
    }
    FallBack "VertexLit"
}

广告牌

    广告牌技术会根据视角方向来旋转一个被纹理着色器的多边形,使得多边形看起来总是面对摄像机。渲染烟雾、云朵、闪光等。广告牌技术本质是构建旋转矩阵,表面法线、指向上的方向、指向右的方向,以及一个锚点,锚点在旋转过程中是固定不变的。通过叉积得到三个相互正交方向。

Shader "Unlit/4"
{
    Properties
    {
       _MainTex ("Main Tex", 2D) = "white" {}
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1 
    }
    SubShader
    {
        //顶点动画化是关闭批处理 "DisableBatching"="True"
        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 "Lighting.cginc"
           
           sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;
            //调整方向
            fixed _VerticalBillboarding;
           
            struct a2v {
				float4 vertex : POSITION;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

            v2f vert (a2v v)
            {
                v2f o;
                //原点作为锚点
                float3 center = float3(0, 0, 0);
                //得到模型空间下的视角
				float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1));
				//通过视角和锚点得到法线
				float3 normalDir = viewer - center;
				//_VerticalBillboarding控制 y 方向的约束度  为1 法向方向固定为视角方向 为0 上方向固定为向(0,1,0)
				normalDir.y =normalDir.y * _VerticalBillboarding;
				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 - center;
				float3 localPos = center + 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"
}

注意,在顶点动画时,批处理会破环这种动画效果,需要使用DisableBatching=True 强制取消对shader的批处理,相应的会带来性能下降的问题以及增加Draw call的调用。应该尽量避免使用模型空间下的绝对位置和方向来计算。

发布了24 篇原创文章 · 获赞 1 · 访问量 1242

猜你喜欢

转载自blog.csdn.net/birdswillbecomdragon/article/details/105322034