UGUI Graphic源码分析,UGUI中最重要的部分之一

系列

UGUI源码分析系列总览
web

Graphic

Related Class: Graphic、MaskableGraphic、GraphicRegistry、CanvasUpdateRegistry、VertexHelpercanvas

Related Interface: ICanvasElement、IMeshModifier、IClippable、IMaskable、IMaterialModifiersvg

Intro: 图形组件的基类,基础中的基础组件源码分析

  • ICanvasElement: Canvas元素(重建接口),当Canvas发生更新时重建(void Rebuild)
  • IMeshModifier:网格处理接口
  • IClippable:裁剪相关处理接口
  • IMaskable:遮罩处理接口
  • IMaterialModifier:材质处理接口

Graphic 做为图像组件的基类,主要实现了网格与图像的生成/刷新方法。
在生命周期Enable阶段、Editor模式下的OnValidate中、层级/颜色/材质改变时都会进行相应的刷新(重建)。
重建过程主要经过 CanvasUpdateSystem 最终被Canvas所从新渲染。
详情请见:CanvasUpdateSystem源码剖析
ui

重建主要分为两个部分:顶点重建(UpdateGeometry)与 材质重建(UpdateMaterial).net

更新完成的结果会设置进CanvasRenderer,从而被渲染造成图像。code

在这里插入图片描述

本章着重分析Graphic源码部分,接下来的章节会重点分析Graphic继承链下的MaskableGraphic与衍生出来的组件们。component


GraphicRegistry

管理同Canvas下的全部Graphic对象orm

Dictionary<Canvas, IndexedSet<Graphic>> m_Graphicsxml

Graphic 初始化时(Enable)会寻找其最近根节点的Canvas组件,并以此为key存储在GraphicRegistry中。


Rebuild

public virtual void Rebuild(CanvasUpdate update)
{
    if (canvasRenderer.cull)
        return;
    switch (update)
    {
        case CanvasUpdate.PreRender:
            if (m_VertsDirty)
            {
                UpdateGeometry();//网格更新
                m_VertsDirty = false;
            }
            if (m_MaterialDirty)
            {
                UpdateMaterial();//材质纹理更新
                m_MaterialDirty = false;
            }
            break;
    }
}

UpdateGeometry

Graphic 顶点(网格)更新与生成,发生顶点重建时会被调用。

过程:

  • 更新VertexHelper数据
  • 遍历身上的IMeshModifier组件(MeshEffect组件,实现网格的一些特效,例如Shadow、Outline),更新VertexHelper数据
  • 将最终的顶点数据设置给 workerMesh,并将workerMesh设置进canvasRenderer中,进行渲染。
private void DoMeshGeneration()
{
    if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0)
        OnPopulateMesh(s_VertexHelper);//更新顶点信息
    else
        s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw.

    var components = ListPool<Component>.Get();
    GetComponents(typeof(IMeshModifier), components);

    for (var i = 0; i < components.Count; i++)
        ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);//若由网格特效,则由特效继续更新顶点信息

    ListPool<Component>.Release(components);

    s_VertexHelper.FillMesh(workerMesh);
    canvasRenderer.SetMesh(workerMesh);//设置当canvasRenderer中
}

基础的网格由 4 个顶点 2 个三角面构成

在这里插入图片描述

VertexHelper : 临时存储有关顶点的全部信息,辅助生成网格

- List<Vector3> m_Positions : 顶点位置

- List<Color32> m_Colors :顶点颜色

- List<Vector2> m_Uv0S :第1个顶点UV坐标

- List<Vector2> m_Uv1 :第2个顶点UV坐标

- List<Vector2> m_Uv2S :第3个顶点UV坐标

- List<Vector2> m_Uv3S :第4个顶点UV坐标

- List<Vector3> m_Normals :法线向量

- List<Vector4> m_Tangents : 切线向量

- List<int> m_Indices : 三角面顶点索引

BaseMeshEffect

  • PositionAsUV1: 根据顶点坐标设置UV1坐标(通常为法线贴图,不加此组件时UV1坐标默认是Vector2.zero
  • Shadow:在顶点数基础上增长了一倍的顶点数,并根据偏移(effectDistance)设置新顶点的坐标,实现阴影效果。
  • Outline:继承自Shadow,原理就是分别在四个角(根据effectDistance换算)上实现了四个Shadow,即增长了4倍的顶点数。

在这里插入图片描述


UpdateMaterial

Graphic 材质更新,发生材质重建时会被调用。

过程:

  • 获取自身材质material,遍历身上的IMaterialModifier组件(材质处理组件,实现材质特效,例如Mask),更新 materialForRendering
  • 将最终的材质数据materialForRendering与纹理mainTexture设置进canvasRenderer中,进行渲染。
protected virtual void UpdateMaterial()
{
    if (!IsActive())
        return;
    canvasRenderer.materialCount = 1;
    canvasRenderer.SetMaterial(materialForRendering, 0);
    canvasRenderer.SetTexture(mainTexture);
}

public virtual Material materialForRendering
{
    get
    {
        var components = ListPool<Component>.Get();
        GetComponents(typeof(IMaterialModifier), components);

        var currentMat = material;
        for (var i = 0; i < components.Count; i++)
            currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat);//这里由IMaterialModifier组件对currentMat进行特效化处理,获得最终展现的材质
        ListPool<Component>.Release(components);
        return currentMat;
    }
}