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中,“场景”是一个视图,我们通过“场景”这个视图,来 ...
随机推荐
- npm run start失败&Node.js 查询指定端口运行情况及终止占用端口办法
缘由: node.js项目中运行npm run start命令脚本报错,No such file or directory 最开始以为是命令脚本找不到所谓的执行路径,但后面发现不是,是package. ...
- JSP核心技术
一.JSP基本概念 JSP是Java Server Page 的简称,跟Servlet 一样可以动态生成HTML响应,文件命名为XXX.jsp JSP 与Servlet 的区别: jsp经编译后就变成 ...
- python使用笔记16--操作redis
操作redis应先引入第三方模块 执行以下命令 pip install redis 1.redis常用方法 1 import redis 2 #decode_responses=True将bytes转 ...
- ffmpeg入门篇-滤镜的基本使用
转发自白狼栈:查看原文 滤镜 什么是滤镜?百度百科介绍说"滤镜主要是用来实现图像的各种特殊效果......". 我们最早在ffmpeg是如何转码的一文中了解过滤镜,来回顾下当时的转 ...
- [刘阳Java]_程序员Java编程进阶的5个注意点,别编程两三年还是增删改查
此文章也是关注网上好几篇技术文章后,今天分享出来.因为,总有在程序学习路上的小伙伴会感到迷茫.而迷茫存在的情况如下 第一种:在大学学习中出现的迷茫,不知道Java到底要学什么.学习Java的标准是什么 ...
- PAT乙级:1076 Wifi密码 (15分)
PAT乙级:1076 Wifi密码 (15分) 题干 下面是微博上流传的一张照片:"各位亲爱的同学们,鉴于大家有时需要使用 wifi,又怕耽误亲们的学习,现将 wifi 密码设置为下列数学题 ...
- php使用curl模拟post请求
废话不多说,直接上代码,做个笔记. $url="http://localhost/header_server.php"; $body = array("mobile&qu ...
- 你有没有乱用“leader”,担当是个好东西
PS:此文为个人认知,不足处请多多批评. 近期在一线leader(经理)身上发现了几个case,然后又回想起前几年自己做的一些傻事,可能都属于明面上leader不会说什么,但私下会有情绪的类型: Ca ...
- 【洛谷P1281 书的复制】二分+动态规划
分析 两个做法,一个DP,一个是二分. 二分:也就是二分枚举每个人分到的东西. DP:区间DP F[I][J]表示前i本书分给j个人用的最短时间 由于每一次j的状态由比j小的状态得出,所以要先枚举j, ...
- IntelliJ IDEA2021.2 常用快捷键汇总总结
Java开发环境:Windows10 64bit+JDK8+IDEA2021.2 =========================================================== ...