不用畫的動畫——ShaderCp11

——20.9.14app

Shader中主要有及兩種動畫,一種就是紋理動畫還有一種就是頂點動畫。函數

動畫效果通常都須要把時間加入一些變量的計算,以便畫面能夠隨時間發生變化。下面是Shader中的如何去訪問時間的方法。性能

1、紋理動畫動畫

序列幀動畫就是咱們接觸的第一種紋理動畫。序列幀動畫原理就是依次播放一系列關鍵幀動畫。當圖片的切換速度達到必定數值後,看上去就像是連續的動畫。優勢:在於它的靈活性強,不用進行物理計算就會有對應的動畫效果。缺點:依舊是須要逐關鍵幀的動畫內容,工做量依舊很大。spa

咱們看看shaderlab部分。咱們須要看有什麼樣的變量。通常來講序列幀通常是一張圖片包括了全部的關鍵幀。而且按播放順序排放。而且方便讀取通常是規整的(就是沒有邊框)。因此咱們要肯定有幾行_HorizontalAmount幾列_VerticalAmount根據圖片決定。還有就是播放速度_Speed,最後就是圖片_MainTex,和顏色_Color。orm

//frag
float time = floor(_Time.y * _Speed);
float row = floor(time / _HorizontalAmount);
float column = time - row * _VerticalAmount; half2 uv = float2(i.uv.x / _HorizontalAmount, i.uv.y / _VerticalAmount); uv.x += column / _HorizontalAmount; uv.y -= row / _VerticalAmount; //half2 uv = i.uv + half2(column, -row); //uv.x /= _HorizontalAmount; //uv.y /= _VerticalAmount;

  首先是_Time.y就是取時間變量t,而後經過取整來肯定行數,而後經過取小數來肯定列數。這裏是在片元着色器中的兩種讀取方式。一種是根據行和列把座標放大到整張圖片。而且經過增長基礎單位來進行能夠看到對uv.y進行的是減操做。是由於unity裏面是從左下爲(0,0)。而後第二種方法先去肯定要讀取的行列。而後再去細分到一個區間內。要注意上面這兩種方法本質上都是爲了取這張關鍵幀中的一張圖片。因而就要把uv座標縮放到其中的一張。blog

Shader "Unlit/11-1ImageSequenceAnimation"
{
    Properties
    {
		_Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Texture", 2D) = "white" {}
		_HorizontalAmount ("HorizontalAmount", Float) = 4
		_VerticalAmount ("VerticalAmount", Float) = 4
		_Speed ("Speed", Range(1, 100)) = 30 
    }
    SubShader
    {
        Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }

        Pass
        {
			Tags { "LightMode"="ForwardBase" }
			ZWrite off
			Blend SrcAlpha OneMinusSrcAlpha
            CGPROGRAM
			#pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag
           
            #include "UnityCG.cginc"

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

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

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

            v2f vert (appdata 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);
				float row = floor(time / _HorizontalAmount);
				float column = time - row * _VerticalAmount;

				half2 uv = float2(i.uv.x / _HorizontalAmount, i.uv.y / _VerticalAmount);
				uv.x += column / _HorizontalAmount;
				uv.y -= row / _VerticalAmount;

				//half2 uv = i.uv + half2(column, -row);
				//uv.x /= _HorizontalAmount;
				//uv.y /= _VerticalAmount;

				fixed4 c = tex2D(_MainTex, uv);
				c.rgb *= _Color;
				return c;

            }
            ENDCG
        }
    }FallBack "Transparent/VertexLit"
}

下面就是針對不一樣的行列。相同的速度數值也是大相徑庭的速度。  圖片

 

而後就是不一樣的背景進遠景對應的速度不一樣。咱們看一下首先須要兩張圖分別是近景和遠景_MainTex _DetailTex。而後就是分別他們的速度_ScrollX _Scroll2X。最後就是有關亮度_Multiplier。ip

T frac(T v)
//vert 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;
fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);

 

   frac函數是返回變量的小數部分。其中的變量能夠是float float2 float3 float4 都會分別對變量取小數。這樣能夠保證圖片能夠循環播放。由於上面每個頂點取偏移值值是同樣的。而後須要混合顏色採用的lerp而後第三個變量取的是secondlayer的a通道是由於這種圖是一張黑白圖取值分別就是1或者0。即可以肯定近景中哪一些是鏤空能夠放遠景的顏色。get

Shader "Unlit/11-2ScrollingBackground"
{
    Properties
    {
        _MainTex ("BaseLayer(RGB)", 2D) = "white" {}
        _DetailTex ("2ndLayer(RGB)", 2D) = "white" {}
		_ScrollX ("BaseLayerScrollSpeed", Float) = 1.0
		_Scroll2X ("2ndLayerScrollSpeed", Float) = 1.0
		_Multiplier ("LayerMultiplier", Float) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }

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

            #include "UnityCG.cginc"

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

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

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

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                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 firstLayer = tex2D(_MainTex, i.uv.xy);
               fixed4 secondLayer = tex2D(_DetailTex, i.uv.zw);
			   fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);
			   c.rgb *= _Multiplier;
			   return c;
            }
            ENDCG
        }
    }FallBack "VertexLit"
}

 2、頂點動畫

