原理
Mask
1.Mask会赋予Image一个特殊的材质(GetModiferMaterial),这个材质会给Image的每个像素点进行标记,将标记结果存放在一个模板缓存内(这个缓存叫做 Stencil Buffer)
2. 当子级UI进行渲染的时候会去检查这个 Stencil Buffer内的标记,如果当前覆盖的区域存在标记(即该区域在Image的覆盖范围内),进行渲染,否则不渲染
RectMask2D
1.C#层:找出父物体中所有RectMask2D覆盖区域的交集(FindCullAndClipWorldRect)
2.C#层:所有继承MaskGraphic的子物体组件调用方法设置剪裁区域(SetClipRect)传递给Shader
3.Shader层:接收到矩形区域_ClipRect,片元着色器中判断像素是否在矩形区域内,不在则透明度设置为0(UnityGet2DClipping )
4.Shader层:丢弃掉alpha小于0.001的元素(clip (color.a - 0.001))
Mask源码解析
首先 Mask调用 MaskUtilities.NotifyStencilStateChanged(this);
去通知所有继承IMaskable 接口的组件去计算Mask绘制
就是调用 IMaskable.RecalculateMasking() 方法
StencilMaterial 就是一个动态修改 和自定义Material 的工具类
RectMask2D 源码解析
RectMask2D 实现了IClipper 接口 PerformClipping 函数 可以用来接待剪裁画布回调的一部分更新循环。
子节点的Image 实现了 IClippable 接口 裁剪接口
调用 MaskUtilities.Notify2DMaskStateChanged() 遍历所有子节点
触发OnCullingChanged 最后在画布Rebuild 的时候触发
Canvas.willRenderCanvases() 调用PerformUpdate
PerformUpdate 函数触发Cull
在调用Cull裁剪函数后 触发Mask2D.cs 里的 PerformClipping
设置完shader的裁剪区域 后调用 Cull函数 进行裁剪
总结一下
Mask 优缺点 多DrawCall 但多个Mask可合批
Mask 会多出两个DrawCall 一个是 Mask 本身 会创建一个材质球 把自身Image 的像素标记到模板缓冲区StencilBuff 导致一个DrawCall 另一个是 在Mask 遍历完所有子节点 修改子节点的材质球参数 赋值为对应的裁剪参数 最后 会重置一下缓存StencilBuff 导致又有一个DrawCall
多个Mask 直接 如果达成合批条件(不能重叠) 即 Mask组件本身的图片一样 他就能合批(即Mask首合批) 同理Mask尾部遍历完如果能合批 也会合批
但是 Mask首 无法与另一个Mask尾合批
多个Mask 里的 子节点如果能合批 也会合批
Mask节点可以作为子节点的BottonUI (即响应UI)
Mask2D 优缺点 没有额外DrawCall 但是 多个Mask2D里面的图是无法合批的
Mask2D 不依赖图片组件 本身组件不会产生额外DrawCall
但是他节点下的图片 不能跟节点外的进行合批 是分离的
多个Mask2D 之间也是不能合批的