Unity UGUI中Mask和RectMask2D
每个月一次经验分享,连比划带抄的完成了。

1、 Mask和RectMask2D原理
(1)、Mask
由于裁切需要同时裁切图片和文本,所以Image和Text都会派生自MaskableGraphic。 如果要让Mask节点下的元素裁切,那么它需要占一个DrawCall,因为这些元素需要一个新的Shader参数来渲染。(查看源码)Image对象在进行Rebuild()时,UpdateMaterial()方法中会获取需要渲染的材质,并且判断当前对象的组件是否有继承IMaterialModifier接口,如果有那么它就是绑定了Mask脚本,接着调用上面提到的GetModifiedMaterial方法修改材质上Shader的参数。
Mask的原理就是利用了StencilBuffer(模板缓冲),它里面记录了一个ID,被裁切元素也有StencilBuffer(模板缓冲)的ID,并且和Mask里的比较,相同才会被渲染。因为模板缓冲可以提供模板的区域,也就是前面设置的圆形图片,所以最终会将元素裁切到这个圆心图片中。 如图所示,在Mask外面放一个普通的图片,默认情况下Stencil Ref的值是0,所以它不会被裁切,永远会显示出来。
(2)、RectMask2D
Mask2D会在OnEnable()方法中,将当前组件注册ClipperRegistry.Register(this);这样在上面ClipperRegistry.instance.Cull();方法时就可以遍历所有Mask2D组件并且调用它们的PerformClipping()方法了。PerformClipping()方法,需要找到所有需要裁切的UI元素,因为Image和Text都继承了IClippable接口,最终将调用Cull()进行裁切。
RectMask2D会将RectTransform的区域作为_ClipRect传入Shader中,并且激活UNITY_UI_CLIP_RECT的Keywords。Stencil Ref 的值是0 表示它并没有使用模板缓冲比较,如果只是矩形裁切,RectMask2D并且它不需要一个无效的渲染用于模板比较,所以RectMask2D在特定情况下的效率会比Mask要高。
2、 组件用法
(1)、Mask组件可以实现遮罩的效果,将一个图像设为拥有mask组件图像的子物体,最后就会隐藏掉子图像和mask图像不重合的部分,如下图分别对比Mask和子物体的区域

在“Mask”物体上添加Mask组件后的效果就是右图效果
(2)、RectMask2D的用法和mask大致相同,不过RectMask2D只能裁剪一个矩形区域,同时RectMask2D可以选择边缘虚化,RectMask2D是根据rect的width和height进行裁剪, 具体效果如下图所示,

3、 性能区分(前提一个图集才能合并,打了图集)
(1)、Mask组件需要依赖一个Image组件,裁剪区域就是Image的大小。
Mask会在首尾(首=Mask节点,尾=Mask节点下的子节点遍历完后)drawcall,多个Mask间如果符合合批条件这两个drawcall可以对应合批(mask1 的首 和 mask2 的首合;mask1 的尾 和 mask2 的尾合。首尾不能合)Mask内的UI节点和非Mask外的UI节点不能合批,但多个Mask内的UI节点间如果符合合批条件,可以合批。具体来说:新建一个场景,默认drawcall是2个;现在添加一个mask,drawcall+3,Mask导致2个drawcall(第1个和第3个,一头一尾),Mask下的子节点Image导致1个drawcall
而这时增加两个mask,不要重叠:
还是5个drawcall, 没有变化。Unity把2个Mask进行了网格合并, 3个drawcall, 分别为[2个Mask头]、[2个Image]、[2个Mask尾].这里可以看出, Mask之间是可以进行合并的, 从而不额外增加drawcall,而如果放到一起

这是因为Unity的合批需要同渲染层级(depth), 同材质, 同图集, 如果重叠了, depth就不同了, 6个drawcall分别为Mask头、Mask的Image、Mask尾、Mask(1)头、Mask(1)的Image、Mask(1)尾.
Mask小结:
a.多个Mask之间可以进行合批(头和头合批, 子对象和子对象合批, 尾和尾合批),需要同渲染层级(depth), 同材质, 同图集.
b.Mask内外不能进行合批.
(2)、看下RectMask2D的情况

只有新增1个子节点Image的drawcall, 而RectMask2D不会导致drawcall.
把RectMask2D复制一个出来, 然后把位置摆开

drawcall为4, 因为RectMask2D本身不会导致drawcall, 所以RectMask2D之间不能进行合批.
RectMask2D小结:
a.RectMask2D本身不产生drawcall.
b.不同RectMask2D的子对象不能合批

Mask遮罩组件和 RectMask2D遮罩组件的选择总结
在图像很大且cpu任务较重的的情况下,mask会对性能有明显的影响,而在图像数量较多时mask略好于RectMask2D,
个人理解是:
当一个界面只有一个mask,那么,RectMask2D 优于 Mask
当有两个mask,那么,两者差不多。
当大于两个mask,那么,Mask 优于 RectMask2D。