之前的預(yù)備知識能讓我們掌握shader的基本用法,并且寫出一些簡單的shader。這次我們通過積雪效果來更進一步的了解shader是如何結(jié)合數(shù)學(xué),實現(xiàn)效果。
另外注意,關(guān)注有強烈光照需求的shader才使用surface shader,否則使用VF shader就可。
Shader "Custom/SnowEffect" { //定義shader的路徑
Properties{
_MainTex("Albedo (RGB)", 2D) = "white" {} //要使用的紋理
//定義了默認值為“bump”的2D類型的紋理_Bump,初始值為空。
_Bump("Bump",2D) = "bump"{}
_Snow("Snow Level",Range(0,1)) = 0
_SnowColor("Snow Color",Color) = (1,1,1,1)
_SnowDirection("Snow Direction",Vector) = (0,1,0) //默認水平方向,值為1,而不是-1
_SnowDepth("Snow Depth",Range(0,0.3)) = 0.1 //積雪厚度
}
SubShader{
//在腳本中可以通過判斷rendertype來做一些處理,比如shader替換、shader關(guān)閉
//參考: https://blog.csdn.net/nnsword/article/details/17840439 類型
// https://blog.csdn.net/zmafly/article/details/51141011 作用
// http://lib.csdn.net/article/unity3d/44793 解析
Tags { "RenderType" = "Opaque" }
CGPROGRAM //shader的正文
//聲明surface shader 函數(shù)名,光照類型,使用的頂點函數(shù)(自定頂點函數(shù)名為vert)
#pragma surface surf Lambert vertex:vert
//添加對PROPERTIES的引用
sampler2D _MainTex;
sampler2D _Bump;
float _Snow;
float4 _SnowColor;
float4 _SnowDirection;
float _SnowDepth;
struct Input {
float2 uv_MainTex;
//添加一個變量float2 uv_Bump來獲得_Bump紋理的uv坐標(biāo)。
float2 uv_Bump;
//獲取世界坐標(biāo)下的法向值
float3 worldNormal;
INTERNAL_DATA
};
void surf(Input IN, inout SurfaceOutput o) {
//tex2D對紋理進行采樣
half4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
//tex2D根據(jù)輸入?yún)?shù)的類型,獲取對應(yīng)含義的值,此處從_Bump紋理中提取法向信息
//UnpackNormal函數(shù)的作用就是將tex2D函數(shù)獲取到的fixed4類型值轉(zhuǎn)換成fixed3類型的值
o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump));
//得到世界坐標(biāo)系下的真正法向量(而非凹凸貼圖產(chǎn)生的法向量,要做一個凹凸貼圖法向值到世界坐標(biāo)系法向值的轉(zhuǎn)化)和雪落
//下相反方向的點乘結(jié)果,即兩者余弦值,并和_Snow(積雪程度)比較
if (dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) > lerp(1, -1, _Snow))
{
/*
我們知道,余弦值是-1和1之間的一個數(shù),越接近于1,說明該點法向與雪落下相反方向越一致,當(dāng)為-1時,說明兩者方向相反。
此處我們可以看出_Snow參數(shù)只是一個插值項,當(dāng)上述夾角余弦值大于
lerp(1,-1,_Snow)=1-2*_Snow時,即表示此處積雪覆蓋,所以此值越大
多講一點,lerp的第三個參數(shù)是一個(0,1)之間的比例因子,代表所處前兩個參數(shù)范圍的位置
注意為了符合正常的自然現(xiàn)象,我們_Snow一般只取0~0.5,因為大于0.5時,插值的結(jié)果將小于0,會造成雪好像穿過了巖石,落到了巖石的后面。這個道理和光照的道理一樣,物體背面是見不到陽光的。
*/
//積雪程度程度越大。此時給覆蓋積雪的區(qū)域填充雪的顏色
o.Albedo = _SnowColor.rgb;
}
else //否則表示未覆蓋
{
o.Albedo = c.rgb;
}
o.Alpha = 1;
}
//自定義頂點函數(shù)
/*
參數(shù)appdata_full v,參數(shù)的類型為appdata_full(Unity內(nèi)置類型),該類型包含了紋理坐標(biāo),法向量,頂點位置,以及切線信息
*/
void vert(inout appdata_full v)
{
//將_SnowDirection從世界坐標(biāo)系轉(zhuǎn)換到模型的局部坐標(biāo)系下
/*
將_SnowDirection乘以Unity內(nèi)置矩陣 – UNITY_MATRIX_IT_MV
(IT表示Inverse Transpose逆轉(zhuǎn)置矩陣,MV表示 ModelView矩陣,該矩陣表示是ModelView的逆轉(zhuǎn)置矩陣)。
現(xiàn)在我們得到了該頂點的法向量。
_Snow*2/3,這表示只有那些更接近雪落下方向的區(qū)域才會增加雪的厚度,更符合自然現(xiàn)象。
而這些通過測試的區(qū)域,沿著(sn.xyz+v.normal)方向進行加厚,也就是將其頂點沿此方向伸展一定距離。
注意到增厚的程度取決于_SnowDepth和_Snow,而增厚的方向是由物體法向和雪落的方向綜合作用的,這也符合自然現(xiàn)象。
*/
float4 sn = mul(UNITY_MATRIX_IT_MV, _SnowDirection);
if (dot(v.normal, sn.xyz) >= lerp(1, -1, (_Snow * 2) / 3))
v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
}
ENDCG
}
FallBack "Diffuse" //shader失敗時回滾
}
調(diào)節(jié)箭頭所指落雪方向可以改變落雪效果