【问题标题】:Unity: UI Image alpha overlapUnity:UI 图像 alpha 重叠
【发布时间】:2019-12-04 13:56:31
【问题描述】:

问题:当 CanvasGroup 修改其透明度时,画布中的两个对象相互混合。

图片中的示例:场景具有深色背景,画布具有绿色和红色图像。如果两个对象的不透明度小于 100%,它们将混合。

左:不透明度为 100%;

中心:不透明度为 50%,图像混合(实际结果);

右:不透明度为 50%,图像不融合(预期结果)

如何防止根据图像顺序混合图像?我知道基于纹理的解决方案(第二台相机渲染到放置在画布中的纹理),但这似乎有点过头了。还有其他方法吗?着色器,通过代码进行更改,还有别的吗?

统一2019.3.0f1

【问题讨论】:

    标签: unity3d


    【解决方案1】:

    解决方案:一个新的着色器及其材质,以及一种额外的材质。 在某些情况下可以避免创建着色器!

    Unity 中的着色器有一个名为 Stencil 的工具。此工具维护可用于丢弃像素的像素缓冲区。

    CanvasImageMask 着色器:它是默认 UI 图像着色器的克隆,除了 Stencil 调整和用于舒适工作的微小添加。当进行蒙版渲染时,Stencil 会将像素缓冲区的值设置为 1 在它实际绘制的地方。第二个着色器对此更改做出反应并丢弃该位置的新像素。请记住,具有此类材质的对象(包含 CanvasImageMask 着色器)应首先出现在 Canvas 中,因为在渲染期间它们将修改像素缓冲区,以便稍后使用。

    所有应该被遮罩的对象都必须使用基于默认 UI 图像着色器和模板调整的另一种材质。此材质将丢弃缓冲区值等于或大于 1 的任何像素。

    请注意,可以避免这项工作。开发者可以创建 2 种材质(默认图像材质的克隆),并在编辑器中修改 Stencil 设置。但是这个解决方案有一个问题:如何创建这样一个不应该绘制的蒙版?最好的解决方案是将 alpha 通道设置为 1(共 255 个)。但如果 Canvas 使用 CanvasGroup,像素可以提前丢弃,遮罩将不起作用。

    CanvasImageMask 着色器隐藏 Stencil 设置(方便)并添加一个标志,它将隐藏蒙版中的所有像素,但仍然可以完成工作。

    CanvasImageMask:

    Shader "Custom/CanvasImageMask"
    {
        Properties
        {
            [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
            _Color ("Tint", Color) = (1,1,1,1)
            [MaterialToggle(DO_NOT_DRAW)]
            _DoNotDraw ("Do not draw", Float) = 0
        }
    
        SubShader
        {
            Tags
            { 
                "Queue"="Transparent" 
                "IgnoreProjector"="True" 
                "RenderType"="Transparent" 
                "PreviewType"="Plane"
                "CanUseSpriteAtlas"="True"
            }
    
            Stencil
            {
                Ref 1
                Comp Greater
                Pass Replace
            }
    
            Cull Off
            Lighting Off
            ZWrite Off
            ZTest [unity_GUIZTestMode]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask RGBA
    
            Pass
            {
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma shader_feature DO_NOT_DRAW
                #include "UnityCG.cginc"
    
                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                };
    
                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color    : COLOR;
                    half2 texcoord  : TEXCOORD0;
                };
    
                fixed4 _Color;
    
                v2f vert(appdata_t IN)
                {
                    v2f OUT;
                    OUT.vertex = UnityObjectToClipPos(IN.vertex);
                    OUT.texcoord = IN.texcoord;
    #ifdef UNITY_HALF_TEXEL_OFFSET
                    OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
    #endif
                    OUT.color = IN.color * _Color;
                    return OUT;
                }
    
                sampler2D _MainTex;
    
                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
                    clip (color.a - 0.01);
                    #ifdef DO_NOT_DRAW
                        color.a = 0;
                    #endif
                    return color;
                }
            ENDCG
            }
        }
    }
    


    如果要比较,这里是默认的 UI 图像着色器:

    Shader "UI/Default"
    {
        Properties
        {
            [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
            _Color ("Tint", Color) = (1,1,1,1)
    
            _StencilComp ("Stencil Comparison", Float) = 8
            _Stencil ("Stencil ID", Float) = 0
            _StencilOp ("Stencil Operation", Float) = 0
            _StencilWriteMask ("Stencil Write Mask", Float) = 255
            _StencilReadMask ("Stencil Read Mask", Float) = 255
    
            _ColorMask ("Color Mask", Float) = 15
        }
    
        SubShader
        {
            Tags
            { 
                "Queue"="Transparent" 
                "IgnoreProjector"="True" 
                "RenderType"="Transparent" 
                "PreviewType"="Plane"
                "CanUseSpriteAtlas"="True"
            }
    
            Stencil
            {
                Ref [_Stencil]
                Comp [_StencilComp]
                Pass [_StencilOp] 
                ReadMask [_StencilReadMask]
                WriteMask [_StencilWriteMask]
            }
    
            Cull Off
            Lighting Off
            ZWrite Off
            ZTest [unity_GUIZTestMode]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask [_ColorMask]
    
            Pass
            {
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
    
                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                };
    
                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color    : COLOR;
                    half2 texcoord  : TEXCOORD0;
                };
    
                fixed4 _Color;
    
                v2f vert(appdata_t IN)
                {
                    v2f OUT;
                    OUT.vertex = UnityObjectToClipPos(IN.vertex);
                    OUT.texcoord = IN.texcoord;
    #ifdef UNITY_HALF_TEXEL_OFFSET
                    OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
    #endif
                    OUT.color = IN.color * _Color;
                    return OUT;
                }
    
                sampler2D _MainTex;
    
                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
                    clip (color.a - 0.01);
                    return color;
                }
            ENDCG
            }
        }
    }
    

    总结一下:创建 CanvasImageMask,基于此着色器创建材质,并在画布上放置图像。创建另一个材质,基于默认着色器,将 Stencil ID 设置为 1,并将 Stencil comparison 设置为 5(也称为“更大”)。将此材质分配给画布上蒙版对象之后的任何其他图像。

    参考资料:

    【讨论】:

    • 它工作得很好,但是嵌套图像呢,我在容器图像父级中有一些图像,下面的所有内容都只是在应用这些材料之后弹出父级。
    • @LobsangWhite,请更具体一些。您期望什么顺序,应该屏蔽哪些图像?例如,这是我所做的:[root]->[root's child 1 = invisible mask]->[root's child 2 = image to be mask]->[any other child of root]。这行得通,因为在画布中顺序很重要。例如,一些嵌套也可以:[root]->[root's child 1 = invisible mask]->[mask's child1 = object to be masked]->[any other child of root]。第二个示例同样有效,因为画布中的顺序相同:root、child 1(child 1 的所有孩子)、child 2(child 2 的所有孩子)、child 3 等
    • 我要试试这个顺序,但是想象一个三明治,其中奶酪是进度条容器和在所有内容顶部引用的图标之间的进度条,通常所有看起来都像当使用画布组修改 alpha 时很糟糕,就像这里描述的问题一样。
    • @LobsangWhite 三明治类比可以更好地说明您的情况。因此,在您的示例中,育儿没有任何作用。绘图顺序可以。容器有图像吗?如果是这样,那么顺序如下:进度条形状的遮罩(模板ID = 1),图标形状的遮罩(模板ID = 1),容器(可遮罩,模板ID = 1),图标形状的遮罩(ID 2 ),进度条(可屏蔽,模板 id = 2),图标(默认材质/着色器)。 id=1 的遮罩将剪切进度条和图标所在的容器图像,而 id=2 的遮罩将剪切图标后面的进度条。
    • @LobsangWhite 我还有一个进度条和顶部的图标。但是,我的图标放在栏的一侧,而不是中间。所以我只使用 1 个蒙版来剪切除图标本身之外的所有内容(进度条和进度条的背景)。而且我正在避免在根目录中有图像的容器。它使我的开发或维护变得复杂。
    猜你喜欢
    • 1970-01-01
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多