Unity遮罩之Mask、RectMask2D与Sprite Mask适用场景分析
遮罩,顾名思义是一种可以掩盖其它元素的控件。常用于修改其它元素的外观,或限制元素的形状。比如ScrollView或者圆头像效果都有用到遮罩功能。本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实现原理,以及通过Unity不同遮罩之间实现方式的对比,找到每一种遮罩的最佳使用场合。
本文是UGUI遮罩系列的第三篇,也是最后一篇。前两篇分别是对Mask和RectMask2D的源码分析,详细解读了它们的原理与实现细节。这次的侧重点是对Mask和RectMask2D做一个对比分析,同时总结一下在Mask和RectMask2D不起作用的场景下如何实现遮罩效果。本文大部分内容建立在读者已了解Mask与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适用场景分析的更多相关文章
- 【UGUI源码分析】Unity遮罩之Mask详细解读
遮罩,顾名思义是一种可以掩盖其它元素的控件.常用于修改其它元素的外观,或限制元素的形状.比如ScrollView或者圆头像效果都有用到遮罩功能.本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实 ...
- 【UGUI源码分析】Unity遮罩之RectMask2D详细解读
遮罩,顾名思义是一种可以掩盖其它元素的控件.常用于修改其它元素的外观,或限制元素的形状.比如ScrollView或者圆头像效果都有用到遮罩功能.本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实 ...
- Sprite Atlas与Sprite Mask详解
https://www.sohu.com/a/169409304_280780 Unity 2017.1正式发布后,带来了一批能帮助大家更加简化工作流的新功能.今天这篇文章,将由Unity技术经理成亮 ...
- 自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析
自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析 我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm ...
- Unity 遮罩 点击panel以外的位置,panel关闭
public Class Panel_ATMRechage : IPanel{ private Dictionary<string,UISprite>mSprites; } protect ...
- Unity3D Mecanim :Body Mask的使用、 角色Retargeting原理分析、Apply RootMotion
一.Body Mask的使用 1.1.配置好骨骼后通过Muscles来微调角色骨骼中的运动范围,以避免角色在动画中的不正确的叠加或失真等现象. 1.2.身体遮罩BodyMask更形象的描述就是身体的开 ...
- unity UGUI实现类似NGUI切换Sprite的方式
很多都是使用NGUI的习惯,因为在NGUI中所有图片都打包在一个图集中,通过更改SpriteName就可以更改图片,so,为了方便调用UGUI的sprite,我们也同样需要为其创建一个asset文件. ...
- 提取文件中的每一个mask,并将mask命名为文件名字
import cv2 as cv import random import glob import os from PIL import Image import shutil def get_sam ...
- Unity学习笔记(一)——基本概念之场景(Scene)
场景,顾名思义就是我们在游戏中所看到的物品.建筑.人物.背景.声音.特效等,基本上和我们玩游戏时所看到的游戏“场景”是同一个概念. Unity 3D中,“场景”是一个视图,我们通过“场景”这个视图,来 ...
随机推荐
- C语言:char总结
char字符型数据1.用单引号限制的1字节的字符称为字符型数据,字符型常量2.字符型常量实质保存的是对应字符的ASCII码值,是一个整数3.字符型常量表示范围:0-2554.声明字符型变量 char ...
- SyntaxError: unexpected EOF while parsing成功解决
报错在eval()函数: 我加了个 if 判断是否为空,就可以正常运行了!
- 解决clipboard.js在移动端复制失败的问题
1.前沿 在项目中使用clipboard.js插件去实现点击按钮,复制一段网址到剪切板的功能.功能做好后,一开始无论这pc端还是移动端都能正常使用.突然某一天测出了一个bug:移动端复制失败,pc端是 ...
- Maven BOM!拿来吧你
what BOM? BOM(Bill of Materials)是由Maven提供的功能,它通过定义一整套相互兼容的jar包版本集合, 使用时只需要依赖该BOM文件,即可放心的使用需要的依赖jar包, ...
- Oracle19c 如何用rman duplicate 克隆一个数据库。(Backup-Based, achive log)
Oracle19c 如何用rman duplicate 克隆一个数据库.(Backup-Based, achive log) 首先克隆有两种方法,一种是Backup-Based,一种是Active方式 ...
- 【递归+树】FBI树
题目描述 我们可以把由"0"和"1"组成的字符串分为三类:全"0"串称为BB串,全"1"串称为I串,既含"0& ...
- 关于Vmware-Tools的安装问题:Please re-run this program as the super user. Execution aborted.
点击VM-Install VMware Tools在桌面上出现一张光盘包含3个文件,分别为manifest.txt:Vmware-tools-版本号.rpm和Vmware-tools-版本号.tar. ...
- C++第五十篇 -- 获取串口的描述信息
如何知道自己的电脑上有无串口呢? -- 手动 1. 查看电脑,看是否有串口器件(串口是一个九针的D型接口) 2. 在设备管理器上查看 乍一看,还以为是有两个串口,其实仔细看描述就知道,这是蓝牙虚拟串口 ...
- 第十八篇 -- GPIO学习
先学习一下GPIO,网上各种找资料,拼凑,所以就不一一贴网址了. 一.GPIO GPIO的英文全称General-Purpose Input /Output Ports,中文意思是通用I/O端口 一个 ...
- 基于MD5+RSA算法实现接口调用防扯皮级鉴权
概述 最近项目中需要对第三方开发接口调用,考虑了一下,准备采用MD5+RSA算对请求数据进行签名,来达到请求鉴权,过滤非法请求的目标. 数字签名采用MD5+RSA算法实现.RSA私钥要严格保密并提供安 ...