本文章用于幫助自己學習,因此只記錄一些個人認為比較重要或者還不夠熟悉的內容,
原作者:http://blog.csdn.net/candycat1992/article/
第十一章 讓畫面動起來
11.1 Unity Shader中的內置變數(時間篇)
影片效果往往都是把時間添加到一些變數的計算中,以便在時間變化時畫面也可以隨之變化,
Unity Shader提供了一系列關于時間的內置變數來允許我們方便地在Shader中訪問運行時間,實作各種影片效果,下表給出了這些內置的時間變數,

11.2 紋理影片
在各種資源都比較局限的移動平臺上,我們往往會使用紋理影片來代替復雜的粒子系統等模擬各種影片效果,
11.2.1序列幀影片
最常見的紋理影片之一就是序列幀影片,序列幀影片的原理非常簡單,就是依次播放一系列關鍵幀影像,當播放速度達到一定數值時,看起來就是一個連續的影片,它的優點在于靈活性很強,我們不需要進行任何物理計算就可以得到非常細膩的影片效果,
要想實作序列幀影片,我們先要提供一張包含了關鍵幀影像的影像,如圖所示,

代碼:
Shader "Unity Shaders Book/Chapter 11/ImageSequenceAnimation"
{
Properties
{
_Color("Color Tint",Color) = (1,1,1,1)
//包含所有關鍵幀影像的紋理
_MainTex ("Image Sequence", 2D) = "white" {}
//水平方向包含的關鍵幀影像個數
_HorizontalAmount("Horizontal Amount",Float) = 4
//豎直方向包含的關鍵幀影像個數
_VerticalAmount("Vertical Amount",Float) = 4
//控制序列幀影片播放速度
_Speed("Speed",Range(1,100)) = 30
}
SubShader
{
//由于序列幀影像通常是透明紋理,
//我們需要設定Pass的相關狀態以渲染透明效果
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"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float _HorizontalAmount;
float _VerticalAmount;
float _Speed;
struct a2v
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
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
{
//_Time.y是自該場景加載后經過的時間,floor函式取整
float time = floor(_Time.y * _Speed);
//求出當前對應的行索引
float row = floor(time / _HorizontalAmount);
//余數就是列索引
float column = time - row * _HorizontalAmount;
//unity紋理坐標從下到上遞增,因此row符號為-,
half2 uv = i.uv + half2(column, -row);
uv.x /= _HorizontalAmount;
uv.y /= _HorizontalAmount;
fixed4 c = tex2D(_MainTex, uv);
c.rgb *= _Color;
return c;
}
ENDCG
}
}
Fallback "Transparent/VertexLit"
}
效果如下:

11.2.2滾動的背景
很多2D游戲都使用了不斷滾動的背景來模擬游戲角色在場景中的穿梭,這些背景往往包含 了多個層(layers)來模擬一種視差效果,而這些背景的實作往往就是利用了紋理影片,在本節中, 我們將實作一個包含了兩層的無限滾動的2D游戲背景,本節使用的紋理資源均來自OpenGameArt (http://opengameart.org)網站,
由于本例模擬的是2D游戲中的滾動背景,因此我們需要把攝像 機的投影模式設定為正交投影,
代碼:
Shader "Unity Shaders Book/Chapter 11/Scrolling Background"
{
Properties
{
//MainTex和DetailTex分別是第一層(較遠)和第二層(較近)的背景紋理
_MainTex ("Base Layer (RGB)", 2D) = "white" {}
_DetailTex ("2nd Layer (RGB)", 2D) = "white" {}
//ScrollX 和_Scroll2X對應了各自的水平滾動速度
_ScrollX ("Base layer Scroll Speed", Float) = 1.0
_Scroll2X ("2nd layer Scroll Speed", Float) = 1.0
//Multiplier引數用于控制紋理的整體亮度
_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.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);
//使用第二層紋理的透明通道呼叫lerp函式插值混合紋理
fixed4 c = lerp(firstLayer, secondLayer, secondLayer.a);
c.rgb *= _Multiplier;
return c;
}
ENDCG
}
}
FallBack "VertexLit"
}
效果如下:

11.3 頂點影片
11.3.1 流動的河流
河流的模擬是頂點影片最常見的應用之一,它的原理通常就是使用正弦函式等來模擬水流的波動效果,
代碼:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 11/Water"
{
Properties
{
_MainTex ("Texture", 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
{
//需要關閉批處理,
//因為批處理會合并所有相關的模型,
//模型各自的模型空間會丟失,
//本例中需要在物體模型空間下對頂點位置進行偏移
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;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v)
{
v2f o;
float4 offset;
//只對頂點x方向進行位移,
//因此yzw的位移量置為0
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;
o.pos = UnityObjectToClipPos(v.vertex + 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 c = tex2D(_MainTex,i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
效果如下:

11.3.2 廣告牌
廣告牌技識訓根據視角方向來旋轉,一個被紋理著色的多邊形(通常就是簡單的四邊形,這個多邊形就是廣告牌),使得多邊形看起來好像總是面對著攝像機,廣告牌技術被用于很多應用,比如渲染煙霧、云朵、閃光效果等,
廣告牌技術的本質就是構建旋轉矩陣,而我們知道一個變換矩陣需要3個基向量,廣告牌技術使用的基向量通常就是表面法線(normal)、指向上的方向(up)以及指向右的方向(right),除此之外,我們還需要指定一個錨點(anchorlocation),這個錨點在旋轉程序中是固定不變的, 以此來確定多邊形在空間中的位置,
廣告牌技術的難點在于,如何根據需求來構建3個相互正交的基向量,計算程序通常是,我們首先會通過初始計算得到目標的表面法線(例如就是視角方向)和指向上的方向,而兩者往往是不垂直的,
但是,兩者其中之一是固定的,例如當模擬草叢時,我們希望廣告牌的指向上的方向永遠是(0,1,0),而法線方向應該隨視角變化;而當模擬粒子效果時,我們希望廣告牌的法線方向是固定的,即總是指向視角方向,指向上的方向則可以發生變化,
我們假設法線方向是固定的,首先,我們根據初始的表面法線和指向上的方向來計算出目標方向的指向右的方向(通過叉積操作):
right = up × normal
對其歸一化后,再由法線方向和指向右的方向計算出正交的指向上的方向即可:
up’ = normal × right
至此,我們就可以得到用于旋轉的3個正交基了,下圖給出了上述計算程序的圖示,如果指向上的方向是固定的,計算程序也是類似的,

代碼:
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unlit/Chapter11-Billboard"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Color Tint",Color) = (1,1,1,1)
//調整是固定法線還是固定指向上的方向
_VerticalBillboarding("Vertical Restraints", Range(0, 1)) = 1
}
SubShader
{
Tags { "Queue" = "Transparent" "Ignoreprojector" = "True" "RenderType" = "Transparent"
"Disable Batching" = "True"}
Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.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));
float3 normalDir = viewer - center;
//_VerticalBillboarding為1時,法線方向固定為視角方向
//為0時,向上方向固定為(0,1,0)
//因為法線y方向分量為0時,與之垂直的向上方向必為(0,1,0)
normalDir.y =normalDir.y * _VerticalBillboarding;
normalDir = normalize(normalDir);
//得到粗略的向上方向(abs函式回傳絕對值)
//由法線方向和粗略的向上方向得到向右方向
//,并歸一化(因為是粗略的,所以向右方向結果不是歸一化的)得到準確向右方向
//,再由法線方向和向右方向得到準確的向上方向
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"
}
效果如下:
Vertical Restraints為1:

Vertical Restraints為0:

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/273301.html
標籤:其他
