UGUI源码阅读(三)Graphic渲染

所有显示内容的ui组件都继承自Graphic,ugui是如何使用这些组件来构建mesh从而渲染呢?

首先看CanvasUpdateRegistry组件驱动Graphic构建渲染信息:

private void PerformUpdate()
{
    ...

    m_PerformingGraphicUpdate = true;

    for (var i = (int)CanvasUpdate.PreRender; i < (int)CanvasUpdate.MaxUpdateValue; i++)
    {
        UnityEngine.Profiling.Profiler.BeginSample(m_CanvasUpdateProfilerStrings[i]);
        for (var k = 0; k < m_GraphicRebuildQueue.Count; k++)
        {
            try
            {
                var element = m_GraphicRebuildQueue[k];
                if (ObjectValidForUpdate(element))
                    element.Rebuild((CanvasUpdate)i);   //构建渲染信息
            }
            catch (Exception e)
            {
                Debug.LogException(e, m_GraphicRebuildQueue[k].transform);
            }
        }
        UnityEngine.Profiling.Profiler.EndSample();
    }

    for (int i = 0; i < m_GraphicRebuildQueue.Count; ++i)
        m_GraphicRebuildQueue[i].GraphicUpdateComplete();

    m_GraphicRebuildQueue.Clear();
    m_PerformingGraphicUpdate = false;
}

再看Graphic组件:

public virtual void Rebuild(CanvasUpdate update)
{
    if (canvasRenderer == null || canvasRenderer.cull)
        return;

    switch (update)
    {
        case CanvasUpdate.PreRender:
            if (m_VertsDirty)
            {
                //生成渲染所需的mesh信息
                UpdateGeometry();
                m_VertsDirty = false;
            }
            if (m_MaterialDirty)
            {
                //生成渲染所需的材质球和贴图
                UpdateMaterial();
                m_MaterialDirty = false;
            }
            break;
    }
}

生成渲染所需的mesh:

protected virtual void UpdateGeometry()
{
    if (useLegacyMeshGeneration)
    {
        DoLegacyMeshGeneration();
    }
    else
    {
        DoMeshGeneration();
    }
}

private void DoMeshGeneration()
{
    //将顶点数据写入到s_VertexHelper
    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.

    //修改mesh信息
    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的数据填充到mesh
    s_VertexHelper.FillMesh(workerMesh);
    canvasRenderer.SetMesh(workerMesh);
}

protected virtual void OnPopulateMesh(VertexHelper vh)
{
    var r = GetPixelAdjustedRect();
    //x,y左下角,z,w右上角
    var v = new Vector4(r.x, r.y, r.x + r.width, r.y + r.height);

    Color32 color32 = color;
    vh.Clear();
    //该Transform下的局部顶点位置,颜色,uv坐标
    vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(0f, 0f));
    vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(0f, 1f));
    vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(1f, 1f));
    vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(1f, 0f));

    //三角形面设置
    vh.AddTriangle(0, 1, 2);
    vh.AddTriangle(2, 3, 0);
}

生成渲染所需的材质球和贴图:

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<IMaterialModifier>.Get();
        GetComponents<IMaterialModifier>(components);
        //通过IMaterialModifier接口修改渲染材质球
        var currentMat = material;
        for (var i = 0; i < components.Count; i++)
            currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat);
        ListPool<IMaterialModifier>.Release(components);
        return currentMat;
    }
}

public virtual Material material
{
    get
    {
        return (m_Material != null) ? m_Material : defaultMaterial;
    }
    set
    {
        if (m_Material == value)
            return;

        m_Material = value;
        SetMaterialDirty();
    }
}

Graphic组件默认使用默认材质球渲染一个矩形白色区域,实际显示图片时,使用Image组件,其继承Graphic,并复写相关功能:

public class Image : MaskableGraphic, ISerializationCallbackReceiver, ILayoutElement, ICanvasRaycastFilter
{
    // case 1066689 cache referencePixelsPerUnit when canvas parent is disabled;
    private float m_CachedReferencePixelsPerUnit = 100;
    
    //每单位含有的像素值
    public float pixelsPerUnit
    {
        get
        {
            float spritePixelsPerUnit = 100;
            if (activeSprite)
                spritePixelsPerUnit = activeSprite.pixelsPerUnit;

            if (canvas)
                m_CachedReferencePixelsPerUnit = canvas.referencePixelsPerUnit;
            //自身每单位像素值 / Canvas每单位像素值
            return spritePixelsPerUnit / m_CachedReferencePixelsPerUnit;
        }
    }

