除了透明测试和混合透明外,Unity 还提供了了另一种实现透明效果的方法:模板测试 Stencil Test , 通过模板测试可以达到逐像素保留或者丢弃像素的目的.

# 所以

就是给物体一个模板值 m, 物体本身有个参照值 n, 可以对模板值进行操作,根据比较方法 (大于,小于。等于等等) 决定该像素是否丢弃

# 语法

1
2
3
4
5
6
7
8
9
10
Stencil{
Ref referenceValue
ReadMask readMask
WriteMask writeMask
Comp comparisonFunction
Pass stencilOperation
Fail stencilOperation
ZFail stencilOperation
}

  • Ref referenceValue : 用来与缓存中已经存在的模板值进行比较的数值,被称为参照值,当比较符合某些设定条件,这个证书可以被写进缓存,数值的范围是【0~255】
  • ReadMask readMask : 是一个范围 0~255 的整数,8 位二进制 11111111, 当读取参照值与模板支可以使用的时候,模板会指定哪些位的数值可以读取,默认为 255,也就是所有位都可以读取。
  • WriteMask writeMask : 同样也是 8 位二进制,当往缓存中写入的时候可以使用,模板会指定那些值允许写入,当 WriteMask 为 0 时,表示的是没有数值会被写入缓存,而不是将 0 写入。默认位数为 255,也就是所有位都可以写入
  • Comp comparisionFunction : 参照值与模板值比较的方法,默认为 always
  • Pass stencilOperation : 如果模板测试和深度测试都通过,缓存中的模板值如何处理,默认为 keep
  • Fail stencilOperation : 如果没有通过,缓存中的模板值如何处理,默认为 keep
  • ZFail stencilOperation : 如果模板测试通过,但是深度测试没通过,缓存中的数值如何处理,默认为 keep

# 比较方法

  • Greater
  • GEqual
  • Less
  • LEqual
  • Equal
  • NotEqual
  • Always
  • Never

P128

# 效果

# 代码

墙的 Shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Shader "Custom/StencilTestB"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_MainColor ("Main Color",Color) =(1,1,1,1)
}
SubShader
{
Tags { "Queue" = "Geometry" }

Pass
{
Tags { "LightMode" = "ForwardBase" }

//设置模板测试状态
Stencil{
Ref 1
Comp NotEqual
Pass Keep
}

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"

struct v2f {
float4 pos : SV_POSITION;
float4 worldPos : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float2 texcoord : TEXCOORD2;
};

sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _MainColor;

v2f vert(appdata_base v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);

float3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = normalize(worldNormal);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

return o;
}

fixed4 frag(v2f i) : SV_Target{
float3 worldLight = UnityWorldSpaceLightDir(i.worldPos.xyz);
worldLight = normalize(worldLight);

fixed NdotL = saturate(dot(i.worldNormal, worldLight));

fixed4 color = tex2D(_MainTex, i.texcoord);
color.rgb *= _MainColor * NdotL * _LightColor0.rgb;
color.rgb += unity_AmbientSky.rgb;

return color;
}
ENDCG
}
}
}

遮挡剔除的 Shader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Shader "Custom/StencilTestA"
{
SubShader
{
//墙上打洞思路: 球体先渲染,
//Geometry-1 => 最先渲染
Tags { "Queue" = "Geometry-1"}
Pass{
//设置模板测试的状态
//模板值为1
//始终比较
//把参考值写入缓存之中
Stencil{
Ref 1
Comp Always
Pass Replace
}

//禁止绘制任何色彩
ColorMask 0
ZWrite Off

CGPROGRAM
#pragma vertex vert
#pragma fragment frag

float4 vert(in float4 vertex:POSITION) : SV_POSITION
{
float4 pos = UnityObjectToClipPos(vertex);
return pos;
}

void frag(out fixed4 color : SV_Target) {
color = fixed4(0,0,0,0);
}
ENDCG
}
}
}