UGUI源码分析:MaskableGraphic,RectMask2D与Mask的原理
系列
文章目录
MaskableGraphic
BaseClass: Graphic
Interface: IClippable、IMaskable、IMaterialModifier
简介
前置:Graphic源码剖析
MaskableGraphic在 Graphic的基础上实现了裁剪与遮罩功能。
这主要是由 IClippable、IMaskable 两个接口来实现的。
在Graphic更新材质的流程中有提及Mask。Graphic 可以理解成由骨头和皮肤所组成,骨头即顶点信息所构建的网格(Mesh),皮肤则是依附于Mesh的材质和纹理。实际上Mesh是不可见的,对于可见物的处理(例如Mask遮罩剔除)都是针对于Material。
理解清楚IClippable与IMaskable相关的组件原理便是理解MaskableGraphic的关键
RectMask2D 矩形裁剪
BaseClass: UIBehaviour
Interface: IClipper、ICanvasRaycastFilter
Intro: 这是UGUI提供的不依赖于Graphic的裁剪组件,它的原理在于设置IClippable组件中canvasRenderer.EnableRectClipping 来实现矩形裁剪效果
说到 IClipper(裁剪者)与 IClippable(可裁剪对象),那必须以RectMask2D组件为例来讲解
RectMask2D的工作原理:
- RectMask2D是IClipper,当启动时(Enable)先向ClipperRegistry中注册自己,然后会调用其所有子节点下IClippable 组件的RecalculateClipping方法,将其添加进最近父节点中的RectMask2D中(这是为了避免各种嵌套带来的浪费)
// MaskableGraphic 中更新裁剪者的方法 private void UpdateClipParent() { var newParent = (maskable && IsActive()) ? MaskUtilities.GetRectMaskForClippable(this) : null; // if the new parent is different OR is now inactive if (m_ParentMask != null && (newParent != m_ParentMask || !newParent.IsActive())) { m_ParentMask.RemoveClippable(this); UpdateCull(false); } // don't re-add it if the newparent is inactive if (newParent != null && newParent.IsActive()) newParent.AddClippable(this); m_ParentMask = newParent; }
-
当Canvas进行刷新的时候(CanvasUpdateSystem),会调用所有启用中的IClipper,执行Cull 操作,遍历执行 IClipper.PerformClipping
ClipperRegistry.instance.Cull();
m_Clippers[i].PerformClipping();
-
PerformClipping : 目的在于更新IClippable中用于裁剪的Rect
首先会借助MaskUtilities、Clipping 寻找的最小的裁剪框clipRect
接着会遍历自身下所有的IClippable组件(由IClippable.RecalculateClipping 添加)设置clipRect
clipTarget.SetClipRect(clipRect, validRect) validRect:用于判断裁剪框是否可用(长宽>0)
canvasRenderer.EnableRectClipping(clipRect) MaskableGraphic 中设置裁剪框
最后会判断是否改变IClippable中cull的状态
canvasRenderer.cull = cull;
在此流程期间RectMask2D会优化处理过程:
1.记录上次的clipRect来判断裁剪矩形是否发生变化,从而省略没必要的重新裁剪。
m_LastClipRectCanvasSpace = clipRect;
2.裁剪层的子集合会因为父级的裁剪而被裁剪,因此可以传递无效的rect来避免重复的处理。
clipTarget.Cull(maskIsCulled ? Rect.zero : clipRect,maskIsCulled ? false : validRect)
IMaskable基于Material的遮罩
MaskableGraphic 中IMaskable实现:
- 当Enable时,若该物体自身含有Mask组件则会调用其子节点路径下所有IMaskable组件方法。
protected override void OnEnable() { base.OnEnable(); m_ShouldRecalculateStencil = true; //控制是否从新计算遮罩深度->改变遮罩材质 UpdateClipParent(); SetMaterialDirty(); if (GetComponent<Mask>() != null) { // 设置Mask遮罩状态 MaskUtilities.NotifyStencilStateChanged(this); } } //IMaskable 接口方法 public virtual void RecalculateMasking() { m_ShouldRecalculateStencil = true; SetMaterialDirty(); }
- 在Grahpic材质重建的过程中会调用其上所有IMaterialModifier组件方法来处理最终的渲染材质materialForRendering
//MaskableGraphic 中IMaterialModifier 组件方法 public virtual Material GetModifiedMaterial(Material baseMaterial) { var toUse = baseMaterial;//来自Graphic的基础材质 if (m_ShouldRecalculateStencil) { var rootCanvas = MaskUtilities.FindRootSortOverrideCanvas(transform); m_StencilValue = maskable ? MaskUtilities.GetStencilDepth(transform, rootCanvas) : 0; m_ShouldRecalculateStencil = false; } // 优化了遮罩处理,如果已经启用了Mask组件,则不必再次做重复的事情 Mask maskComponent = GetComponent<Mask>(); if (m_StencilValue > 0 && (maskComponent == null || !maskComponent.IsActive())) { //借助StencilMaterial生产一个新的遮罩材质,这里是使用list存储避免重复生成一样的材质 var maskMat = StencilMaterial.Add(toUse, (1 << m_StencilValue) - 1, StencilOp.Keep, CompareFunction.Equal, ColorWriteMask.All, (1 << m_StencilValue) - 1, 0); StencilMaterial.Remove(m_MaskMaterial); m_MaskMaterial = maskMat; toUse = m_MaskMaterial; } return toUse;//返回新生成的遮罩材质 }
.
.
.
.
.
嗨,我是作者Vin129,逐儿时之梦正在游戏制作的技术海洋中漂泊。知道的越多,不知道的也越多。希望我的文章对你有所帮助:)
- 【OVS2.5.0源码分析】vlan&trunk实现原理分析(2)
- Golang源码系列一:Map实现原理分析
- SpringBoot2.x系列:整合SpringMVC之错误处理底层原理及源码分析
- PHPCMS2008源码浅析-模板原理分析 PHPCMS20008二次开发
- 【OpenCV】SIFT原理与源码分析
- springMVC原理(四):SpringMVC视图机制详解[附带源码分析]
- F2FS源码分析-6.2 [其他重要数据结构以及函数] 逻辑地址到物理地址映射的原理以及实现-f2fs_map_blocks函数
- jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理
- SIFT原理与源码分析
- 【Android】从源码分析PagerAdapter/FragmentPagerAdapter调用notifydataSetChanged()刷新的原理
- Spring原理与源码分析系列(四)- Spring IoC源码分析(上)
- HashMap实现原理及源码分析
- Spark源码分析(1) 从WordCount示例看Spark延迟计算原理
- BlockManager原理剖析与源码分析
- 从源码分析java集合类原理(2)-LinkedList原理分析
- Cocos2d-x:整体框架源码分析以及启动过程原理(win32)
- SOFA 源码分析 — 自定义线程池原理
- rockeMq-nameSrv原理源码分析
- android动画之从源码角度分析动画原理
- 【Web容器】Tomcat源码分析(6)-请求原理分析(中)