——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!