模版缓冲区(stencil buffer),是在OpenGL三维绘图等计算机图像硬件中常见的除颜色缓冲区(color buffer或frame buffer)、深度缓冲区(depth buffer 或 z buffer )、累积缓冲区(accumulation buffer)之外另一种数据缓冲。
模版缓冲区通常是每像素 8 位。该值可以写入、递增或递减。后续绘制调用可以根据该值进行测试,以确定在运行像素着色器之前是否应丢弃像素。用于遮罩ugui中的遮罩就是通过模板缓冲区实现的。
模板测试在渲染管线中的位置:裁减测试---->透明度测试---->模板测试---->深度测试
比较方法
//
// 摘要:
// Depth or stencil comparison function.
public enum CompareFunction
{
//
// 摘要:
// Depth or stencil test is disabled.
Disabled = 0,
//
// 摘要:
// Never pass depth or stencil test.
Never = 1,
//
// 摘要:
// Pass depth or stencil test when new value is less than old one.
Less = 2,
//
// 摘要:
// Pass depth or stencil test when values are equal.
Equal = 3,
//
// 摘要:
// Pass depth or stencil test when new value is less or equal than old one.
LessEqual = 4,
//
// 摘要:
// Pass depth or stencil test when new value is greater than old one.
Greater = 5,
//
// 摘要:
// Pass depth or stencil test when values are different.
NotEqual = 6,
//
// 摘要:
// Pass depth or stencil test when new value is greater or equal than old one.
GreaterEqual = 7,
//
// 摘要:
// Always pass depth or stencil test.
Always = 8
}
模版像素操作
//
// 摘要:
// Specifies the operation that's performed on the stencil buffer when rendering.
public enum StencilOp
{
//
// 摘要:
// Keeps the current stencil value.
Keep = 0,
//
// 摘要:
// Sets the stencil buffer value to zero.
Zero = 1,
//
// 摘要:
// Replace the stencil buffer value with reference value (specified in the shader).
Replace = 2,
//
// 摘要:
// Increments the current stencil buffer value. Clamps to the maximum representable
// unsigned value.
IncrementSaturate = 3,
//
// 摘要:
// Decrements the current stencil buffer value. Clamps to 0.
DecrementSaturate = 4,
//
// 摘要:
// Bitwise inverts the current stencil buffer value.
Invert = 5,
//
// 摘要:
// Increments the current stencil buffer value. Wraps stencil buffer value to zero
// when incrementing the maximum representable unsigned value.
IncrementWrap = 6,
//
// 摘要:
// Decrements the current stencil buffer value. Wraps stencil buffer value to the
// maximum representable unsigned value when decrementing a stencil buffer value
// of zero.
DecrementWrap = 7
}
下面做一个Demo
1.以unlit shader为模板创建两个shader:SimpleMask.shader和SimpleObject.shader,创建两个材质SimpleMask.mat和SimpleObject.mat,并将shader拖到对应的材质上。SimpleMask.mat给一张红色贴图,SimpleObject.mat给一张蓝色贴图以区别显示
2.场景上创建两个cube:SimpleMask和SimpleObject,并引用对应的材质,调整位置(两个cube会交叉)
3.我们修改 SimpleMask.shader和SimpleObject.shader以使SimpleMask Cube遮罩SimpleObject Cube
SimpleMask.shader中加入以下代码:
Stencil
{
Ref 1//参考值为1
Comp Always//参考值与缓冲区的当前内容进行比较的函数 Always为始终比较
Pass replace//如果模板测试(和深度测试)通过,如何处理缓冲区的内容 替换模板缓冲区中的数据为1
}
SimpleObject.shader中加入以下代码:
Stencil
{
Ref 1//参考值为1
Comp equal//和模板缓冲区中的数据是否相等
Pass keep// 测试通过后不改变模板缓冲区中的内容,我不是模板,只负责被遮罩
}
我们去场景中调整SimpleObject Cube发现效果如下(被红色遮罩的区域才会显示)
由于红色物体是遮罩物体,我们把它(红色物体)范围调大localscale改为2,并隐藏掉:
SimpleMask.shader中加入以下代码:
ColorMask 0
ZWrite off
ZTest off
再去场景中调整SimpleObject Cube发现效果如下(遮罩效果完成)
SimpleMask.shader
Shader "Unlit/SimpleMask"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
ColorMask 0
ZWrite off
ZTest off
Stencil
{
Ref 1//参考值为1
Comp Always//参考值与缓冲区的当前内容进行比较的函数 Always为始终比较
Pass replace//如果模板测试(和深度测试)通过,如何处理缓冲区的内容 替换模板缓冲区中的数据为1
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
SimpleObject.shader
Shader "Unlit/SimpleObject"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Stencil
{
Ref 1//参考值为1
Comp equal//和模板缓冲区中的数据是否相等
Pass keep// 测试通过后不改变模板缓冲区中的内容,我不是模板,只负责被遮罩
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}