遮罩,顾名思义是一种可以掩盖其它元素的控件。常用于修改其它元素的外观,或限制元素的形状。比如ScrollView或者圆头像效果都有用到遮罩功能。本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实现原理,以及通过Unity不同遮罩之间实现方式的对比,找到每一种遮罩的最佳使用场合。

本文是UGUI遮罩系列的第三篇,也是最后一篇。前两篇分别是对Mask和RectMask2D的源码分析,详细解读了它们的原理与实现细节。这次的侧重点是对Mask和RectMask2D做一个对比分析,同时总结一下在Mask和RectMask2D不起作用的场景下如何实现遮罩效果。本文大部分内容建立在读者已了解Mask与RectMask2D原理的基础之上,所以在阅读本文前建议先看下前两篇文章。

  1. 【UGUI源码分析】Unity遮罩之Mask详细解读
  2. 【UGUI源码分析】Unity遮罩之RectMask2D详细解读

本文所做的一些测试与验证均基于Unity2019.4版本

Mask与RectMask2D对比

1. Mask遮罩的大小与形状依赖于Graphic,而RectMask2D只需要依赖RectTransform

Mask是利用Graphic渲染时修改对应片元的模板值来确定遮罩的大小与形状的,Graphic的形状决定了Mask遮罩的形状。因此缺少Graphic组件,Mask遮罩将会失效。当禁用了对象的Graphic组件,比如Image组件,Unity会有以下警告提示

RectMask2D是利用自己的RectTransform计算出裁剪矩形,然后降低不在矩形内的片元透明度来实现遮罩效果,因此不需要依赖Graphic组件

2. Mask支持圆形或其他形状遮罩, 而RectMask2D只支持矩形

Mask遮罩形状可以更加多样,由于Mask遮罩的形状由Graphic决定,所以利用不同的Graphic可以实现不同形状的遮罩

而RectMask2D通过RectTransform计算裁剪矩形的机制导致它只能支持矩形遮罩,仅在 2D 空间中有效,不能正确掩盖不共面的元素

3. Mask会增加drawcall

除了绘制元素本身所需的1个drawcall以外,Mask还会额外增加2个drawcall。一个用来在绘制元素前修改模板缓冲的值,另一个用来在所有UI绘制完后将模板缓冲的值恢复原样

举个栗子,如下所示的一个UI场景,画布下一个带有Mask组件的panel父节点,其下有一个子节点Image

通过Unity的帧调试器查看渲染过程,共有3次drawcall

3次drawcall的区别主要在于模板参数的不同。第一次是总是通过(Stencil Comp:Always)模板测试,并将模板值替换(Stencil Pass:Replace)为1(Stencil Ref:1)。第二次是用于绘制Image的。第三次是总是通过(Stencil Comp:Always)模板测试,并将模板值设置为0(Stencil Pass:Zero),即起到擦除模板值的作用。

4. RectMask2D可能会破坏合批

有如下所示的一个测试场景,Panel1和Panel2都是只挂有RectTransform组件的单纯父节点,其下都有一个Image子节点,正常情况下应该可以合批,drawcall应为1

通过帧调试器查看,确实如此,成功合批,drawcall是1

此时给Panel1添加一个RectMask2D组件,实现遮罩效果。Panel2保持不变

通过帧调试器查看,drawcall数量是2,原本的合批被破坏了。

由此,网上查到的一些资料会得出“RectMask2D节点下的所有孩子都不能与外界UI节点合批且多个RectMask2D之间不能合批”的结论,实际上这是一种不严谨的说法,甚至是错误的。要搞清楚这个问题,需要先弄明白为什么RectMask2D会破坏合批?

通过帧调试器可以发现,是RectMask2D传递裁剪矩形时,修改了Shader的参数,导致不能合批。从下图可以看到2次drawcall的区别就在于_ClipRect不同

既然是裁剪矩形参数不同导致不能合批,那如果将两个裁剪矩形参数设置为一致是不是就能合批了呢?动手验证一下,给Panel1和Panel2都添加上RectMask2D组件,同时将它们的RectTransform参数设置为完全一致(这样可以保证裁剪矩形参数相同),然后把Panel1的子节点Image往左移,Panel2的子节点Image往右移,让它们都能显示出来。最终效果如下图所示

再次测试后可以看到drawcall只有1次了,_ClipRect是相同的值。因此可以得出结论,RectMask2D确实由于裁剪矩形参数的设置会破坏合批,但不是一定的。在满足条件时,RectMask2D节点下的孩子也能与外界UI节点合批,多个RectMask2D之间也是能合批的。

5. Mask与RectMask2D用哪个?

Mask的实现利用了模板缓冲区,会增加2个drawcall,性能会受到一定影响。简单的UGUI界面,还是建议使用RectMask2D,相对来说性能更强,也无需额外的绘制调用。但由于RectMask2D也有可能破坏合批,在复杂的情况下,并没有确切的结论来判断哪个更优,只能利用工具实际测试找到最优者,具体问题具体分析才是正确做法。当然,诸如圆形遮罩等一些RectMask2D无法胜任的场景,还是要使用Mask

粒子系统实现遮罩效果

游戏的UI界面也经常会添加粒子效果,有时也会需要对粒子添加遮罩。Mask和RectMask2D只适用于UGUI,对粒子系统无法生效。此时可以使用SpriteMask。SpriteMask的原理与Mask相同,都是基于模板测试实现。

粒子系统的Renderer模块有对应的Mask属性设置,可以调整粒子在精灵遮罩外部和内部的可见性

MeshRenderer实现遮罩效果

