Mask

Mask

RectMask2D

旧版本(2019.4之前)的方式

inline float UnityGet2DClipping (in float2 position, in float4 clipRect)
{
    float2 inside = step(clipRect.xy, position.xy) * step(position.xy, clipRect.zw);
    return inside.x * inside.y;
}

2019.4

// fs
#ifdef UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);  
color.a *= m.x * m.y;
#endif

裁剪

  • _ClipRect.xy是矩形左下角坐标,_ClipRect.zw是矩形右上角坐标,_ClipRect.zw - _ClipRect.xy表示矩形的宽高
  • OUT.mask.xy等于矩形左下角到点的位置组成的向量加上到矩形右上角点的位置组成的向量OUT.mask.xy = v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw,等同于(v.vertex.xy - clampedRect.xy) + (v.vertex.xy - clampedRect.zw)
  • OUT.mask.zw_UIMaskSoftnessX_UIMaskSoftnessY(在属性中限制为大于0)成反比 OUT.mask.zw = half2(0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));

以x为例,点在矩形

  • 左右边界上,abs(OUT.mask.x) = 矩形宽度
  • 内部,abs(OUT.mask.x) < 矩形宽度
  • 外部,abs(OUT.mask.x) > 矩形宽度

y也是同理

在矩形外部则_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy) < 0,用saturate截取到0~1并应用到alpha上,最后实现了矩形裁剪

Softness

同上,如果在矩形内部,越靠近矩形中心,则(_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy))越接近1

但图像都大于等于1像素,对于一个高度为100的矩形,即在距离边缘1像素的范围内x从0变化到1,y也已经从0变化到1了。之后大于1部分都会被截取到1,因此只有(在编辑器下的Scene视图)放大许多倍才能看清楚

函数为f(x)=x

image-20221014162214211

未放大,边缘1像素看不清楚

image-20221014161501869

放大后才能看清楚

image-20221014161347038

要做线性映射,则要分别除以_UIMaskSoftnessX、_UIMaskSoftnessY的两倍,即函数变为f(x)=x/100。两倍的原因是,比如_UIMaskSoftnessY为50,则对于一个高度为100的矩形在中心部分(距离边缘50像素)的位置上函数结果刚好为1

线性映射:
#ifdef UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) / (_UIMaskSoftnessY * 2));
color.a *= m.x * m.y;
#endif

计算结果是线性的,但人眼感知是非线性的会察觉到明显的条带边缘,因此边缘处不能只是用线性lerp,可以简单地替换为smoothstep来修复

#ifdef UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) / half2(_UIMaskSoftnessX * 2, _UIMaskSoftnessY * 2));
m = smoothstep(0, 1, m);
color.a *= m.x * m.y;
#endif
原本的方法
#ifdef UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
color.a *= m.x * m.y;
#endif

参考:【UGUI源码分析】Unity遮罩之RectMask2D详细解读 - iwiniwin - 博客园 (cnblogs.com)

SoftMaskForUGUI

原理:采样mask图,将其提供的alpha值传给所有子物体UI作为实际的alpha使用,这些UI的材质使用的shader全部替换为了支持SoftMask的特定shader

缺点:如果子UI重叠,重叠的部分对应的纹理像素是不透明的,但最后输出的片元的alpha值使用了mask的alpha,造成最后渲染的UI不会完全遮挡先前的UI,而是进行了透明度混合

outside_default.png

和默认UI不同的地方:

  • Blend One OneMinusSrcAlpha改成为Blend SrcAlpha OneMinusSrcAlpha
  • 片元着色器中color.rgb *= color.a 改成了 color.a *= SoftMask()
Shader "Hidden/UI/Default (SoftMaskable)"
{
    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

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
    }

    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]
        // 默认UI:Blend One OneMinusSrcAlpha,因为默认UI在片元着色器里直接用color.rgb *= color.a应用了alpha
        Blend SrcAlpha OneMinusSrcAlpha
        ColorMask [_ColorMask]

        Pass
        {
            Name "Default"
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"

            #pragma multi_compile __ UNITY_UI_CLIP_RECT
            #pragma multi_compile __ UNITY_UI_ALPHACLIP


            #include "Packages/com.coffee.softmask-for-ugui/Shaders/SoftMask.cginc"	// Add for soft mask
            #pragma shader_feature __ SOFTMASK_EDITOR	// Add for soft mask

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                UNITY_VERTEX_OUTPUT_STEREO
            };

            sampler2D _MainTex;
            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;
            float4 _MainTex_ST;

            v2f vert(appdata_t v)
            {
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.worldPosition = v.vertex;
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

                OUT.color = v.color * _Color;

                return OUT;
            }

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);

                #ifdef UNITY_UI_ALPHACLIP
                    clip (color.a - 0.001);
                #endif

                // 实际应用了mask的alpha的地方
                // 默认UI:color.rgb *= color.a
                color.a *= SoftMask(IN.vertex, IN.worldPosition);	// Add for soft mask

                return color;
            }
            ENDCG
        }
    }
}

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值