• Unity中UI Shader遮罩RectMask2D



    前言

    Unity中UI Shader遮罩RectMask2D


    一、需要定义一个变体UNITY_UI_CLIP_RECT

    UNITY_UI_CLIP_RECT

    当父级物体有Rect Mask 2D组件时激活.
    需要先手动定义此变体#pragma multi_compile _ UNITY_UI_CLIP_RECT
    同时需要声明:_ClipRect(一个四维向量,四个分量分别表示RectMask2D的左下角点的xy坐标与右上角点的xy坐标.)
    UnityGet2DClipping (float2 position, float4 clipRect)即可实现遮罩.

    //声明一个变体,用于RectMask使用
    #pragma multi_compile _ UNITY_UI_CLIP_RECT

    注意: 上面的 _ 前后均有空格

    在片元着色器使用以下代码测试看看基本功能
    #if UNITY_UI_CLIP_RECT
    return 1;
    #else
    return 0.5;
    #endif

    测试代码:

    Shader"MyShader/P1_1_8"
    {
        Properties
        {
            //命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
            //比如说,在更改 Image 的源图片时,同时更改这个
            [PerRendererData]_MainTex("MainTex",2D) = "white"{}
            _StencilComp ("Stencil Comparison", Float) = 8.000000
            _Stencil ("Stencil ID", Float) = 0.000000
            _StencilOp ("Stencil Operation", Float) = 0.000000
            _StencilWriteMask ("Stencil Write Mask", Float) = 255.000000
            _StencilReadMask ("Stencil Read Mask", Float) = 255.000000
            _ColorMask ("Color Mask", Float) = 15.000000
        }
        
        SubShader
        {
            //更改渲染队列(UI的渲染队列一般是半透明层的)
            Tags {"Queue" = "TransParent"}
            //混合模式
            Blend SrcAlpha OneMinusSrcAlpha
            
            ColorMask [_ColorMask]
            
            Stencil
            {
                Ref [_Stencil]
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]
                Comp [_StencilComp]
                Pass [_StencilOp]
            }
            Pass
            {
                CGPROGRAM
                #pragma vertex  vert
                #pragma fragment frag
                //声明一个变体,用于RectMask使用
                #pragma multi_compile _ UNITY_UI_CLIP_RECT
                #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
                {
                    #if UNITY_UI_CLIP_RECT
                        return  1;
                    #else
                        return 0.5;
                    #endif
                    
                    
                    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
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    效果:
    在这里插入图片描述
    在这里插入图片描述


    二、需要申明一个_ClipRect,这是使用上面这个变体需要使用的,这个属性并没有在Properties声明

    同时需要声明:_ClipRect(一个四维向量,四个分量分别表示RectMask2D的左下角点的xy坐标与右上角点的xy坐标.)

    这个坐标是用来存储RectMask所占的位置信息
    因为UI是一个矩形,所以记录了 左下角 和 右上角 顶点信息 后就可以知道 RectMask所占的位置
    在这里插入图片描述

    注意:在UGUI中模型顶点的本地坐标,经过顶点着色器传入片段着色器会转化为屏幕坐标(即看见的坐标值很大不是UI模型的本地坐标,而是UI模型的屏幕坐标)

    1、现在我们用简单的代码测试一下 _ClipRect 的使用

    主要逻辑:
    1、在应用程序阶段传入顶点着色器的结构体中 加入 顶点信息
    2、在顶点着色着色器传入片元着色器的结构体中 加入 顶点信息
    3、在 片元着色器中,使用 _ClipRect 的坐标信息用于判断,符合条件的返回 1(半透明白) ,不符合返回 0.5(半透明灰)

    Shader"MyShader/P1_1_8"
    {
        Properties
        {
            //命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
            //比如说,在更改 Image 的源图片时,同时更改这个
            [PerRendererData]_MainTex("MainTex",2D) = "white"{}
            _StencilComp ("Stencil Comparison", Float) = 8.000000
            _Stencil ("Stencil ID", Float) = 0.000000
            _StencilOp ("Stencil Operation", Float) = 0.000000
            _StencilWriteMask ("Stencil Write Mask", Float) = 255.000000
            _StencilReadMask ("Stencil Read Mask", Float) = 255.000000
            _ColorMask ("Color Mask", Float) = 15.000000
        }
        
        SubShader
        {
            //更改渲染队列(UI的渲染队列一般是半透明层的)
            Tags {"Queue" = "TransParent"}
            //混合模式
            Blend SrcAlpha OneMinusSrcAlpha
            
            ColorMask [_ColorMask]
            
            Stencil
            {
                Ref [_Stencil]
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]
                Comp [_StencilComp]
                Pass [_StencilOp]
            }
            Pass
            {
                CGPROGRAM
                #pragma vertex  vert
                #pragma fragment frag
                //声明一个变体,用于RectMask使用
                #pragma multi_compile _ UNITY_UI_CLIP_RECT
                #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 : COLOR;
                    //定义一个四维变量存储顶点信息
                    float4 vertex : TEXCOORD1;
                };
                
                sampler2D _MainTex;
                fixed4 _Color;
                //在使用 RectMask 需要使用的变体时,需要声明一个四维变量 _ClipRect
                float4 _ClipRect;
                
                
                v2f vert(appdata v)
                {
                    v2f o;
                    //把顶点信息转化到裁剪坐标下
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    o.color = v.color;
                    o.vertex = v.vertex;
                    return o;
                }
                fixed4 frag(v2f i) : SV_Target
                {
                    #if UNITY_UI_CLIP_RECT
                        if(_ClipRect.x < i.vertex.x)
                        {
                            return 1;
                        }
                        else
                        {
                            return 0.5;
                        }
                    #else
                        return 0.5;
                    #endif
                    
                    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
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    效果:
    请添加图片描述

    然后我们基于以上的基础,让 内层UI只在 _ClipRect 范围内渲染

    测试代码:

    Shader"MyShader/P1_1_8"
    {
        Properties
        {
            //命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
            //比如说,在更改 Image 的源图片时,同时更改这个
            [PerRendererData]_MainTex("MainTex",2D) = "white"{}
            _StencilComp ("Stencil Comparison", Float) = 8.000000
            _Stencil ("Stencil ID", Float) = 0.000000
            _StencilOp ("Stencil Operation", Float) = 0.000000
            _StencilWriteMask ("Stencil Write Mask", Float) = 255.000000
            _StencilReadMask ("Stencil Read Mask", Float) = 255.000000
            _ColorMask ("Color Mask", Float) = 15.000000
        }
        
        SubShader
        {
            //更改渲染队列(UI的渲染队列一般是半透明层的)
            Tags {"Queue" = "TransParent"}
            //混合模式
            Blend SrcAlpha OneMinusSrcAlpha
            
            ColorMask [_ColorMask]
            
            Stencil
            {
                Ref [_Stencil]
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]
                Comp [_StencilComp]
                Pass [_StencilOp]
            }
            Pass
            {
                CGPROGRAM
                #pragma vertex  vert
                #pragma fragment frag
                //声明一个变体,用于RectMask使用
                #pragma multi_compile _ UNITY_UI_CLIP_RECT
                #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 : COLOR;
                    //定义一个四维变量存储顶点信息
                    float4 vertex : TEXCOORD1;
                };
                
                sampler2D _MainTex;
                fixed4 _Color;
                //在使用 RectMask 需要使用的变体时,需要声明一个四维变量 _ClipRect
                float4 _ClipRect;
                
                
                v2f vert(appdata v)
                {
                    v2f o;
                    //把顶点信息转化到裁剪坐标下
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    o.color = v.color;
                    o.vertex = v.vertex;
                    return o;
                }
                fixed4 frag(v2f i) : SV_Target
                {
                    #if UNITY_UI_CLIP_RECT
                        if(_ClipRect.x < i.vertex.x && _ClipRect.z > i.vertex.x && _ClipRect.y < i.vertex.y && _ClipRect.w > i.vertex.y)
                        {
                            fixed4 mainTex = tex2D(_MainTex,i.uv);
                            return  mainTex * i.color;
                        }
                        else
                        {
                            return 0;
                        }
                    #else
                        return 0.5;
                    #endif
                    
                    
                }
                
                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
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    效果:
    请添加图片描述

    2、因为 if 语句在 Shader 中十分消耗性能,所以要避免使用 if 语句 ,if只适合用于理解原理(三目运算符也是同理)

    我们使用Math中的 step(a,b) 函数来解决这个问题
    如果a<=b返回1,否则返回0.

    如果使用 这个 Math 方法,则可以按这样的思路设计,使用宏判断后,在宏判断中记录 step 后的值,然后最后与需要输出的颜色混合输出即可。(因为0乘任何数等于0)

    所以这里把之前的条件语句转化为了如下语句
    value = step(_ClipRect.x,i.vertex.x) * step(i.vertex.x,_ClipRect.z) * step(_ClipRect.y,i.vertex.y) * step(i.vertex.y,_ClipRect.w);

    3、以上代码还可以再进一步优化,因为 step 不只可以用与点之间的比较,可以用于向量之间的比较,所以在以上代码的基础上,减少step的使用

    fixed2 rect = step(_ClipRect.xy,i.vertex.xy) * step(i.vertex.xy,_ClipRect.zw);
    value = rect.x * rect.y ;

    因为我们使用的混合模式为

    Blend SrcAlpha OneMinusSrcAlpha

    所以使用 纹理采样后的透明值 与输出结果相乘,即可让透明部分透明

    return mainTex * i.color * value * mainTex.a;

    4、使用 UnityGet2DClipping (float2 position, float4 clipRect)

    需要导入库:#include

    value = UnityGet2DClipping(i.vertex,_ClipRect);
    //函数实现 和 法3一样

    5、最终测试代码

    Shader"MyShader/P1_1_9"
    {
        Properties
        {
            //命名要按标准来,这个属性才可以和Unity组件中的属性产生关联
            //比如说,在更改 Image 的源图片时,同时更改这个
            [PerRendererData]_MainTex("MainTex",2D) = "white"{}
            _StencilComp ("Stencil Comparison", Float) = 8.000000
            _Stencil ("Stencil ID", Float) = 0.000000
            _StencilOp ("Stencil Operation", Float) = 0.000000
            _StencilWriteMask ("Stencil Write Mask", Float) = 255.000000
            _StencilReadMask ("Stencil Read Mask", Float) = 255.000000
            _ColorMask ("Color Mask", Float) = 15.000000
        }
        
        SubShader
        {
            //更改渲染队列(UI的渲染队列一般是半透明层的)
            Tags {"Queue" = "TransParent"}
            //混合模式
            Blend SrcAlpha OneMinusSrcAlpha
            
            ColorMask [_ColorMask]
            
            Stencil
            {
                Ref [_Stencil]
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]
                Comp [_StencilComp]
                Pass [_StencilOp]
            }
            Pass
            {
                CGPROGRAM
                #pragma vertex  vert
                #pragma fragment frag
                //声明一个变体,用于RectMask使用
                #pragma multi_compile _ UNITY_UI_CLIP_RECT
                #include 
    
                #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 : COLOR;
                    //定义一个四维变量存储顶点信息
                    float4 vertex : TEXCOORD1;
                };
                
                sampler2D _MainTex;
                fixed4 _Color;
                //在使用 RectMask 需要使用的变体时,需要声明一个四维变量 _ClipRect
                float4 _ClipRect;
                
                
                v2f vert(appdata v)
                {
                    v2f o;
                    //把顶点信息转化到裁剪坐标下
                    o.pos = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    o.color = v.color;
                    o.vertex = v.vertex;
                    return o;
                }
                fixed4 frag(v2f i) : SV_Target
                {
                    float value = 0;
                    #if UNITY_UI_CLIP_RECT
                        //法1、使用if有助于理解
                        /*if(_ClipRect.x < i.vertex.x && _ClipRect.z > i.vertex.x && _ClipRect.y < i.vertex.y && _ClipRect.w > i.vertex.y)
                        {
                            return 1;
                        }
                        else
                        {
                            return 0;
                        }*/
                        //法2、利用step来优化if
                        //value =  step(_ClipRect.x,i.vertex.x) * step(i.vertex.x,_ClipRect.z) * step(_ClipRect.y,i.vertex.y) * step(i.vertex.y,_ClipRect.w);
                        //法3、使用step进行向量比较,减少step的使用数量
                        /*fixed2 rect = step(_ClipRect.xy,i.vertex.xy) * step(i.vertex.xy,_ClipRect.zw);
                        value = rect.x * rect.y;*/
                        //法4、利用Unity自带函数实现
                        value = UnityGet2DClipping(i.vertex,_ClipRect);
                    #else
                        return 0.5;
                    #endif
                    
                    fixed4 mainTex = tex2D(_MainTex,i.uv);
                    return  mainTex * i.color * value * mainTex.a;
                }
                
                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
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
  • 相关阅读:
    摸鱼网站推荐,不会摸鱼的程序员不是好程序猿
    ResNet-50的网络结构:
    Node.js精进(1)——模块化
    技术分享 | 接口自动化实战演练及测试考察点
    【服务器数据恢复】EMC Unity存储误操作删除数据卷的数据恢复案例
    Java实现Modbus Tcp协议读写模拟工具数据
    云管理平台的安全措施
    c++ 可变参数模版 & 编译期排序
    MySQL数据库
    vue3 hook库
  • 原文地址:https://blog.csdn.net/qq_51603875/article/details/133185157