热门标签 | 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();}
}


推荐阅读
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 使用Ubuntu中的Python获取浏览器历史记录原文: ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
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社区 版权所有