UI界面添加的一些特效也有可能是MeshRenderer实现的,例如利用Shader制作的顶点动画。但MeshRenderer没有提供Mask相关设置,无法使用遮罩。好在基于模板测试实现遮罩的原理都是相同的,可以自己动手修改MeshRenderer使用的材质,在Shader中添加ShaderLab模板配置来使用模板测试

需要添加到Shader中的代码如下所示

Properties
{
_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
} Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}

添加完成后,材质界面会多出模板相关的配置,如下所示

再配合SpriteMask,修改对应的模板参数,就可以模拟遮罩效果了。例如

  • Stencil Comparison设置为3,就相当于"Visible Inside Mask"
  • Stencil Comparison设置为6,就相当于"Visible Outside Mask"
  • Stencil Comparison设置为8,就相当于"No Masking"

参考

Unity遮罩之Mask、RectMask2D与Sprite Mask适用场景分析的更多相关文章

  1. 【UGUI源码分析】Unity遮罩之Mask详细解读

    遮罩,顾名思义是一种可以掩盖其它元素的控件.常用于修改其它元素的外观,或限制元素的形状.比如ScrollView或者圆头像效果都有用到遮罩功能.本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实 ...

  2. 【UGUI源码分析】Unity遮罩之RectMask2D详细解读

    遮罩,顾名思义是一种可以掩盖其它元素的控件.常用于修改其它元素的外观,或限制元素的形状.比如ScrollView或者圆头像效果都有用到遮罩功能.本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实 ...

  3. Sprite Atlas与Sprite Mask详解

    https://www.sohu.com/a/169409304_280780 Unity 2017.1正式发布后,带来了一批能帮助大家更加简化工作流的新功能.今天这篇文章,将由Unity技术经理成亮 ...

  4. 自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析

    自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析 我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm ...

  5. Unity 遮罩 点击panel以外的位置,panel关闭

    public Class Panel_ATMRechage : IPanel{ private Dictionary<string,UISprite>mSprites; } protect ...

  6. Unity3D Mecanim :Body Mask的使用、 角色Retargeting原理分析、Apply RootMotion

    一.Body Mask的使用 1.1.配置好骨骼后通过Muscles来微调角色骨骼中的运动范围,以避免角色在动画中的不正确的叠加或失真等现象. 1.2.身体遮罩BodyMask更形象的描述就是身体的开 ...

  7. unity UGUI实现类似NGUI切换Sprite的方式

    很多都是使用NGUI的习惯,因为在NGUI中所有图片都打包在一个图集中,通过更改SpriteName就可以更改图片,so,为了方便调用UGUI的sprite,我们也同样需要为其创建一个asset文件. ...

  8. 提取文件中的每一个mask,并将mask命名为文件名字

    import cv2 as cv import random import glob import os from PIL import Image import shutil def get_sam ...

  9. Unity学习笔记(一)——基本概念之场景(Scene)

    场景,顾名思义就是我们在游戏中所看到的物品.建筑.人物.背景.声音.特效等,基本上和我们玩游戏时所看到的游戏“场景”是同一个概念. Unity 3D中,“场景”是一个视图,我们通过“场景”这个视图,来 ...

随机推荐

  1. luogu2594 [ZJOI2009]染色游戏

    做法其他题解已经说得很清楚了,但似乎没有对于本题 SG 函数正确性的证明,我来口胡一下( 证明: 猜想: \[\operatorname{SG}(i,j)=\begin{cases}\operator ...

  2. HCNA Routing&Switching之动态路由协议OSPF建立邻居的条件

    前文我们了解了OSPF的router id.数据包结构.类型.不同类型的数据包作用以及OSPF状态机制,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15027 ...

  3. 论文阅读:hector_slam: A Flexible and Scalable SLAM System with Full 3D Motion Estimation.

    参考:<A Flexible and Scalable SLAM System with Full 3D Motion Estimation.> 该论文是ROS中hector_mappin ...

  4. HTML5-CSS(一)

    一.创建 CSS 样式表有三种方式 1. 元素内嵌样式<p style="color:red;font-size:50px;">这是一段文本</p>解释:即 ...

  5. Vue 可拖拽组件 Vue Smooth DnD 详解和应用演示

    本文发布自 https://www.cnblogs.com/wenruo/p/15061907.html 转载请注明出处. 简介和 Demo 展示 最近需要有个拖拽列表的需求,发现一个简单好用的 Vu ...

  6. Leetcode春季打卡活动 第二题:206. 反转链表

    Leetcode春季打卡活动 第二题:206. 反转链表 206. 反转链表 Talk is cheap . Show me the code . /** * Definition for singl ...

  7. 记一次 .NET 某云采购平台API 挂死分析

    一:背景 1. 讲故事 大概有两个月没写博客了,关注我的朋友应该知道我最近都把精力花在了星球,这两个月时间也陆陆续续的有朋友求助如何分析dump,有些朋友太客气了,给了大大的红包,哈哈,手里面也攒了1 ...

  8. CF459E-DP

    CF459E-DP 核心代码15行 思路 观察数据范围,我们建m层分层图跑最短路想到DP. DP最大的特点就是无后效性.那么我们这一题哪个条件无后效性呢? 发现DP值一定从边权小于当前点的位置转移而来 ...

  9. Springboot+Mybatis+小程序

    思维导图: 项目效果图 一览界面: 新增界面:

  10. [BSidesCF 2020]Had a bad day 1--PHP伪协议

    首先先打开主页,审查代码,并没有什么特别的地方使用dirsearch,发现flag.php![在这里插入图片描述](https://img-blog.csdnimg.cn/82348deddfd94c ...