UnityShader29:模板测试

一、什么是模板测试

OpenGL基础30:模板测试

Unity官方文档:Unity - Manual: ShaderLab command: Stencil

一个很经典的模板测试例子就是 UI Mask:

二、Unity 中的模板测试

SubShader
{
    Tags { …… }
    Pass
    {
        Stencil
        {
            //可选
            Ref referenceValue
	        ReadMask readMask
	        WriteMask writeMask
	        Comp comparisonFunction
	        Pass stencilOperation
	        Fail stencilOperation
	        ZFail stencilOperation
        }
        CGPROGRAM
        //……
        CGEND
    }
}

1):Ref

  • 对应 OpenGL 中 glStencilFunc(GLenum func, GLint ref, GLuint mask) 第二个参数 GLint ref
  • 范围 [0, 255]

指定模板测试的引用值,模板缓冲的内容会与这个值对比,模板缓冲默认值当然为0

2):ReadMask

  • 对应 OpenGL 中 glStencilFunc(GLenum func, GLint ref, GLuint mask) 第三个参数 GLuint mask
  • 范围 [0, 255],默认值 255

读遮罩,在模板测试对比引用值和储存的模板值前,对它们进行按位与操作

3):WriteMask

  • 对应 OpenGL 中 glStencilMask(GLint 0~255)
  • 范围 [0, 255],默认值 255

写遮罩,再写入模板缓冲时,对它们进行按位与操作(如果对应位为1,则对应位可写入缓存,如果对应位为0,则对应位不可写入缓存)

4):Comp

  • 对应 OpenGL 中 glStencilFunc(GLenum func, GLint ref, GLuint mask) 第一个参数 GLenum func
  • 枚举,默认值 Always

模板测试规则,也就是参考值和缓冲值比较规则

  • Always:永远通过测试
  • Never:永远不通过测试
  • Less:在片段模板值小于缓冲的模板值时通过测试
  • Equal:在片段模板值等于缓冲区的模板值时通过测试
  • LEqual:在片段模板值小于等于缓冲区的模板值时通过测试
  • Greater:在片段模板值大于缓冲区的模板值时通过测试
  • NotEqual:在片段模板值不等于缓冲区的模板值时通过测试
  • GEqual:在片段模板值大于等于缓冲区的模板值时通过测试

5):Pass

  • 对应 OpenGL 中 glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass) 第三个参数 GLenum dppass
  • 枚举,默认值 Keep

当模板测试和深度测试都通过时,模板缓冲的值处理方式

  • Keep:保持现有的模板值
  • Zero:将模板值置为0
  • Replace:替换,将模板值设置为 Ref 值
  • IncrSat:如果模板值不是最大值就将模板值+1
  • IncrWrap:与 IncrSat 一样将模板值+1,如果模板值已经是最大值则设为0
  • DecrSat:如果模板值不是最小值就将模板值-1
  • DecrWrap:与 DecrSat 一样将模板值-1,如果模板值已经是最小值则设为最大值
  • Invert:将模板值按位反转

6):Fail

  • 对应 OpenGL 中 glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass) 第三个参数 GLenum sfail
  • 枚举,默认值 Keep

当模板测试不通过时,模板缓冲的值处理方式

7):ZFail

  • 对应 OpenGL 中 glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass) 第二个参数 GLenum dpfail
  • 枚举,默认值 Keep

当模板测试通过、深度测试未通过时,模板缓冲的值处理方式

三、关于模板缓冲区

若 Shader 中应用了模板测试,那么对于每个像素,测试的过程如下:

  • StencilBufferValue:模板缓冲区当前模板值
(Ref & ReadMask) Comp (StencilBufferValue & ReadMask)

模板缓冲区的大小为固定的8位,这也是为什么 Ref 的值范围为 [0, 255],除此之外,若纹理缓冲设置为 Depth24Stencil8,那么8位模板值就会存在于 Zbuffer 中

在 Unity 屏幕后处理中使用 Stencil:

出乎意料之外:直接调用 Blit 无法实现模板测试效果,因为其无法识别深度模板缓冲,但是还好,如果是 Unity 默认的 Build-in 管线,可以使用下面的代码解决:

void OnRenderImage(RenderTexture src, RenderTexture dest)
{
    //……    material为你自己的材质
    RenderTexture buffer = RenderTexture.GetTemporary(src.width, src.height, 24);
    Graphics.SetRenderTarget(buffer.colorBuffer, src.depthBuffer);

    Graphics.Blit(src, buffer, material);
    Graphics.Blit(buffer, dest);
    RenderTexture.ReleaseTemporary(buffer);
}

原理是通过设置 SetRenderTarget() 让 Graphics.Blit 调用的时候能够获取到深度图信息,注意中间纹理的 depthBuffer 必须要为 24,这样才可以从中拿到模板信息

搞定之后就可以进行测试:

Subshader
{
    ZTest Always Cull Off ZWrite Off
    Pass
    {
        Stencil
        {
            Ref 1
            Comp Equal
        }
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        ENDCG
    }
}

如果黑屏了那就是没问题!所有的像素都没有通过模板测试

参考资料:

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值