Chapter11_紋理動畫

序列幀動畫

Shader "Chan/SqueueAction"{
	Properties
	{
		_Color("Main Color",Color) = (1,1,1,1)
		_MainTex("Main Tex",2D) = "white"{}
		_HorizontalAmount("Horizontal Amount",Float) = 8
		_VerticalAmount("Vertical Amount",Float) = 8
		_Speed("Action Speed",Range(0,40)) = 20 
	}

 SubShader
 {
     //動畫幀一般包含透明通道,因此設置爲渲染隊列設置爲 透明 = Transparent
    //透明度操作必有的三個標籤
	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;
				 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
			{
     			 float2 cellUV = float2(i.uv.x /_HorizontalAmount,i.uv.y/_VerticalAmount);
				 //位移
     			 float deltaX = 1 / _HorizontalAmount;
     			 float deltaY = 1 / _VerticalAmount;
  
     			 int index = _Time * _Speed;
     			 int col = fmod(index, _HorizontalAmount);
     			 int row = index / _HorizontalAmount;

     			 // 頂點(0,0)到(1,1)區域內經過映射過的UV座標 + 位移座標
     			 cellUV.x += col * deltaX;
     			 cellUV.y += row * deltaY;
 
				 fixed4 c = tex2D(_MainTex,cellUV);
				 c.rgb *= _Color;
				 return c;
			}
			ENDCG
			}
	  }
}

原理:隨着時間增加,每隔一段時間間隔,改變UV的座標,進而改變紋理的採樣,達成幀動畫效果。

具體操作是將獲取到的頂點UV(0,0)到(1,1)的區間的UV座標區域,根據時間變化映射到相應的紋理相應的UV區域,然後根據映射後的UV區域,採樣得到紋素,進而顯示屏幕像素。

half2 uv = float2(i.uv.x /_HorizontalAmount, i.uv.y / _VerticalAmount);

將原本(0,0)到(1,1)UV區域,根據幀動畫紋理的行列數劃分成  _HorizontalAmount * _VerticalAmount個(1/_HorizontalAmount,1/_VerticalAmount)大小的區域。第一個區域如下:

// 頂點(0,0)到(1,1)區域內經過映射過的UV座標 + 位移座標。這種寫法,紋理Wrap Mode != Repeat也沒問題。因爲UV的xy分量不會超過1.0;
                                   cellUV.x += col * deltaX;
                                   cellUV.y += row * deltaY; 

效果:

 

參考:http://www.cnblogs.com/Esfog/p/4088597.html  


UV動畫 

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

Shader  "Chan/Chapter11_ScrollBackGround"
{
	Properties
	{
		_MainTex("Main Tex",2D) = "white"{}
		_DetailTex("Detail Tex",2D) = "white"{}
		_ScrollX("Main Tex Scroll Speed",float) = 1.0
		_Scroll2X("Detail Tex Scroll",float) = 1.0
		_Multiplier("Layer Multiplier",float) = 1
	}
	SubShader
		{
			Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}

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

				CGPROGRAM

				#pragma vertex vert
				#pragma fragment frag
				#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);
					o.uv = v.texcoord;
					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 backGroundLayer = tex2D(_MainTex,i.uv.xy);
					fixed4 frontGroundLayer = tex2D(_DetailTex,i.uv.zw);

					fixed4 color = lerp(backGroundLayer,frontGroundLayer, frontGroundLayer.a);
					color.rgb *= _Multiplier;
					return color;
				}
				ENDCG
		     }
		}
	FallBack "VertexLit"
}

一前一後兩張紋理,在頂點着色器函數vert中,修改兩張紋理的UV。不在片元着色器frag,主要基於性能考慮。vert中足夠滿足需求。

frac函數 輸出其輸入的小數部分。它實質上移除整數部分,僅保留小數部分。如果輸入爲 4.32,則會輸出 0.32。此節點在與 Time(時間)節點一起使用時非常有用,會隨時間變化帶來鋸齒波。

效果如下:

https://blog.csdn.net/NF_XY/article/details/77000858 


 頂點動畫-簡單位移

Shader  "Chan/Chapter11_Water"
{
	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
		//UV滾動速度
		_Speed("Speed",Float) = 0.5 
	}
	SubShader
		{
			//透明效果Tags 三劍客 + 不批處理合並模型網格
			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;
				fixed4 _Color;
				float _Magnitude;
				float _Frequency;
				float _InvWaveLength;
				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;

					//頂點偏移量
					float4 offset = float4(0,0,0,0);
					offset.x = sin(_Frequency * _Time.y + 
						v.vertex.x * _InvWaveLength + 
						v.vertex.y * _InvWaveLength + 
						v.vertex.z * _InvWaveLength) * _Magnitude;
					offset += v.vertex;
					o.pos = UnityObjectToClipPos(offset); 

					o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.uv += float2(0.0,_Time.y * _Speed);
					return o;
				}

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

原理:使用一個正弦函數,隨着時間增減,對網格頂點進行進行偏移,再加UV滾動。即可實現。

Tags{"DisableBatching" = "True"} 使用此Shader的模型不進行批處理,批處理會合並使用相同材質的網格。多個小溪模型被合併網格,就沒法對每個網格進行頂點偏移了。效果如下:

