序列幀動畫
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"
}