UnityShader29:模板测试

Unity3D 专栏收录该内容
51 篇文章 9 订阅

 

一、什么是模板测试

OpenGL基础30:模板测试

Unity官方文档:https://docs.unity3d.com/Manual/SL-Stencil.html

一个很经典的模板测试例子就是 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
    }
}

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

 

 

参考资料:

 

  • 2
    点赞
  • 1
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值