UGUI源码解析——Graphic

本文详细介绍了Unity UGUI系统中的Graphic组件,它是Text、RawImage、Image等组件的基础,负责图像的显示与更新。文章通过源码解析,阐述了Graphic如何处理生命周期事件、响应布局变化以及渲染过程。同时,文章探讨了UGUI的优化策略,包括减少DrawCall和提高性能的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:前言

Graphic是UGUI的核心组件,负责图像的显示与更新,是MaskableGraphic的基类,而MaskableGraphic是Text、RawImage、Image的基类


二:源码解析——类头


Graphic继承自UIBehaviour和ICanvasElement
UIBehaviour是所有UGUI组件的最基类,负责接收来自UnityEngine或者UnityEditor的事件:UGUI源码解析——UIBehaviour
ICanvasElement负责接收Canvas重新渲染的事件:UGUI源码解析——CanvasUpdateRegistry

Graphic添加了三个特性
——DisallowMultipleComponent:不允许一个对象添加多个相同的组件
——RequireComponent:依赖于CanvasRenderer画布渲染组件和RectTransform组件
——ExecuteAlways:编辑器模式下可以运行
另外Graphic是一个抽象类,意味着不能被实例化,但是它提供了很多可重写的方法可被继承并重写


三:源码解析——继承自UIBehaviour的方法

——OnEnable

首先调用CacheCanvas方法找到渲染当前元素的Canvas,之后调用GraphicRegistry.RegisterGraphicForCanvas方法将此元素注册到m_Graphics字典中(每个Canvas对应了一个Graphic序列),接着设置s_WhiteTexture(mainTexture的默认贴图),最后调用SetAllDirty将元素添加到布局和图像重建序列中(这样说有些不严谨,因为布局重建是通过LayoutRebuilder类管理的,如果父物体身上没有继承ILayoutGroup的组件是不会添加到布局重建序列中的),SetAllDirty下面会重点说

——OnDisable

首先从GraphicRegistry和CanvasUpdateRegistry中将当前元素移除注册并清理canvasRenderer,最后通过LayoutRebuilder.MarkLayoutForRebuild方法将元素添加到布局重建序列中(因为Graphic已经Disable所以不需要将元素添加到图像重建序列中)

——OnRectTransformDimensionsChange

将元素添加到图像和布局重建序列中,如果正在进行布局重建则只将元素添加到图像重建序列中

——OnBeforeTransformParentChanged

首先将渲染当前元素的canvas从GraphicRegistry移除注册,并将元素添加到布局重建序列中

——OnTransformParentChanged

首先调用CacheCanvas方法找到渲染当前元素的Canvas,之后调用GraphicRegistry.RegisterGraphicForCanvas方法将此元素注册到m_Graphics字典中(每个Canvas对应了一个Graphic序列),最后使用SetAllDirty将元素添加到图像和布局重建序列中

——OnCanvasHierarchyChanged

首先调用CacheCanvas方法找到渲染当前元素的Canvas,如果此canvas与缓存的canvas不同则移除之前注册的canvas并调用GraphicRegistry.RegisterGraphicForCanvas方法重新将此元素注册到m_Graphics字典中(每个Canvas对应了一个Graphic序列)


四:源码解析——继承自ICanvasElement的方法

——Rebuild

在CanvasUpdateRegistry类中给委托Canvas.willRenderCanvases注册了PerformUpdate方法,PerformUpdate会在CanvasRender渲染之前会遍历布局和图像重建序列调用每个元素的Rebuild方法,在Rebuild方法里调用UpdateGeometry和UpdateMaterial更新网格和材质,布局不在Graphic类中管理而是单独通过LayoutRebuilder去管理的​​​​​

