(九)Mask详解

1.前言

本文从逻辑和原理上详细分析Ugui的Mask组件。Mask组件的逻辑没有RectMask2D复杂,但是原理稍微麻烦一点,用得到渲染的模板检测。

2.模板遮罩原理

2.1 模板值

以下图为例,假如均没有开启模板检测,canvas上只有一个RawImage组件,那么整个背景模板值为0。如果图中RawImage开启模板检测,并设置模板检测值为2(模板检测参考值是自定义的)。那么图片区域模板值为2,以外红色区域为0(模板值是以像素为单位进行设置的),如果开启根据透明通道值设置(Use Alpha Clip为true),则RawImage非透明区域为2,其他区域为0。
在这里插入图片描述

2.2 模板参数设置

模板值通过shader中设置相关变量实现的,我们通过ui默认的shader来解释。新建Material,并设置shader为UI/default,并赋值给一个RawImage,则可以看到参数面板如下,则可进行相关参数设置:
在这里插入图片描述
我们来解释一下参数及其意义:

Stencil ID:设置的模板参考值,如图设置为2

Stencil Operation:模板值操作方式,即如何进行操作,如果如图设置为2。则表示replace,即用Stencil ID的值代替此ui覆盖区域的像素的模板值。参考值如下所示:

namespace UnityEngine.Rendering
{
    public enum StencilOp
    {
        Keep = 0,//保持原值
        Zero = 1,//设置为0
        Replace = 2,//用当本参考值代替原值
        IncrementSaturate = 3,//逐次增加,当超过255时保持255
        DecrementSaturate = 4,//逐次减小,当少于0时保持0
        Invert = 5,//按位取反
        IncrementWrap = 6,//逐次增加,当超过255时继续从0开始
        DecrementWrap = 7//逐次减小,当少于0时继续从255开始
    }
}

Stencil Comparison:比较方法,参考值如下,如果设置为3(即equal),则表示如果当前RawImage渲染时,模板值如果与之前模板值相同(即通过模板测试)则渲染当前RawImage像素,反之舍弃。

namespace UnityEngine.Rendering
{
    public enum CompareFunction
    {
        Disabled = 0,//不进行深度和模板检测
        Never = 1,//模板和深度检测均不通过
        Less = 2,//小于前模板值时通过检测
        Equal = 3,//等于前模板值时通过检测
        LessEqual = 4,//小于等于前模板值时通过检测
        Greater = 5,//大于前模板值时通过检测
        NotEqual = 6,//不等于前模板值时通过检测
        GreaterEqual = 7,//大于等于前模板值时通过检测
        Always = 8//一直通过检测,与1相反
    }
}

Stencil Read/Write Mask:即读取或者重新写入模板值时的Mask,即与填写的值按位与进行计算。如果255则表示值不变,即取出/写入多少就是多少。

Color Mask:表示写入颜色值时写入的成分。如下所示:

namespace UnityEngine.Rendering
{
    [Flags]
    public enum ColorWriteMask
    {
        Alpha = 1,
        Blue = 2,
        Green = 4,
        Red = 8,
        All = 15
    }
}

Use Alpha Clip:是否进行根据通道值剔除,如果开启则会按像素进行处理,否则按Rect形状进行模板值操作。

2.3 模板参数使用

模板测试分两步进行,即模板值测试与模板值设置。

2.3.1 模板测试

用StecilBufferValue来代替模板缓冲值,则如果满足(Stencil ID&Read Mask) StencilComparison (StencilBufferValue&Read Mask)则表示通过测试,即当前像素可以写入FrameBuffer(若不理解可认为是写到屏幕上)。否则抛弃像素。

2.3.2 模板设置

如果Stencil Operation值为Replace,则表示设置当前像素位置的模板值为Stencil ID,如果为IncrementSaturate,则表示再当前卖报纸的基础上加1,如果超过255,则保持255。模板测试之后,无论模板测试通过与否,都要对模板进行相应的更新

2.4 模板测试应用

假如在上一张图片的基础上再加一张图片,且这两张均未开启模板检测,则显示结果如下。因为渲染顺序问题,则蓝色路飞头像会遮挡前面山水图。
在这里插入图片描述

然后设置山水图模板参数如下所示:
在这里插入图片描述
如上图片所示,我们开启模板检测,并设置山水图模板值为2。StencilComparision值为8,表示直接通过模板测试。则将此图片像素信息全部写入缓冲区(因为ColoMask值为15)。由于StencilOperation值为2(替换),则将StencilID值写入覆盖区域。对第二张蓝色路飞图开启模板检测,设置模板参数如下图所示。由于其StencilComparision参数为3,则表示若前后模板值相等即均为2,则通过测试保留像素值,否则丢弃此像素值。同时保持模板值不变,即原来是2的区域还是2,是0的区域还是0。
在这里插入图片描述效果如如下所示:
在这里插入图片描述
如果将第二张图片StencilComparision为5,即如果大于模板值则保留像素,那么第一章图片区域的像素就会被舍弃,而其他区域会被保留,效果类似于挖洞,如下所示:
在这里插入图片描述

3.Mask逻辑

上述为Mask的实现原理,至于子物体全部遮罩则是逻辑设置参数实现的。流程如下:
1)Mask启动时通知所有子游戏物体,可以进行进行模板值重新计算(m_ShouldRecalculateStencil标记为true)。
2)Graphic进行渲染时会设置材质,会调用如下属性,会查找所有IMaterialModifier,修改模板值。

        public virtual Material materialForRendering
        {
            get
            {
                var components = ListPool<Component>.Get();
                GetComponents(typeof(IMaterialModifier), components);

                var currentMat = material;
                for (var i = 0; i < components.Count; i++)
                    currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat);
                ListPool<Component>.Release(components);
                return currentMat;
            }
        }

3)由于Mask和MaskableGraphic均继承实现接口IMaterialModifier,所以根据层级设置相关模板参数,从而实现子物体遮罩想过。

4.结语

以上为Mask的基本原理与基本逻辑

posted @ 2020-04-11 17:52  81192  阅读(1101)  评论(0编辑  收藏  举报