頂點動畫-廣告牌(Billboarding)

Shader "Chan/Chapter 11/Billboard"
{
	Properties
	{
		_MainTex ("Main Tex", 2D) = "white" {}
	    _Color ("Color Tint", Color) = (1,1,1,1)
		_VerticalBillboarding ("Vertical Restraints", Range(0,1)) = 1
	}
	SubShader
	{
		Tags{ "Queue" = "Transparent" "IgnoreProject" = "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;
			fixed4 _Color;
			float _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));

				//開始計算3個正交矢量
				//根據觀察方向和錨點計算目標法線方向
				float3 normalDir = viewer - center;
				//根據_VerticalBillboarding屬性控制垂直方向上的約束
				//當_VerticalBillboarding爲1時,法線方向固定,爲視角方向;
				//當_VerticalBillboarding爲0時,向上方向固定,爲(0,1,0)
				//獲得的法線方向需要進行歸一化操作得到單位矢量
				normalDir.y = normalDir.y * _VerticalBillboarding;
				normalDir = normalize(normalDir);
				
				//獲得粗略的向上方向,爲了方式法線方向和向上方向平行(如果平行,叉積會得到錯誤的結果),對法線方向的y分量進行判斷
				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));

				//根據原始的位置相對於錨點的偏移量以及3個正交基矢量,以計算得到新的頂點位置
				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"
}

原理:

廣告牌技術的本質就是構建旋轉矩陣,而我們知道一個變換矩陣需要3個基向量。廣告牌技術使用的基向量通常就是表面法線(normal)、指向上的方向(up)以及指向右的方向(right)。除此之外,我們還需要指定一個錨點。這個錨點在旋轉的過程中是固定不變的,以此來確定多邊形在空間中的位置。

類似蹺蹺板兩頭,一邊是面片,一邊是攝像機,蹺蹺板不論怎麼旋轉,上下移動,攝像機看面片總是正對它。

https://www.twblogs.net/a/5b8b212c2b717718832d828d/zh-cn  推導慢慢看吧。。。

http://wiki.jikexueyuan.com/project/modern-opengl-tutorial/tutorial27.html

https://chengkehan.github.io/BillboardWithShader.html

 

PS: 

1.Unity自帶的Quad來作爲廣告牌,不能使用自帶的Plane。這是因爲,我們的代碼是建立在一個豎直襬放的多邊形的基礎上的,也就是說,這個多邊形的頂點結構需要滿足在模型空間下是豎直排列的。只有這樣,我們才能使用v.vertex來計算到正確的相對於中心的位置偏移量

2.爲了避免顯示使用模型空間的中心來作爲錨點,我們可以利用頂點顏色來存儲每個頂點到錨點的距離值,這種做法在商業遊戲中很常見。

 3.其次,如果我們想要對包含了頂點動畫的物體添加陰影,那麼如果像之前那樣使用內置的Diffuse等包含的陰影Pass來渲染,就得不到正確的陰影效果(這裏指的是無法向其他物體正確地投射陰影)。這是因爲,我們講過Unity 的陰影繪製需要調用一個ShadowCaster Pass,而如果直接使用Diffuse等內置的ShadowCasterPass,這個Pass中並沒有進行相關的頂點動畫。因此Unity仍然會按原來的頂點位置計算渲染陰影,

而如果涉及半透明物體我們都把Fallback設置成了Transparent/VertexLit ,而Transparent/VertexLit沒有定義ShadowCaster Pass,因此也就不會產生陰影。

因此需要自定義一個ShadowCaster Pass。

Shader "Chan/Chapter 11/Vertex Animation With Shadow" 
{
	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
			//UV滾動速度
			_Speed("Speed",Float) = 0.5 
		}
	SubShader
		{
			//透明效果Tags 三劍客 + 不批處理合並模型網格
			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;
				fixed4 _Color;
				float _Magnitude;
				float _Frequency;
				float _InvWaveLength;
				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;

					//定義一個正弦函數,做頂點偏移量
					float4 offset = float4(0,0,0,0);
					offset.x = sin(_Frequency * _Time.y + 
						v.vertex.x * _InvWaveLength + 
						v.vertex.y * _InvWaveLength + 
						v.vertex.z * _InvWaveLength) * _Magnitude;
					offset += v.vertex;
					o.pos = UnityObjectToClipPos(offset); 

					o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
					o.uv += float2(0.0,_Time.y * _Speed);
					return o;
				}

				fixed4 frag(v2f i) :SV_Target
				{
					fixed4 color = tex2D(_MainTex,i.uv);
					color *= _Color;
					return color;
				}
				ENDCG
		    }

		    //沒有ShadowCaster Pass,自定義一個
			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 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;

					//使用SHADOW_CASTER_FRAGMENT來讓Unity自動完成陰影投射的部分,把結果輸出到深度圖和陰影映射紋理中  
					TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
					
					return o;
				}
				
				fixed4 frag(v2f i) : SV_Target {
				    SHADOW_CASTER_FRAGMENT(i)
				}
				ENDCG
			}
		}
		
	FallBack "VertexLit"
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章