热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

UGUI源码之Mask、IMaterialModifier、IMaskable

原图放在我的Processon:https:www.processon.comviewlink60d71f12637689326ce58b20一、Mask的实质1

原图放在我的 Processon:https://www.processon.com/view/link/60d71f12637689326ce58b20


一、Mask 的实质

1、Mask 是通过模板测试进行遮罩的。

Unity模板测试简略文档。

需要知道:模板测试发生在光栅化阶段/逐片元操作时。如图:

2、Mask 通过创建遮罩材质使UI系统完成模板测试。

⑴、Mask 组件在 OnEnable() 时将同级关联的 CanvasRenderer 的 hasPopInstruction 字段置为 true。使这个节点的所有子节点绘制完之后多一次绘制,用于遮罩。

⑵、Mask 组件会在 GetModifiedMaterial 方法(实现自 IMaterialModifier)中为同级关联的 CanvasRenderer 生成一个 unmaskMaterial。并设置为 popMaterial,用于遮罩。

⑶、Mask 组件会在 GetModifiedMaterial 方法(实现自 IMaterialModifier)中为同级的 Graphic 组件生成一个 maskMaterial。(最终赋给 CanvasRenderer)(见 Graphic类中的  canvasRenderer.SetMaterial(materialForRendering, 0);)

⑷、Mask 的子 MaskableGraphic 会在GetModifiedMaterial 方法(实现自 IMaterialModifier)中为自身生成一个maskMaterial。(最终赋给 CanvasRenderer)(见 Graphic类中的  canvasRenderer.SetMaterial(materialForRendering, 0);)

