1. 程式人生 > >【Unity Shader】簡單積雪效果的實現

【Unity Shader】簡單積雪效果的實現

1.前言

公司的專案進入真機除錯階段,體驗了一個月的996模式的正式結束,放假第一天來寫篇部落格

 2.實現思路

1.積雪的實現

一般由模型的紋理貼圖和一張積雪的紋理圖混合而成。

//2個取樣結果的差值(1.模型紋理,2.積雪紋理/顏色)
color.rgb = lerp(color, _SnowColor, SnowThreshold);

不過因為沒找到比較合適的積雪紋理圖,作為一名實習生也不好意思讓公司的美術大佬幫我量身定做,所以退而求其實選擇使用顏色-白色作為紋理的替代。

_SnowColor ("Snow Color", Color) = (1.0, 1.0, 1.0, 1.0)

積雪的程度由引數SnowLevel,SnowDepth來共同控制。

float _SnowLevel;
float _SnowDepth;

2.光照

還是使用Lambert漫反射光照模型,上次沒介紹,這裡簡單介紹一下公式吧

C_{LambertDiffuse} = (C_{Light} \cdot M_{Diffuse})Max(0,\widehat{n}\cdot \widehat{l})

其中:

 C_{Light} 光源顏色

M_{Diffuse} 材質漫反射顏色

Max(0,\widehat{n}\cdot \widehat{l})  法線與光源方向點積的正數值

 3.程式碼實現

以下是完整的shader程式碼

Shader "Unlit/SnowShader"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_SnowColor ("Snow Color", Color) = (1.0, 1.0, 1.0, 1.0)
		_SnowLevel ("Snow Level", Range(0, 1)) = 0
		_SnowDepth ("Snow Depth", Range(0, 0.5)) = 0.1
	}
	
	SubShader{

		Tags {"RenderType" = "Opaque"}

		CGINCLUDE
		#include "UnityCG.cginc"
		#include "Lighting.cginc"
		#include "AutoLight.cginc"

		sampler2D _MainTex;
		float4 _MainTex_ST;
		float4 _SnowColor;
		float _SnowLevel;
		float _SnowDepth;
		
		struct a2v{
			float4 vertex : POSITION;
			float3 normal : NORMAL;
			float2 texcoord : TEXCOORD0;	
		};

		struct v2f{
			float4 pos : SV_POSITION;
			float2 uv : TEXCOORD0;
			float3 worldNormal : TEXCOORD1;
			float3 worldPos : TEXCOORD2;
			SHADOW_COORDS(3)
		};

		v2f vert(a2v v) {
			v2f o;
			
			o.pos = UnityObjectToClipPos(v.vertex);
			o.worldNormal = UnityObjectToWorldNormal(v.normal);
			o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
			o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			
			TRANSFER_SHADOW(o);
			return o;
		}

		fixed4 frag(v2f i) : SV_Target{

			//使用Lambert光照模型

			//材質漫反射顏色
			fixed3 albedo = tex2D(_MainTex, i.uv).rgb;
			//入射光方向
			fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
			//世界法線
			fixed3 worldNormal = normalize(i.worldNormal);
			//環境光
			fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
			//漫反射光照
			fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, -worldLightDir));
			//統一光照衰減和陰影
			UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
			//混合光照
			fixed3 lightColor = diffuse * atten + ambient;
			//紋理取樣
			fixed4 color = tex2D(_MainTex, i.uv);

			//計算閾值,以世界法線和垂直方向夾角點積作為積雪厚度判斷標準,引數Snowlevel,SnowDepth一起控制積雪程度
			//一般來說,夾角越小(點積值越大)積雪越厚
			half SnowThreshold = dot(i.worldNormal, float3(0, 1, 0)) - lerp(1, -1, _SnowLevel);
			SnowThreshold = saturate(SnowThreshold / _SnowDepth);
			// SnowThreshold = saturate(_SnowDepth / SnowThreshold);
			color.rgb = lerp(color, _SnowColor, SnowThreshold);
			//混合顏色 輸出,這裡把插值函式第三個值置為1,可以得到一種類似卡通風格的渲染(也就是隻有color作為輸出)
			fixed3 finalColor = lerp(lightColor, color, SnowThreshold);

			return float4(finalColor, 1);

		}

		ENDCG


		Pass{

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			ENDCG

		}

	}

	FallBack "Diffuse"
}

4.實現效果

寫在最後