目录
Unity Shader 之 Stencil 模板测试相关讲解,及简单实例
一、简单介绍
二、Stencil 模板测试的模板格式
三、模板测试判断依据
四、模板缓冲值的更新
五、说明重点罗列
六、示例演示
Shader Language的发展方向是设计出在便携性方面可以和C++、Java等相比的高级语言,“赋予程序员灵活而方便的编程方式”,并“尽可能的控制渲染过程”同时“利用图形硬件的并行性,提高算法效率”。
Stencil (模板测试/蒙版测试):与深度测试,透明度测试类似,决定一个片元是否被扔掉。深度测试的比较数据在深度缓冲中,透明度测试的比较对象是颜色缓冲中的值,而模版测试的比较数据在Stencil中,并且模板测试要先于深度测试与透明度测试,在fragment函数之前就会执行模板测试。
1、格式
Stencil{
Ref referenceValue //Ref 就是参考值,当参数允许赋值时,会把参考值赋给当前像素
ReadMask readMask //ReadMask 对当前参考值和已有值进行mask操作,默认值255,一般不用
WriteMask writeMask //WriteMask 写入Mask操作,默认值255,一般不用
//Comp 比较方法。是拿Ref参考值和当前像素缓存上的值进行比较。默认值Always
Comp comparisonFunction
Pass stencilOperation //Pass 当模版测试和深度测试都通过时,进行处理
Fail stencilOperation //Fail 当模版测试和深度测试都失败时,进行处理
ZFail stencilOperation //ZFail 当模版测试通过而深度测试失败时,进行处理
}
2、说明
Ref
Ref referenceValue
Ref用来设定参考值referenceValue,这个值将用来与模板缓冲中的值进行比较。referenceValue是一个取值范围位0-255的整数。
ReadMask
ReadMask readMask
ReadMask 从字面意思的理解就是读遮罩,readMask将和referenceValue以及stencilBufferValue进行按位与(&)操作,readMask取值范围也是0-255的整数,默认值为255,二进制位11111111,即读取的时候不对referenceValue和stencilBufferValue产生效果,读取的还是原始值。
WriteMask
WriteMask writeMask
WriteMask是当写入模板缓冲时进行掩码操作(按位与【&】),writeMask取值范围是0-255的整数,默认值也是255,即当修改stencilBufferValue值时,写入的仍然是原始值。
Comp
Comp comparisonFunction
Comp是定义参考值(referenceValue)与缓冲值(stencilBufferValue)比较的操作函数,默认值:always
Pass
Pass stencilOperation
Pass是定义当模板测试(和深度测试)通过时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep
Fail
Fail stencilOperation
Fail是定义当模板测试(和深度测试)失败时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep
ZFail
ZFail是定义当模板测试通过而深度测试失败时,则根据(stencilOperation值)对模板缓冲值(stencilBufferValue)进行处理,默认值:keep
3、注意
Comp,Pass,Fail 和ZFail将会应用给背面消隐的几何体(只渲染前面的几何体),除非Cull Front被指定,在这种情况下就是正面消隐的几何体(只渲染背面的几何体)。你也可以精确的指定双面的模板状态通过定义CompFront,PassFront,FailFront,ZFailFront(当模型为front-facing geometry使用)和ComBack,PassBack,FailBack,ZFailBack(当模型为back-facing geometry使用)
和深度测试一样,在unity中,每个像素的模板测试也有它自己一套独立的依据,具体公式如下:
if(referenceValue&readMask comparisonFunction stencilBufferValue&readMask)
通过像素
else
抛弃像素
在这个公式中,主要分comparisonFunction的左边部分和右边部分
在上一步的模板测试之后,无论模板测试通过与否,都要对模板进行相应的更新。具体到怎么更新,则由程序员自己定义。上面关于模板缓冲语法中,Pass,Fail,ZFail等命令就是根据不同判断条件对模板缓冲区的值(stencilBufferValue)进行更新的操作,这些命令取值(stencilOperation)的类型及意义如下面列表所示:
在更新模板缓冲值的时候,也有writeMask进行掩码操作,用来对特定的位进行写入和屏蔽,默认值为255(11111111),即所有位数全部写入,不进行屏蔽操作。
例子小说明一下:
stencil{
Ref 33
Comp always
Pass replace
}
在上面的代码中:
1、本案例2个shader实现,并添加使用stencil缓冲用来限制渲染区域,只有在区域内能看到指定渲染;
2、其中,显示的 "Queue"="Geometry",区域限制的为 "Queue"="Geometry-1",即 显示模型 后于 限制区域渲染
3、代码中,ColorMask 0作用是屏蔽颜色的输出,即不输出颜色到屏幕。ZWrite Off用来关闭深度写入,防止深度测试中后面的角色的像素被剔除掉;在stencil中 Ref 1将referenceValue设置成1,Comp Always 保证模板测试始终通过,Pass Replace 操作则将stencilBufferValue刷新为1;即这段代码的功能是在屏幕上对应模型的位置不输入任何颜色,而将对应位置的模板缓冲值刷新为1;
ColorMask 0
ZWrite Off
Stencil
{
Ref 1
Comp Always
Pass Replace
}
4、代码中,Ref 1将referenceValue设置成1,在接下来的一行代码中,Comp Equal的意思是,如果referenceValue=stencilBufferValue,则模板测试通过,渲染像素,否则抛弃;在这个例子中,由于屏幕中的像素默认的模板值(stencilBufferValue)为0(我猜的,貌似是正确的哈)而参考值referenceValue为1,,所以正常情况下使用这个shader的模型是不显示的,但是在使用了第一个shader的cube区域,由于stencilBufferValue被刷新为1,所以在这个区域中,角色是能够显示的
Stencil {
Ref 1
Comp Equal
}
5、工程截图
6、测试效果
7、shader 代码
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unlit/StencilTest_Cube"
{
SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry-1"}
CGINCLUDE
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) : SV_Target {
return half4(1,1,0,1);
}
ENDCG
Pass {
ColorMask 0
ZWrite Off
Stencil
{
Ref 1
Comp Always
Pass Replace
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
}
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unlit/StencilTest_Show"
{
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "Queue" = "Geometry""RenderType"="Opaque" }
LOD 100
Pass {
Stencil {
Ref 1
Comp Equal
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
half2 texcoord : TEXCOORD0;
UNITY_FOG_COORDS(1)
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.texcoord);
return col;
}
ENDCG
}
}
}