⑸、从 StencilMaterial 类中可以看到,生成的材质都是基于 UI-Default.shader 创建的,然后修改了其模板测试相关的参数。可以继续查看 UI-Default.shader 了解详情。(Unity 官网下载 Built-in-Shaders:https://unity.cn/releases/lts。)

⑹、为使多层 Mask 可以嵌套工作(和 RectMask2D 一样取交集),在生成这些材质时会根据自身深度(自身所在的、嵌套Mask下的位置)提供不同的模板测试参数。

例如一个三层嵌套的例子。

(具体进行模板测试时的参数比较、操作待研究。。)

原图在我的 processon: https://www.processon.com/view/link/60db2583637689326ced8271

3、从性能上简单比较 Mask 和 RectMask2D。

⑴、Mask 的模板测试发生在光栅化阶段/逐片元操作时,而 RectMask2D 的粗裁剪发生在应用阶段/设置渲染状态时。可以看出 RectMask2D 的粗裁剪执行得很早,可以大幅略过被裁剪部分的渲染过程。

⑵、Mask 会将 CanvasRenderer的 hasPopInstruction 设为 true,这会增加一次 drawcall。

⑶、Mask 基于 UI-Default.shader 为自身及子Graphic 创建了新材质,所以会打断与其他UI的合批。


二、全注释: 

---------------------- NRatel 割 -------------------------------

更多 UGUI 注释已放入  https://github.com/NRatel/uGUI。

---------------------- NRatel 割 -------------------------------

1、IMaterialModifier

namespace UnityEngine.UI
{// Interface which allows for the modification of the Material used to render a Graphic before they are passed to the CanvasRenderer.// When a Graphic sets a material is is passed (in order) to any components on the GameObject that implement IMaterialModifier.// This component can modify the material to be used for rendering.// 这个接口允许渲染一个图形的材质在传递到 CanvasRenderer 之前被修改。public interface IMaterialModifier{// Perform material modification in this function.// 在此方法中执行材质的修改。// "baseMaterial":The material that is to be modified. //将被修改的材质// 返回值:The modified material. //被修改后的材质Material GetModifiedMaterial(Material baseMaterial);}
}

2、Mask

using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering;
using UnityEngine.Serialization;namespace UnityEngine.UI
{[AddComponentMenu("UI/Mask", 13)][ExecuteAlways][RequireComponent(typeof(RectTransform))][DisallowMultipleComponent]// A component for masking children elements.// By using this element any children elements that have masking enabled will mask where a sibling Graphic would write 0 to the stencil buffer.// 用于遮罩子元素的组件。// 通过使用这个元素&#xff0c;任何启用了 Mask&#xff08;MaskableGraphic 的 maskable&#xff09; 的子元素都会被遮罩。与本组件同级关联的 Graphic 将在模板测试缓冲区上写入0。public class Mask : UIBehaviour, ICanvasRaycastFilter, IMaterialModifier{[NonSerialized]private RectTransform m_RectTransform; //与本组件关联的 RectTransform。public RectTransform rectTransform{get { return m_RectTransform ?? (m_RectTransform &#61; GetComponent()); }}[SerializeField]private bool m_ShowMaskGraphic &#61; true;// Show the graphic that is associated with the Mask render area.// 是否显示与 Mask 关联的图形的渲染区域。// 设置时&#xff0c;若关联的 Graphic 不为 null&#xff0c;则标记 Graphic 的材质脏标记 为脏。public bool showMaskGraphic{get { return m_ShowMaskGraphic; }set{if (m_ShowMaskGraphic &#61;&#61; value)return;m_ShowMaskGraphic &#61; value;if (graphic !&#61; null)graphic.SetMaterialDirty();}}[NonSerialized]private Graphic m_Graphic;// The graphic associated with the Mask.// 与 Mask 关联的 graphic。public Graphic graphic{get { return m_Graphic ?? (m_Graphic &#61; GetComponent()); }}[NonSerialized]private Material m_MaskMaterial; //Mask材质[NonSerialized]private Material m_UnmaskMaterial;protected Mask(){}//Mask是否启用&#xff08;生效&#xff09;&#xff1a;激活且关联的 Graphic 不为 null。public virtual bool MaskEnabled() { return IsActive() && graphic !&#61; null; }[Obsolete("Not used anymore.")]public virtual void OnSiblingGraphicEnabledDisabled() {}// 1、调用父类 OnEnable。// 2、若关联的 Graphic 不为null// ⑴、启用与 Graphic 关联的 CanvasRenderer 组件的 hasPopInstruction。// ⑵、标记 Graphic 的材质脏标记 为脏。// 3、通知 StencilStateChanged。&#xff08;通知所有实现 IMaskable 接口的子物体重新计算遮罩。protected override void OnEnable(){base.OnEnable();if (graphic !&#61; null){// hasPopInstruction&#xff1a;// Enable“render stack”pop draw call。// 当使用 hierarchy 渲染时&#xff0c;canvasRenderer 可以插入一个"pop"指令。// 这个"pop"指令将在所有子元素被渲染后执行。// CanvasRenderer 组件将使用配置的 pop 材质渲染。graphic.canvasRenderer.hasPopInstruction &#61; true; graphic.SetMaterialDirty();}MaskUtilities.NotifyStencilStateChanged(this);}// 1、调用父类 OnDisable。// 2、若关联的 Graphic 不为null// ⑴、标记 Graphic 的材质脏标记 为脏。// ⑵、关闭与 Graphic 关联的 CanvasRenderer 组件的 hasPopInstruction。// ⑶、设置与 Graphic 关联的 CanvasRenderer 组件的 popMaterialCount 为 0。// 3、将 m_MaskMaterial 从 StencilMaterial 中移除&#xff0c;并设置 m_MaskMaterial 为 null。// 4、将 m_UnmaskMaterial 从 StencilMaterial 中移除&#xff0c;并设置 m_UnmaskMaterial 为 null。// 5、通知 StencilStateChanged。&#xff08;通知所有实现 IMaskable 接口的子物体重新计算遮罩。protected override void OnDisable(){// we call base OnDisable first here as we need to have the IsActive return the correct value when we notify the children that the mask state has changed.// 我们首先在这里调用 base.OnDisable&#xff0c;因为我们需要 在通知子物体Mask状态改变时&#xff0c;让 IsActive 返回正确的值。// 疑问 ??? 未理解&#xff0c;为什么调 base.OnDisable 会影响到 IsActive。// 实际测试&#xff0c;某 UIBehaviour 的子类的 OnDisable 中&#xff0c;调用 base.OnDisable 前后&#xff0c;其 activeInHierarchy 和 activeSelf 均为 false。base.OnDisable();if (graphic !&#61; null){graphic.SetMaterialDirty();graphic.canvasRenderer.hasPopInstruction &#61; false;graphic.canvasRenderer.popMaterialCount &#61; 0; // popMaterialCount&#xff1a;CanvasRenderer组件可用的材质数量&#xff0c;用于内部遮罩。}StencilMaterial.Remove(m_MaskMaterial);m_MaskMaterial &#61; null;StencilMaterial.Remove(m_UnmaskMaterial);m_UnmaskMaterial &#61; null;MaskUtilities.NotifyStencilStateChanged(this);}#if UNITY_EDITORprotected override void OnValidate(){base.OnValidate();if (!IsActive())return;if (graphic !&#61; null)graphic.SetMaterialDirty();MaskUtilities.NotifyStencilStateChanged(this);}#endif// 实现 ICanvasRaycastFilter 的接口// 射线投射位置是否有效public virtual bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera){if (!isActiveAndEnabled) //若未激活或未启用&#xff0c;则有效&#xff08;不过滤&#xff09;return true;// 若激活且启用&#xff0c;则检查投射点是否在本 rectTransform 的矩形内。 在则有效。return RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera);}// Stencil calculation time!// 实际的模板测试在这里进行&#xff01;// 实现 IMaterialModifier 的接口。// 1、检查 Mask 是否启用&#xff0c;若未启用&#xff0c;直接返回 baseMaterial。public virtual Material GetModifiedMaterial(Material baseMaterial){if (!MaskEnabled())return baseMaterial;var rootSortCanvas &#61; MaskUtilities.FindRootSortOverrideCanvas(transform); // 获取最深根的 Canvas&#xff0c;或第一个“使用独立绘制顺序”的 Canvas。var stencilDepth &#61; MaskUtilities.GetStencilDepth(transform, rootSortCanvas); // 计算模板测试深度。if (stencilDepth >&#61; 8){Debug.LogWarning("Attempting to use a stencil mask with depth > 8", gameObject);return baseMaterial; //如果深度>&#61;8&#xff0c;抛出警告&#xff0c;直接返回 baseMaterial。}int desiredStencilBit &#61; 1 <}

3、IMaskable

在 Mask 状态改变时&#xff0c;MaskUtilities 利用这个接口配合对遮罩情况进行更新。

using System;namespace UnityEngine.UI
{// This element is capable of being masked out.// 这个元素可以被遮罩。&#xff08;目前只有MaskableGraphic实现它&#xff09;public interface IMaskable{// Recalculate masking for this element and all children elements.// Use this to update the internal state (recreate materials etc).// 重新计算此元素和所有子元素的遮罩。// 更新内部状态(重新创建材质等)。void RecalculateMasking();}
}


推荐阅读
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Inno Setup区段之Components篇相关知识详解
    本文详细介绍了Inno Setup区段之Components篇相关的知识,包括Components和Types的使用方式以及各个参数的说明,希望对读者有一定的参考价值。内容涵盖了ComponentsName、Description、Types、ExtraDiskSpaceRequired、ExtraDiskSpaceRequiredFlags等多个关键词,帮助读者更好地理解和应用Inno Setup区段之Components篇的知识。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
author-avatar
蓝向芸_443
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有