UpdateGeometry方法中首先调用了OnPopulateMesh方法将元素的顶点、颜色、UV等信息暂存到m_VertexHelper中,Graphic类中的OnPopulateMesh绘制了一个矩形,我们可以在自己的UI类中,重写OnPopulateMesh方法,实现自定义的UI
接着遍历身上继承了IMeshModifier的组件去调用ModifyMesh方法(一般是实现网格的一些特效,例如Shadow、Outline),更新m_VertexHelper数据
然后调用s_VertexHelper.FillMesh(workerMesh)将暂存的数据填入workMesh中
最后调用canvasRenderer.SetMesh(workerMesh)将workerMesh赋值给canvasRenderer,这样下一帧canvasRenderer渲染时就会用workerMesh的数据进行绘制了(SetMesh方法最终在C++中实现,这也是UGUI的效率比NGUI高一些的原因,因为NGUI的Mesh合并是在C#中完成)

UI是如何绘制出来的?
首先要生成显示UI用的Mesh,例如一个矩形的Mesh,由4个顶点,2个三角形组成,每个顶点都包含UV坐标、顶点色等信息,调节Image或者Text的颜色,其实就是改变它们的顶点色
然后将网格和纹理信息发送给CanvasRender,CanvasRender再将渲染命令发送给GPU进行渲染,这样一个简单的UI元素就显示出来了,这个过程叫做一次批处理
其实这个流程与渲染一个普通的Cube是类似的
可以简单的理解为,所谓的UI其实就是用一个正交的Camera看着若干的平面网格,只不过单单的显示出来还远远不够,还有一些其他操作,比如DrawCall需要合并,Button需要点击等等


UpdateMaterial方法中首先将自身材质赋值给canvasRenderer(遍历身上继承了IMaterialModifier的组件(例如Mask),然后将贴图赋值给canvasRenderer,这样下一帧canvasRenderer渲染时就会用materialForRendering和mainTexture的数据进行绘制了


五:SetDirty


SetLayoutDirty调用了LayoutRebuilder.MarkLayoutForRebuild方法,此方法内部其实是调用了CanvasUpdateRegistry类中的InternalRegisterCanvasElementForLayoutRebuild方法将元素添加到了布局重建的序列中
SetMaterialDirty和SetVerticesDirty调用了CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild方法将元素添加到了图像重建的序列中


六:UGUI优化技巧

<think>我们正在解析用户的问题:"我想了解image源码中onpoluteMesh的功能请问image源码onpoluteMesh功能解析"根据用户的问题,我们需要关注的是UnityUGUI中的Image组件的OnPopulateMesh方法。首先,我们回顾一下引用[1]的内容:引用[1]:UGUI源码解析——Image。——OnPopulateMesh重写了OnPopulateMesh方法修改元素的顶点、颜色、UV等信息存到m_VertexHelper中,最终会将m_VertexHelper传入Mesh并交给Canvas进行绘制UGUI源码解析——Graphic[^1]。从引用[1]我们可以得知:1.Image组件重写了OnPopulateMesh方法。2.该方法的作用是修改元素的顶点、颜色、UV等信息,并将这些信息存储到m_VertexHelper(一个顶点辅助类)中。3.最终,这些信息会被传入Mesh并交给Canvas进行绘制。因此,OnPopulateMesh的核心功能是构建用于绘制Image的网格(Mesh)数据。接下来,我们详细解析OnPopulateMesh的功能:在UnityUGUI中,Graphic类是所有可视化UI元素的基类。它定义了OnPopulateMesh虚方法,该方法用于生成UI元素的网格(由顶点、三角形、颜色、UV等组成)。Image类继承自Graphic,并重写了OnPopulateMesh方法,以实现Image特有的网格生成逻辑。Image的OnPopulateMesh方法主要完成以下任务:1.根据Image的类型(例如Simple、Sliced、Tiled、Filled等)生成不同的网格。2.计算顶点位置:根据Image的RectTransform的尺寸和锚点,以及Image的类型(如是否有边框、是否填充等),计算出每个顶点的位置。3.设置顶点颜色:每个顶点的颜色通常设置为Image组件的color属性(包括透明度)。4.设置UV:将纹理坐标(UV)设置到每个顶点上,以便将纹理正确地映射到网格上。5.生成三角形:将顶点按顺序连接成三角形,形成网格的面。具体到Image源码中的实现(以Unity源码为例):```csharpprotectedoverridevoidOnPopulateMesh(VertexHelpervh){//根据Image的类型调用不同的生成网格的方法switch(type){caseType.Simple:GenerateSimpleSprite(vh,m_PreserveAspect);break;caseType.Sliced:GenerateSlicedSprite(vh);break;caseType.Tiled:GenerateTiledSprite(vh);break;caseType.Filled:GenerateFilledSprite(vh,m_PreserveAspect);break;}}```对于不同类型的Image:-Simple:生成一个简单的矩形网格,由两个三角形组成(四个顶点)。-Sliced:当Image使用九宫格切片(Sliced)时,会生成9个矩形网格(每个切片区域一个矩形),用于实现拉伸时保持边框不变形。-Tiled:平铺模式,根据纹理的大小平铺重复,生成多个矩形网格。-Filled:填充模式,根据填充比例生成一个部分显示的网格(例如圆形填充、水平填充等)。在生成网格的过程中,会使用到Sprite的边框信息(用于Sliced类型)、填充比例(用于Filled类型)等。最后,生成的网格数据(通过VertexHelper存储)会传递给Canvas渲染系统,由CanvasRenderer负责实际的渲染。总结:OnPopulateMesh的主要功能是生成Image的网格数据,包括顶点位置、顶点颜色、UV和三角形索引,这些数据用于渲染Image。根据Image的类型(Simple,Sliced,Tiled,Filled),网格的生成方式不同。§§相关问题§§1.OnPopulateMesh在UGUI渲染流程中的调用时机是什么?2.如何自定义一个继承自Graphic的组件并重写OnPopulateMesh?3.Image的填充模式(Filled)是如何在OnPopulateMesh中实现的?</think>### Image源码中OnPopulateMesh功能解析Unity UGUI中,`OnPopulateMesh`是`Graphic`类的核心方法,负责生成UI元素的渲染网格数据。对于`Image`组件,其功能实现如下: #### 核心功能 1. **网格数据生成** 重写`OnPopulateMesh`方法,根据Image类型(Simple/Sliced/Tiled/Filled)计算顶点位置、UV坐标和三角形索引,存入`VertexHelper`对象: ```csharp protected override void OnPopulateMesh(VertexHelper vh) { switch (type) { case Type.Simple: GenerateSimpleSprite(vh, m_PreserveAspect); break; case Type.Sliced: GenerateSlicedSprite(vh); break; //...其他类型处理 } } ``` 2. **顶点信息构建** - **顶点位置**:根据RectTransform尺寸计算4个角点坐标 - **UV映射**:将纹理坐标映射到顶点(受Image.fillOrigin影响) - **顶点颜色**:应用Image.color属性(含透明度) 示例伪代码: ```csharp vh.AddVert(new Vector3(x, y), color, new Vector2(uv.x, uv.y)); ``` 3. **三角形索引生成** 将4个顶点连接成2个三角形(6个索引),形成可渲染的四边形: ```csharp vh.AddTriangle(0, 1, 2); // 第一个三角形 vh.AddTriangle(2, 3, 0); // 第二个三角形 ``` #### 特殊类型处理 1. **Sliced模式** 使用九宫格切片算法,生成9个独立网格区域,确保边框拉伸不变形[^1]。 2. **Filled模式** 根据填充比例(fillAmount)和填充方向(fillMethod)动态裁剪网格: ```csharp if (fillMethod == FillMethod.Radial) { // 计算弧度裁剪顶点 CutRadial(vh, fillAmount); } ``` 3. **Tiled模式** 平铺纹理时重复生成网格单元,优化大尺寸平铺性能。 #### 渲染流程中的作用 最终`VertexHelper`数据会传递给Canvas渲染系统: ```mermaid graph LR A[OnPopulateMesh] --> B[填充VertexHelper] B --> C[生成Mesh] C --> D[CanvasRenderer提交] D --> E[GPU渲染] ``` 该方法在以下时机自动调用: 1. UI尺寸变化时 2. 纹理/颜色属性修改时 3. 父级Canvas重建时 [^1]:UGUI源码解析——Image。 ——OnPopulateMesh 重写了OnPopulateMesh方法修改元素的顶点、颜色、UV等信息存到m_VertexHelper中,最终会将m_VertexHelper传入Mesh并交给Canvas进行绘制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello Bug.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值