遮罩,顾名思义是一种可以掩盖其它元素的控件。常用于修改其它元素的外观,或限制元素的形状。比如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. Linux常用基础命令(二)

    Linux常用基础命令 一.-ls--列表显示目录内容 二.-alias--设置别名 三.-du--统计目录及文件空间占用情况 四.-mkdir--创建新目录 五.-touch--创建空文件 六.-l ...

  2. WIN10家庭版 访问WINXP 共享打印机

    WIN10家庭版 1.安装对应的打印机驱动 2.打开WIN10计算机---在地址栏中输入:\\计算机XP名称,显示对应的共享资源,直接选择即可.如果无法访问则进行如下第三步 3.设置过程 开始 -设置 ...

  3. python all any函数(相反)

    ''' all() 函数用于判断给定的可迭代参数 iterable 中的所有元素是否都为 TRUE,如果是返回 True,否则返回 False. 元素除了是 0.空.FALSE 外都算 TRUE. 语 ...

  4. C++11 左值引用和右值引用与引用折叠和完美转发

    1.左值与右值 最感性的认识. 当然,左值也是可以在右边的. 左值是可以被修改的,右值不能. 当然取地址也是. 生存周期一般左值会比右值的长,一般右值都计算时产生的无名临时对象,存在时间比较短. 下面 ...

  5. sql-1-准备

    一.准备工作 1.mysql安装和配环境 不要以exe文件安装,要下载压缩包安装 下载地址:https://dev.mysql.com/downloads/mysql 在系统path中加上bin目录 ...

  6. 小程序框架WePY 从入门到放弃踩坑合集

    小程序框架WePY 从入门到放弃踩坑合集 一点点介绍WePY 因为小程序的语法设计略迷, 所以x1 模块化起来并不方便, 所以x2 各厂就出了不少的框架用以方便小程序的开发, 腾讯看到别人家都出了框架 ...

  7. javascript学习笔记-(三)

    ES6标准新增了一种新的函数:Arrow Function(箭头函数) 案例: 为什么叫Arrow Function?因为它的定义用的就是一个箭头: x => x * x 上面的箭头函数相当于: ...

  8. Appium使用inspactor开始session报"Could not connect to server; are you sure it's running?"

    appium在使用inspactor start session时提示:Could not connect to server; are you sure it's running?如下图 解决方案为 ...

  9. 冒泡排序、选择排序、直接插入排序、快速排序、折半查找>从零开始学JAVA系列

    目录 冒泡排序.选择排序.直接插入排序 冒泡排序 选择排序 选择排序与冒泡排序的注意事项 小案例,使用选择排序完成对对象的排序 直接插入排序(插入排序) 快速排序(比较排序中效率最高的一种排序) 折半 ...

  10. centos7 下安装docker报错:You could try using...

    搞了台VPS,想要装docker,发现死活装不上,各种报错.之前系统是centos6,发现官方现在已经不支持centos6了,遂升级到centos7,然后还是出现下面这个错误. Error: Pack ...