咱們先作一個2d的頂點動畫。就是河流。咱們看一下咱們須要河流的紋理_MainTex,_Color調整總體顏色,控制水流動的幅度_Magniture,水流的波動_Frequency,波長的倒數_InvWaveLength(即該值越大,波長越小),流動速度_Speed.

Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True" }
//vert
offset.yzw = float3(0,0,0);
offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;

  DisableBatching關閉該tags是爲了指明是否要對該SubShader進行批處理,由於這些須要特殊處理的Shader基本包括頂點動畫。會合並與之相關的模型致使相關的頂點出現問題,合併模型會致使各自模型空間丟失(留個坑?)。頂點動畫本質就是改變其中的頂點着色器中頂點的位置。這個的vertex是模型空間中的加上模型空間的位置份量。

Shader "Unlit/11-3Water"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_Color ("Color", Color) = (1,1,1,1)
		_Magnitude ("Distortion Magnitude", Float) = 1
		_Frequency ("Distortion Frequency", Float) = 1
		_InvWaveLength ("InvWaveLength", Float) = 10
		_Speed ("Speed", Float) = 0.5
    }
    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"

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

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

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

            v2f vert (appdata v)
            {
                v2f o;
				float4 offset;
				offset.yzw = float3(0,0,0);
				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 c = tex2D(_MainTex, i.uv);
				c.rgb *= _Color.rgb;
				return c;
            }
            ENDCG
        }
    }FallBack "VertexLit"
}

  

 

 

另外一種頂點動畫就是廣告牌技術。會根據視角方向來旋轉多邊形,看上去好像面向攝像頭,好比延誤雲朵閃光。其中的難處在於創建三個相互垂直的基向量。視角方向和向上的向量每每不垂直,因此要經過叉乘獲得一個與二者相垂直的變量,而後再取該向量與視角方向叉積,更新向上的變量。

 

 

 

float3 center = float3(0,0,0);
float3 viewer = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
float3 normalDir = viewer - center;
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));

  上面就是獲得三個基向量的過程。_VerticalBillboarding主要是這個向量經過調整數值來改變normal變量來模擬特殊需求。好比草地下面的根部是不懂的。還有一些廣告牌只會有旋轉操做可是不會脫離垂直方向。

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

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

Shader "Unlit/11-4Billboard"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_Color ("Color", Color) = (1,1,1,1)
		_VerticalBillboarding ("VerticalBillboarding", Range(0, 1)) = 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"
			#include "Lighting.cginc"

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

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
			fixed4 _Color;
			float _VerticalBillboarding;

            v2f vert (appdata v)
            {
                v2f o;
				float3 center = float3(0,0,0);
				float3 viewer = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
				float3 normalDir = viewer - center;
				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"
}

 

而後能夠看到咱們在作河流的時候河流的影子是不對的,咱們須要重寫ShaderCaster Pass。

struct v2f {
    V2F_SHADOW_CASTER;
;

v2f vert(appdata_base v){
	v2f o;
	float4 offset;
	offset.yzw = float3(0,0,0);
	offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
	v.vertex += offset;
	TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
	return o;
}

fixed4 frag(v2f i) : SV_Target{
	SHADOW_CASTER_FRAGMENT(i);
}

  這裏採用了UnityCG.cginc中定義的一些宏。來計算陰影所需的內容。V2F_SHADOW_CASTER用於定義一些變量。

Shader "Unlit/11-5VertexAnimWithShadow"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_Color ("Color", Color) = (1,1,1,1)
		_Magnitude ("Distortion Magnitude", Float) = 1
		_Frequency ("Distortion Frequency", Float) = 1
		_InvWaveLength ("InvWaveLength", Float) = 10
		_Speed ("Speed", Float) = 0.5
    }
    SubShader
    {
        Tags { "DisableBatching"="True" }

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

			Cull off
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag   

            #include "UnityCG.cginc"

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

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

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

            v2f vert (appdata v)
            {
                v2f o;
				float4 offset;
				offset.yzw = float3(0,0,0);
				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 c = tex2D(_MainTex, i.uv);
				c.rgb *= _Color.rgb;
				return c;
            }
            ENDCG
        }
		Pass{
			Tags { "LightMode"="ShadowCaster" }
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_shadowcaster

			#include "UnityCG.cginc"
			
			float _Magnitude;
			float _Frequency;
			float _InvWaveLength;
			float _Speed;

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

			struct v2f {
				V2F_SHADOW_CASTER;
			};

			v2f vert(appdata_base v){
				v2f o;
				float4 offset;
				offset.yzw = float3(0,0,0);
				offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
				v.vertex += offset;
				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
				return o;
			}

			fixed4 frag(v2f i) : SV_Target{
				SHADOW_CASTER_FRAGMENT(i);
			}

			ENDCG
		}
    }FallBack "VertexLit"
}

  

 

最後就是一些小事項。取消批處理能夠放置模型空間出現問題,可是會帶來性能的問題,就是DrawCall增長了。因此應該避免一些在模型空間的計算,用頂點顏色存頂點到錨點的距離,避免使用模型空間中性做爲錨點。

感謝你看到這裏,Cheers!

相關文章
相關標籤/搜索