• Unity中Shader实现模板测试Stencil



    前言

    Unity中Shader实现模板测试


    组件中的Mask
    在这里插入图片描述
    Mask 和 Rect Mask 2D 是UI中的Mask
    Sprite Mask 是精灵中的Mask

    一、UI中的遮罩

    1、Mask ——> 模板测试

    2、RectMask2D ——> UNITY_UI_CLIP_RECT


    二、模板缓冲区Stencil一般是和Pass平行的部分,Pass部分写的是颜色缓冲区

    Stencil:

    模板缓冲区(StencilBuffer)可以为屏幕上的每个像素点保存一个无符号整数值,这个值的具体意义视程序的具体应用而定.在渲染的过程中,可以用这个值与一个预先设定的参考值相比较,根据比较的结果来决定是否更新相应的像素点的颜色值.这个比较的过程被称为模板测试.
    将StencilBuffer的值与ReadMask与运算,然后与Ref值进行Comp比较,结果为true时进行Pass操作,否则进行Fail操作,操作值写入StencilBuffer前先与WriteMask与运算.

    模版缓冲中的默认值为:0
    公式:(Ref & ReadMask) Comp (StencilBufferValue & ReadMask)
    一般读取掩码ReadMask都是默认的,不做修改
    Stencil
    {
    Ref [_Stencil]
    ReadMask [_StencilReadMask]
    WriteMask [_StencilWriteMask]
    Comp [_StencilComp] ((UnityEngine.Rendering.CompareFunction))
    Pass [_StencilOp] (UnityEngine.Rendering.StencilOp)
    Fail [_Fail]
    ZFail [_ZFail]
    }

    Ref: 设定的参考值,这个值将用来与模板缓冲中的值进行比较.取值范围位为0-255的整数.
    ReadMask: ReadMask的值将和Ref的值以及模板缓冲中的值进行按位与(&)操作,取值范围也是0-255的整数,默认值为255(二进制位11111111),即读取的时候不对Ref的值和模板缓冲中的值产生修改,读取的还是原始值.
    WriteMask: WriteMask的值是当写入模板缓冲时进行的按位与操作,取值范围是0-255的整数,默认值也是255,即不做任何修改.
    Comp: 定义Ref与模板缓冲中的值比较的操作函数,默认值为always.
    Pass: 当模板测试(和深度测试)通过时,则根据(stencilOperation值)对模板缓冲值进行处理,默认值为keep.
    Fail: 当模板测试(和深度测试)失败时,则根据(stencilOperation值)对模板缓冲值进行处理,默认值为keep.
    ZFail: 当模板测试通过而深度测试失败时,则根据(stencilOperation值)对模板缓冲值进行处理,默认值为keep

    Comp(比较操作)

    Less: 相当于“<”操作,即仅当左边<右边,模板测试通过,渲染像素.
    Greater: 相当于“>”操作,即仅当左边>右边,模板测试通过,渲染像素.
    Lequal: 相当于“<=”操作,即仅当左边<=右边,模板测试通过,渲染像素.
    Gequal: 相当于“>=”操作,即仅当左边>=右边,模板测试通过,渲染像素.
    Equal: 相当于“=”操作,即仅当左边=右边,模板测试通过,渲染像素.
    NotEqual: 相当于“!=”操作,即仅当左边!=右边,模板测试通过,渲染像素.
    Always: 不管公式两边为何值,模板测试总是通过,渲染像素.
    Never: 不管公式两边为何值,模板测试总是失败 ,像素被抛弃.

    Pass(模版缓冲区的更新)

    Keep: 保留当前缓冲中的内容,即stencilBufferValue不变.
    Zero: 将0写入缓冲,即stencilBufferValue值变为0.
    Replace: 将参考值写入缓冲,即将referenceValue赋值给stencilBufferValue.
    IncrSat: 将当前模板缓冲值加1,如果stencilBufferValue超过255了,那么保留为255,即不大于255.
    DecrSat: 将当前模板缓冲值减1,如果stencilBufferValue超过为0,那么保留为0,即不小于0.
    NotEqual: 相当于“!=”操作,即仅当左边!=右边,模板测试通过,渲染像素.
    Invert: 将当前模板缓冲值(stencilBufferValue)按位取反.
    IncrWrap: 当前缓冲的值加1,如果缓冲值超过255了,那么变成0,(然后继续自增).
    DecrWrap: 当前缓冲的值减1,如果缓冲值已经为0,那么变成255,(然后继续自减).


    三、实际使用

    1、在使用模板缓冲区前,需要如下图设置一个遮罩图层

    在这里插入图片描述

    2、设置完后,在内层的Image使用的材质的Shader中添加如下代码,即可实现只在有遮罩的部分,渲染后渲染的部分

    在这里插入图片描述

    Stencil
    {
    Ref 1
    //ReadMask [_StencilReadMask]
    //WriteMask [_StencilWriteMask]
    Comp Equal
    Pass Keep
    //Fail [_Fail]
    //ZFail [_ZFail]
    }

    这里的 Stencil Id:1 对应的是Shader中的 Ref 1。
    Comp使用Equal,则是用模板缓冲区的值于 Ref 的值相比
    Pass使用Keep,则是比较通过的值,保存缓冲区中的值

    测试代码:

    Shader"MyShader/P1_1_6"
    {
        Properties
        {
            //命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
            //比如说,在更改 Image 的源图片时,同时更改这个
            [PerRendererData]_MainTex("MainTex",2D) = "white"{}
           
        }
        
        SubShader
        {
            //更改渲染队列(UI的渲染队列一般是半透明层的)
            Tags {"Queue" = "TransParent"}
            //混合模式
            Blend SrcAlpha OneMinusSrcAlpha
            
            Stencil
             {
                Ref 1
                //ReadMask [_StencilReadMask]
                //WriteMask [_StencilWriteMask]
                Comp Equal
                Pass Keep
                //Fail [_Fail]
                //ZFail [_ZFail]
            }
            Pass
            {
                CGPROGRAM
                #pragma vertex  vert
                #pragma fragment frag
                #include "UnityCG.cginc"
                //存储 应用程序输入到顶点着色器的信息
                struct appdata
                {
                    //顶点信息
                    float4 vertex:POSITION;
                    float2 uv : TEXCOORD;
                    //这里定义一个语义为Color的4维向量,用于传入顶点颜色,设置语义为COLOR后,这个变量就会与顶点颜色对应
                    fixed4 color:COLOR;
                };
                //存储 顶点着色器输入到片元着色器的信息
                struct v2f
                {
                    //裁剪空间下的位置信息(SV_POSITION是必须的)
                    float4 pos:SV_POSITION;
                    float2 uv : TEXCOORD;
                    //这里的语义主要代表精度不同,TEXCOORD 在这里只是代表高精度
                    fixed4 color : TEXCOORD1;
                };
                
                sampler2D _MainTex;
                fixed4 _Color;
                v2f vert(appdata v)
                {
                    v2f o;
                    //把顶点信息转化到裁剪坐标下
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    o.color = v.color;
                    return o;
                }
                fixed4 frag(v2f i) : SV_Target
                {
                    fixed4 mainTex = tex2D(_MainTex,i.uv);
                    return  mainTex * i.color;
                }
                
                ENDCG
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    效果:
    请添加图片描述

    四、让实现的模板测试更加方便自定义

    1、在属性面板暴露一个int类型,提供给 Ref 使用

    _Ref(“Stencil Ref”,int) = 0

    2、在属性面板暴露一个int类型,提供给 Comp 使用,并且设置成Unity内置的几个枚举类型[Enum(UnityEngine.Rendering.CompareFunction)]

    [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp(“Stencil Comp”,int) = 0

    3、在属性面板暴露一个int类型,提供给 Pass 使用,并且设置成Unity内置的几个枚举类型[Enum(UnityEngine.Rendering.StencilOp)]

    [Enum(UnityEngine.Rendering.StencilOp)]_StencilOp(“Stencil Op”,int) = 0

    4、最终测试代码

    Shader"MyShader/P1_1_6"
    {
        Properties
        {
            //命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
            //比如说,在更改 Image 的源图片时,同时更改这个
            [PerRendererData]_MainTex("MainTex",2D) = "white"{}
            //暴露一个变量来供模板测试中 Ref 使用
            _Ref("Stencil Ref",int) = 0
            //暴露一个变量来供模板测试中 Comp 使用
            [Enum(UnityEngine.Rendering.CompareFunction)]_StencilComp("Stencil Comp",int) = 0
            
            [Enum(UnityEngine.Rendering.StencilOp)]_StencilOp("Stencil Op",int) = 0
        }
        
        SubShader
        {
            //更改渲染队列(UI的渲染队列一般是半透明层的)
            Tags {"Queue" = "TransParent"}
            //混合模式
            Blend SrcAlpha OneMinusSrcAlpha
            
            Stencil
            {
                Ref [_Ref]
                //以下两个属性一般不做修改
                //ReadMask [_StencilReadMask]
                //WriteMask [_StencilWriteMask]
                Comp [_StencilComp]
                Pass [_StencilOp]
                //Fail [_Fail]
                //ZFail [_ZFail]
            }
            Pass
            {
                CGPROGRAM
                #pragma vertex  vert
                #pragma fragment frag
                #include "UnityCG.cginc"
                //存储 应用程序输入到顶点着色器的信息
                struct appdata
                {
                    //顶点信息
                    float4 vertex:POSITION;
                    float2 uv : TEXCOORD;
                    //这里定义一个语义为Color的4维向量,用于传入顶点颜色,设置语义为COLOR后,这个变量就会与顶点颜色对应
                    fixed4 color:COLOR;
                };
                //存储 顶点着色器输入到片元着色器的信息
                struct v2f
                {
                    //裁剪空间下的位置信息(SV_POSITION是必须的)
                    float4 pos:SV_POSITION;
                    float2 uv : TEXCOORD;
                    //这里的语义主要代表精度不同,TEXCOORD 在这里只是代表高精度
                    fixed4 color : TEXCOORD1;
                };
                
                sampler2D _MainTex;
                fixed4 _Color;
                v2f vert(appdata v)
                {
                    v2f o;
                    //把顶点信息转化到裁剪坐标下
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    o.color = v.color;
                    return o;
                }
                fixed4 frag(v2f i) : SV_Target
                {
                    fixed4 mainTex = tex2D(_MainTex,i.uv);
                    return  mainTex * i.color;
                }
                
                ENDCG
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    效果:
    请添加图片描述

  • 相关阅读:
    jmeter csv可变参数压测
    少儿编程竞赛概览
    实践案例:平安健康的 Dubbo3 迁移历程总结
    【虚拟线程】java21虚拟线程用法 限流等
    Dubbo3 Triple 协议简介与选型思考
    CentOS 6.6系统怎么安装?CentOS Linux系统安装配置图解教程
    ELK + Filebeat 分布式日志管理平台部署
    jvm实现的锁优化
    eNSP-打开华为USG6000V1防火墙web管理页面方法
    目标级联分析法( Analytical Target Cascading , ATC )理论matlab程序
  • 原文地址:https://blog.csdn.net/qq_51603875/article/details/133046081