(九)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的基本原理与基本逻辑

原文地址:https://www.cnblogs.com/llstart-new0201/p/12681277.html

时间: 2024-08-08 06:58:25

(九)Mask详解的相关文章

java提高篇(九)-----详解匿名内部类

摘自http://blog.csdn.net/chenssy/article/details/13170015 java提高篇(九)-----详解匿名内部类 在Java提高篇-----详解内部类中对匿名内部类做了一个简单的介绍,但是内部类还存在很多其他细节问题,所以就衍生出这篇博客.在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.如何初始化匿名内部类.匿名内部类使用的形参为何要为final. 一.使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式

spring学习(九)--详解spring启动配置(经典文章)

摘自:https://www.jianshu.com/p/280c7e720d0c spring的启动是建筑在servlet容器之上的,所有web工程的初始位置就是web.xml,它配置了servlet的上下文(context)和监听器(Listener),下面就来看看web.xml里面的配置: <!--上下文监听器,用于监听servlet的启动过程--> <listener> <description>ServletContextListener</descri

[顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功)

原文:[顶]ORACLE PL/SQL编程详解之二:PL/SQL块结构和组成元素(为山九仞,岂一日之功) [顶]ORACLE PL/SQL编程详解之二: PL/SQL块结构和组成元素(为山九仞,岂一日之功) 继上四篇:ORACLE PL/SQL编程之八:把触发器说透                ORACLE PL/SQL编程之六:把过程与函数说透(穷追猛打,把根儿都拔起!)                [推荐]ORACLE PL/SQL编程之四:把游标说透(不怕做不到,只怕想不到) [推荐]

三十九、git add详解

一.前言git add命令主要用于把我们要提交的文件的信息添加到索引库中.当我们使用git commit时,git将依据索引库中的内容来进行文件的提交.二.基本git add <path>表示 add to index only files created or modified and not those deleted 我通常是通过git add <path>的形式把我们<path>添加到索引库中,<path>可以是文件也可以是目录.git不仅能判断出&

Android 颜色渲染(九) PorterDuff及Xfermode详解

版权声明:本文为博主原创文章,未经博主允许不得转载. Android 颜色渲染(九)  PorterDuff及Xfermode详解 之前已经讲过了除ComposeShader之外Shader的全部子类, 在讲ComposeShader(组合渲染)之前,  由于构造ComposeShader需要 PorterDuffXfermode或者PorterDuff.Mode作为参数,所以在此先详细地了解下这两个类的作用,这对之后的绘图会有很大的帮 助: 在讲具体的使用之前补充一点知识,这就是 Proter

“全栈2019”Java第九十九章:局部内部类与继承详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第九十九章:局部内部类与继承详解 下一章 "全栈2019"Java第一百章:局部内部类可以实现接口吗? 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学习小

“全栈2019”Java多线程第十九章:死锁详解

难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多线程第十九章:死锁详解 下一章 "全栈2019"Java多线程第二十章:同步方法产生死锁的例子 学习小组 加入同步学习小组,共同交流与进步. 方式一:关注头条号Gorhaf,私信"Java学习小组". 方式二:关注公众号Gorhaf,回复"Java学习小组&q

轻松学习JavaScript二十九:JavaScript中的this详解

这几天在看很多的JS的代码,多次出现this关键字,有时候表示不理解,就仔细看了这一方面的知识. 在JavaScript语言中,this的定义是:this是包含它的函数作为方法被调用时所属的对象.说明:这句话有点咬 嘴,但一个多余的字也没有,定义非常准确,我们可以分3部分来理解它:1包含它的函数.2作为方法被调用时.3所 属的对象.随着函数使用场合的不同,this的值会发生变化.但是有一个总的原则,那就是this指的是,调用函数的那 个对象. this是Javascript语言的一个关键字,它代

标准文件IO详解(九)---fileno函数详解

在前面笔记“打开流详解”中提到了 fdopen 函数,可以通过文件描述符 fd 来获取对应的文件流指针.而同时 C 库函数提供了 fileno 函数,这个函数的作用就是能够通过 文件流指针来获取对应的 文件描述符 fd . ======================================================= 函数原型: 函数参数: stream:要操作的文件流指针 返回值: 函数返回与文件流指针对应的文件描述符,此函数不会出错(和umask函数类似) =========