上一篇文章(主城場景建築被雪覆蓋效果一)中我們實現了被雪覆蓋效果的shader,現在我們來看下怎麼來全局控制這個效果。

首先我們要控制這個下雪的效果其實就是要控制_SnowCount這個參數。要修改一個Material的float類型參數,大家常用的方法可能就是去get物體的Renderer組件,然後在通過renderer.sharedMaterial.SetFloat("_SnowCount" , count)這種方式去修改。如果我們的場景中有很多個Materials那就比較麻煩了,要先去遍歷拿到這些不同Materials的Renderer集合,再去用上面的方法。這裡我們用Shader.SetGlobalFloat("_SnowCount" , count)來實現會更方便,我們可以在C#代碼裡面整體控制所有設置了_SnowCount這個全局參數的shader的值,而不需要去遍歷得到那些集合。不過這裡要注意的是_SnowCount這個參數一定要是全局的,否則不會起作用。shader裡面設置全局參數的方法很簡單,一般我們設置shader的參數都是會現在Properties中自定義一些參數類型,然後在CGPROGRAM中在去定義一遍。如果我們要將一個參數設為全局的那隻要在Properties中把這個參數注釋掉,但是在CGPROGRAM中的定義還是保留著就行了。

Shader "Custom/Building/Building_Snow"
{
Properties
{
_Noise ("Noise", 2D) = "black" {}
//_SnowCount("SnowCount", float) = 1
_Color("MainColor", Color) = (1,1,1,1)
_MainTex ("MainTexture", 2D) = "white" {}
_SnowTex ("SnowTexture", 2D) = "white" {}
_SelfShadow("SelfShadow",Range(0, 1)) = 0.5
_SnowControl("SnowControl(x:Noise R y:Noise G z:Sonw)",vector) = (1,1,1,0.3)
//X:Noise貼圖紅色通道的Tiling, Y:Noise貼圖藍色通道的Tiling, Z:雪貼圖的Tiling, W:noise混合的邊緣過度
}
SubShader
{
......
Pass
{
CGPROGRAM
......
fixed _SelfShadow;
fixed4 _Color;
half _SnowCount;
sampler2D _Noise;
sampler2D _MainTex;
sampler2D _SnowTex;
half4 _SnowControl;
......
ENDCG
}
}
}

然後我們在C#的Update中通過下面的方法就可以看到全局控制的效果了,是不是要方便很多。

float count = Mathf.Abs(Mathf.Sin(Time.time * 0.15f)) * 30;
Shader.SetGlobalFloat("_SnowCount" , count);

這裡我們在擴展一下,在我們的場景中可能會有很多個shader都要用到這個下雪的效果,比如是建築,地面,植被等等。為了減少編寫代碼的時間,方便整體修改,我們可以把這些寫到一個cginc的庫文件中,就像我們寫shader都會去調用這個#include "UnityCG.cginc"一樣。

首先新建一個Include.cginc,代碼如下:

#ifndef CAG_CG_INCLUDE
#define CAG_CG_INCLUDE

struct appdata_snow
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct v2f_snow
{
float4 pos : SV_POSITION;
float4 uvColor : TEXCOORD0;
fixed4 snowControl : COLOR;
};

sampler2D _Noise;
half _SnowCount;
sampler2D _MainTex;
sampler2D _SnowTex;
half4 _SnowControl;

v2f_snow vert_snow(appdata_snow v)
{
v2f_snow o;
UNITY_SETUP_INSTANCE_ID(v);
o.pos = UnityObjectToClipPos(v.vertex);
o.uvColor.xy = v.texcoord0;
o.uvColor.zw = v.texcoord1;
o.snowControl = saturate(dot(fixed3(0, 1, 0), UnityObjectToWorldNormal(v.normal)));

return o;
}

fixed4 GetBlendTexColor(v2f_snow i) {

fixed n1 = tex2D(_Noise, i.uvColor.zw * _SnowControl.x).r;
fixed n2 = tex2D(_Noise, i.uvColor.zw * _SnowControl.y).g;
fixed noise = saturate((n1 * n2 * i.snowControl *_SnowCount - _SnowControl.w) / (1 - _SnowControl.w));
fixed4 col = tex2D(_MainTex, i.uvColor.xy) * (1-noise) + tex2D(_SnowTex, i.uvColor.zw * _SnowControl.z) * noise;
return col;
}

#endif

然後我們寫Shader的時候只要去調用這個cginc就行了,這樣代碼也很簡單清晰,便於維護修改。

Shader "Custom/Building/Building_Snow"
{
Properties
{
_Noise ("Noise", 2D) = "black" {}
//將_SnowCount設置為全局參數,需要在這裡被注釋掉
//_SnowCount("SnowCount", float) = 1
_Color("MainColor", Color) = (1,1,1,1)
_MainTex ("MainTexture", 2D) = "white" {}
_SnowTex ("SnowTexture", 2D) = "white" {}
_SelfShadow("SelfShadow",Range(0, 1)) = 0.5
_SnowControl("SnowControl(x:Noise R y:Noise G z:Sonw)",vector) = (1,1,1,0.3)
//X:Noise貼圖紅色通道的Tiling, Y:Noise貼圖藍色通道的Tiling, Z:雪貼圖的Tiling, W:noise混合的邊緣過度
}
SubShader
{
Tags
{
"Queue" = "Geometry"
"RenderType"="Opaque"
}

Cull Back
LOD 200

Pass
{
CGPROGRAM
//定義vertex 計算調用我們Include.cginc中的vert_snow方法,而不在這裡單獨再寫
#pragma vertex vert_snow
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile_instancing

#include "UnityCG.cginc"
//Include.cginc文件的路徑,「../」表示shader存放路徑的上一級目錄
#include "../CGInclude/CAGInclude.cginc"

fixed _SelfShadow;
fixed4 _Color;
//在Include.cginc中定義過的參數不需要這裡再次定義,否則會報錯
//half _SnowCount;
//sampler2D _Noise;
//sampler2D _MainTex;
//sampler2D _SnowTex;
//half4 _SnowControl;

fixed4 frag (v2f_snow i) : SV_Target
{
//調用Include.cginc中的GetBlendTexColor()方法得到混合後的顏色值
fixed4 col = _Color * GetBlendTexColor(i);
//加上採樣自身陰影貼圖的計算
col *= tex2D(_MainTex, i.uvColor.zw).a * _SelfShadow + (1 - _SelfShadow);
return col;
}
ENDCG
}
}
}

推薦閱讀:

相关文章