【Shader】實驗09——模板測試實現神奇的轉轉樂

這個還是挺久之前做的,都快忘了。上篇總結後處理的時候在亂七八糟的工程裏突然找到,就也來寫一篇。

效果還是挺有意思的,我覺得放在人物選擇界面這種會很棒。

神奇的轉轉樂
unity的渲染管線的逐片元操作階段有三項測試,通過三項測試的片元(fragment)才能成爲像素(pixel)。
雖然這個階段是不可編程的,但是卻是高度可配置的。

三項測試按順序分別是:

透明度測試(Alpha Test)

低於給定透明度的片元會被捨棄,默認是關閉的。

模板測試(Stencil Test)。

如果開啓該測試,會在模板測試階段讀取片元的模板值,並與給定的參考值進行比較,再決定是否捨棄該片元。

一般用來限制渲染區域,可用來渲染輪廓、陰影等用法。

默認是關閉的。

深度測試(Depth Test)

讀取片元的深度值,並與深度緩衝區已存在的深度值進行比較。

如果片元深度值大於緩衝器深度值,則說明該片元在場景中已有物體之後,則捨棄該片元;反之則說明該片元在場景已有物體之前,則保留該片元,並把緩衝區深度值更新爲該片元深度值。

默認開啓,可配置深度測試和深度寫入的開啓與否來渲染半透明物體(要很小心渲染順序)。

源碼

模板測試可以做出很多很有意思的東西,比如上面那個轉轉樂。具體的模板測試各種操作配置可以見這篇博文

轉轉樂的具體思路是在方框的四個側面分別放四個plane當做mask,在外部寫入模板值,並設置只有相同模板值才能渲染。

雖然寫了陰影的模板測試,但是不知道爲啥會出現比較詭異的陰影情況,模板配置改了很多遍也沒法正常,只能關掉陰影投射和接收,希望以後哪一天我搞定了再回來改掉。

mask和幾何體的shader源碼如下:

StencilMask源碼

Shader "Unlit/StencilMask"
{
	Properties
	{ 
		_StencilMask("Stencil Mask", Int) = 0 
	} 
	SubShader
	{ 
		Tags{ "RenderType" = "Opaque" "Queue" = "Geometry" } 

		ColorMask 0 //不輸出任何顏色
		ZWrite off //關閉深度寫入

		Stencil
		{ 
			Ref[_StencilMask] 
			Comp Always
			Pass Replace 
		} 

		Pass
		{ 
			CGPROGRAM 
			#pragma vertex vert 
			#pragma fragment frag 

			struct appdata 
			{ 
				float4 vertex : POSITION; 
			}; 
			struct v2f 
			{ 
				float4 pos : SV_POSITION; 
			};

			v2f vert(appdata v) 
			{ 
				v2f o; 
				o.pos = UnityObjectToClipPos(v.vertex); 
				return o; 
			} 

			half4 frag(v2f i) : COLOR
			{ 
				return half4(1,1,1,1); 
			} 
			ENDCG 
		} 
	} 
}

StencilGeometry源碼

Shader "Unlit/StencilGeometry"
{
	Properties
	{
		_StencilMask("Stencil Mask", Int) = 0
		_Color("Color", Color) = (1,1,1,1)
	}

	SubShader
	{
		Tags{ "RenderType" = "Opaque" "Queue" = "Geometry+1" }

		Pass
		{
			Stencil
			{
				Ref[_StencilMask]
				Comp equal  //只有與模板緩衝區裏的值相等才通過模板測試
				Pass Keep
			}

			Tags {"LightMode" = "ForwardBase"}

			CGPROGRAM
			#pragma vertex vert 
			#pragma fragment frag 


			#pragma multi_compile_fwdbase
			#include "UnityCG.cginc"
			#include "AutoLight.cginc"
			#include "Lighting.cginc"

			fixed4 _Color;

			struct v2f
			{
				float4 pos : SV_POSITION;
				float4 worldPos : TEXCOORD0;
				float3 worldNormal : TEXCOORD1;
				SHADOW_COORDS(2)
			};
			
			v2f vert(appdata_base v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				TRANSFER_SHADOW(o);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Color.rgb;
				fixed3 diffuse = _LightColor0.rgb * _Color.rgb * saturate(dot(worldNormal, worldLightDir));
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

				return fixed4(diffuse * atten + ambient, 1.0);
			}

			ENDCG
		}

		Pass
		{
			Stencil
			{
				Ref 0
				Comp equal
				Pass Keep
			}

			Tags {"LightMode" = "ShadowCaster"}
			
			CGPROGRAM 
			#pragma vertex vert 
			#pragma fragment frag 
			#pragma multi_compile_shadowcaster
			#include "UnityCG.cginc"

			struct v2f
			{
				V2F_SHADOW_CASTER;
			};

			v2f vert(appdata_base v)
			{
				v2f o;
				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				SHADOW_CASTER_FRAGMENT(i);
			}

			ENDCG 
		}		
	} 
	FallBack "Diffuse" 
} 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章