這個還是挺久之前做的,都快忘了。上篇總結後處理的時候在亂七八糟的工程裏突然找到,就也來寫一篇。
效果還是挺有意思的,我覺得放在人物選擇界面這種會很棒。
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"
}