在渲染管线中, 模板测试发生在片元着色器处理和透明度测试之后, 深度测试之前.
模板测试最常见的应用就是各种遮罩, 特别是有形状的遮罩, 如Unity中的Mask
组件, 这些遮罩的特点就是可以按照某种形状(可以是非矩形的)来渲染片元.
比如我们在游戏中经常看到的各种形状的头像 ,而Rect Mask 2D
组件使用的是裁剪测试(Scissor Test), 可以指定绘制一块矩形区域内的片元. 模板测试的圆形遮罩如图所示.
和深度测试类似, 模板测试也有一个对应的缓存, 即模板缓存(Stencil Buffer), 用于记录所有像素的模板值, 默认值为0.
在渲染某个物体时, 我们可以指定一个参考值(Referencce value, unity中叫Stencil ID), 当渲染这个物体的某个片元时, 将片元携带的参考值与模板缓存中的值作比较, 根据不同的比较方式, 满足要求则通过测试, 然后根据设定好的操作对模板缓存中的值进行更新, 当然, 也可以指定没有通过测试时的操作.
由上面的过程, 我们要注意几个关键的点:
模板测试是一个不可编程, 但是可以配置的管线阶段. 我们通过指令对模板测试进行配置, 在Unity中, 其基本语法为:
Stencil
{
Ref refValue
Comp always
Pass keep
Fail keep
ZFail keep
WriteMask 255
ReadMask 255
}
我们通过Ref refValue
来指定参考值, 参考值是一个整型值, 正负都可以.
然后通过Comp xxx
来指定比较函数, 接收的参数是比较函数的枚举值, 对应C#中的枚举UnityEngine.Rendering.CompareFunction
, 从0到8, 分别代表(括号中的是其对应的枚举):
0(Disabled)
: 关闭模板测试, 等同于全部通过测试, 经过测试发现不是真的关闭.1(Never):
全部不能通过测试2(Less)
: 待比较的值小于缓存中的值时通过测试3(Equal)
: 待比较的值等于缓存中的值时通过测试4(LessEqual)
: 待比较的值小于等于缓存中的值时通过测试5(Greater)
: 待比较的值大于缓存中的值时通过测试6(NotEqual)
: 待比较的值不等于缓存中的值时通过测试7(GreaterEqual)
: 待比较的值大于等于缓存中的值时通过测试8(Always)
: 全部通过测试, 默认值再然后通过通过或者不通过测试的各种情况, 指定对模板值的更新操作, 分别是:
Pass operation
: 模板测试和深度测试都通过后的操作Fail operation
: 模板测试和深度测试都未通过后的操作ZFail operation
: 模板测试通过而深度测试未通过后的操作它们的参数都是同一个类型, 即操作函数的枚举值, 对应C#中的枚举UnityEngine.Rendering.StencilOp
, 从0到7, 分别代表:
0(Keep)
: 保持模板缓存中的值不变1(Zero)
: 将模板缓存中的值置为02(Replace)
: 使用参考值替换模板缓存中的值3(IncrementSaturate)
: 使模板缓冲区值增大, 最大限制为可表示的最大无符号值4(DecrementSaturate)
: 使模板缓冲区值减小, 最小限制为05(Invert)
: 对模板缓冲区值按位求反6(IncrementWrap)
: 与IncrementSaturate类似, 只是达到最大后继续增大将重新设置为 07(DecrementWrap)
: 与DecrementSaturate类似, 只是达到最小后继续减小将重新设置为可表示的最大无符号值最后的ReadMask
和WriteMask
两个掩码是对模板值和参考值的额外处理, 值为0-255之间的整数值, ReadMask
代表在读取模板值后, 将其与掩码按位与之后再与参考值作比较, 而WriteMask
代表在使用参考值更新模板值之前, 在模板值与掩码按位与之后再更新, 一般两个掩码都设置为255, 代表不做任何额外处理.
在Unity中可以通过属性面板的方式来设置, 但是经过测试, 使用变量的方式来设置模板参数无法关闭模板测试:
_Stencil ("Stencil ID", Float) = 0
[Enum(UnityEngine.Rendering.CompareFunction)] _StencilComp ("Stencil Comparison", Int) = 8
[Enum(UnityEngine.Rendering.StencilOp)] _StencilOp ("Stencil Operation", Int) = 0
_StencilWriteMask ("Stencil Write Mask", Range(0, 255)) = 255
_StencilReadMask ("Stencil Read Mask", Range(0, 255)) = 255
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
效果如图:
以上就是模板测试的基础内容. 下一篇文章我们会使用模板测试来模拟Unity中Mask
组件, 并且使用另一种方式来解决锯齿问题, 希望对大家有所帮助.