    //使Rect的宽高比与Sprite一致
    private void PreserveSpriteAspectRatio(ref Rect rect, Vector2 spriteSize)
    {
        var spriteRatio = spriteSize.x / spriteSize.y;
        var rectRatio = rect.width / rect.height;

        if (spriteRatio > rectRatio)
        {
            var oldHeight = rect.height;
            rect.height = rect.width * (1.0f / spriteRatio);
            rect.y += (oldHeight - rect.height) * rectTransform.pivot.y;
        }
        else
        {
            var oldWidth = rect.width;
            rect.width = rect.height * spriteRatio;
            rect.x += (oldWidth - rect.width) * rectTransform.pivot.x;
        }
    }

    /// Image's dimensions used for drawing. X = left, Y = bottom, Z = right, W = top.
    private Vector4 GetDrawingDimensions(bool shouldPreserveAspect)
    {
        //如果Sprite已被打图集,则边缘可能已从源Sprite剪切
        var padding = activeSprite == null ? Vector4.zero : Sprites.DataUtility.GetPadding(activeSprite);
        var size = activeSprite == null ? Vector2.zero : new Vector2(activeSprite.rect.width, activeSprite.rect.height);

        Rect r = GetPixelAdjustedRect();
        // Debug.Log(string.Format("r:{2}, size:{0}, padding:{1}", size, padding, r));

        int spriteW = Mathf.RoundToInt(size.x);
        int spriteH = Mathf.RoundToInt(size.y);

        //归一化渲染位置
        var v = new Vector4(
            padding.x / spriteW,
            padding.y / spriteH,
            (spriteW - padding.z) / spriteW,
            (spriteH - padding.w) / spriteH);

        if (shouldPreserveAspect && size.sqrMagnitude > 0.0f)
        {
            PreserveSpriteAspectRatio(ref r, size);
        }

        //Rect中顶点的渲染位置
        v = new Vector4(
            r.x + r.width * v.x,
            r.y + r.height * v.y,
            r.x + r.width * v.z,
            r.y + r.height * v.w
        );

        return v;
    }

    /// <summary>
    /// Adjusts the image size to make it pixel-perfect.
    /// </summary>
    /// <remarks>
    /// This means setting the Images RectTransform.sizeDelta to be equal to the Sprite dimensions.
    /// </remarks>
    public override void SetNativeSize()
    {
        if (activeSprite != null)
        {
            float w = activeSprite.rect.width / pixelsPerUnit;
            float h = activeSprite.rect.height / pixelsPerUnit;
            rectTransform.anchorMax = rectTransform.anchorMin;
            rectTransform.sizeDelta = new Vector2(w, h);
            SetAllDirty();
        }
    }


    /// <summary>
    /// Update the UI renderer mesh.
    /// </summary>
    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        if (activeSprite == null)
        {
            base.OnPopulateMesh(toFill);
            return;
        }

        switch (type)
        {
            case Type.Simple:
                if (!useSpriteMesh)
                    GenerateSimpleSprite(toFill, m_PreserveAspect);
                else
                    GenerateSprite(toFill, m_PreserveAspect);
                break;
            case Type.Sliced:
                GenerateSlicedSprite(toFill);
                break;
            case Type.Tiled:
                GenerateTiledSprite(toFill);
                break;
            case Type.Filled:
                GenerateFilledSprite(toFill, m_PreserveAspect);
                break;
        }
    }

    /// <summary>
    /// Generate vertices for a simple Image.
    /// </summary>
    void GenerateSimpleSprite(VertexHelper vh, bool lPreserveAspect)
    {
        //获取顶点渲染位置
        Vector4 v = GetDrawingDimensions(lPreserveAspect);
        var uv = (activeSprite != null) ? Sprites.DataUtility.GetOuterUV(activeSprite) : Vector4.zero;

        var color32 = color;
        vh.Clear();
        vh.AddVert(new Vector3(v.x, v.y), color32, new Vector2(uv.x, uv.y));
        vh.AddVert(new Vector3(v.x, v.w), color32, new Vector2(uv.x, uv.w));
        vh.AddVert(new Vector3(v.z, v.w), color32, new Vector2(uv.z, uv.w));
        vh.AddVert(new Vector3(v.z, v.y), color32, new Vector2(uv.z, uv.y));

        vh.AddTriangle(0, 1, 2);
        vh.AddTriangle(2, 3, 0);
    }
}

后续我们看ugui的遮罩实现

猜你喜欢

转载自blog.csdn.net/qilin598866753/article/details/140286707