模板测试(Stencil Test)的基础知识

本文分享模板测试(Stencil Test)的基础知识

在渲染管线中, 模板测试发生在片元着色器处理和透明度测试之后, 深度测试之前.

模板测试最常见的应用就是各种遮罩, 特别是有形状的遮罩, 如Unity中的Mask组件, 这些遮罩的特点就是可以按照某种形状(可以是非矩形的)来渲染片元.

比如我们在游戏中经常看到的各种形状的头像 ,而Rect Mask 2D组件使用的是裁剪测试(Scissor Test), 可以指定绘制一块矩形区域内的片元. 模板测试的圆形遮罩如图所示.

模板测试(Stencil Test)的基础知识_第1张图片

模板测试基础

和深度测试类似, 模板测试也有一个对应的缓存, 即模板缓存(Stencil Buffer), 用于记录所有像素的模板值, 默认值为0.

在渲染某个物体时, 我们可以指定一个参考值(Referencce value, unity中叫Stencil ID), 当渲染这个物体的某个片元时, 将片元携带的参考值与模板缓存中的值作比较, 根据不同的比较方式, 满足要求则通过测试, 然后根据设定好的操作对模板缓存中的值进行更新, 当然, 也可以指定没有通过测试时的操作.

由上面的过程, 我们要注意几个关键的点:

  • 模板值: 模板缓存中已经存在的值
  • 参考值: 在渲染该物体前, 由程序设置的指定值
  • 比较函数: 决定如何将两个值作比较的函数
  • 操作函数: 定义通过或者不通过测试后对模板值的更新操作

模板测试是一个不可编程, 但是可以配置的管线阶段. 我们通过指令对模板测试进行配置, 在Unity中, 其基本语法为:

Stencil
{
    Ref refValue
    Comp always
    Pass keep
    Fail keep
    ZFail keep
    WriteMask 255
    ReadMask 255
}

Unity中模板测试语法的说明

我们通过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): 将模板缓存中的值置为0
  • 2(Replace): 使用参考值替换模板缓存中的值
  • 3(IncrementSaturate): 使模板缓冲区值增大, 最大限制为可表示的最大无符号值
  • 4(DecrementSaturate): 使模板缓冲区值减小, 最小限制为0
  • 5(Invert): 对模板缓冲区值按位求反
  • 6(IncrementWrap): 与IncrementSaturate类似, 只是达到最大后继续增大将重新设置为 0
  • 7(DecrementWrap): 与DecrementSaturate类似, 只是达到最小后继续减小将重新设置为可表示的最大无符号值

最后的ReadMaskWriteMask两个掩码是对模板值和参考值的额外处理, 值为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]
}

效果如图:

模板测试(Stencil Test)的基础知识_第2张图片

以上就是模板测试的基础内容. 下一篇文章我们会使用模板测试来模拟Unity中Mask组件, 并且使用另一种方式来解决锯齿问题, 希望对大家有所帮助.

你可能感兴趣的:(Unity,图形学基础,unity,游戏引擎)