Unity 文本颜色描边性能优化方案

  • Post author:
  • Post category:其他


在这首先回答一个问题就是:为什么我们需要实现一个描边的方案?Unity没有吗?

答:

Unity肯定有自己的代替方案,只是性能看起来并没那么优越。Unity 的方案是挂载一个 OutLine 组件。



Outline

outline inherit Shadow:

【Unity】Source Code:

public class Outline : Shadow
  {
    protected Outline()
    {
    }

    public override void ModifyMesh(VertexHelper vh)
    {
      if (!this.IsActive())
        return;
      List<UIVertex> uiVertexList = ListPool<UIVertex>.Get();
      vh.GetUIVertexStream(uiVertexList);
      int num = uiVertexList.Count * 5;
      if (uiVertexList.Capacity < num)
        uiVertexList.Capacity = num;
      int start1 = 0;
      int count1 = uiVertexList.Count;
      this.ApplyShadowZeroAlloc(uiVertexList, (Color32) this.effectColor, start1, uiVertexList.Count, this.effectDistance.x, this.effectDistance.y);
      int start2 = count1;
      int count2 = uiVertexList.Count;
      this.ApplyShadowZeroAlloc(uiVertexList, (Color32) this.effectColor, start2, uiVertexList.Count, this.effectDistance.x, -this.effectDistance.y);
      int start3 = count2;
      int count3 = uiVertexList.Count;
      this.ApplyShadowZeroAlloc(uiVertexList, (Color32) this.effectColor, start3, uiVertexList.Count, -this.effectDistance.x, this.effectDistance.y);
      int start4 = count3;
      int count4 = uiVertexList.Count;
      this.ApplyShadowZeroAlloc(uiVertexList, (Color32) this.effectColor, start4, uiVertexList.Count, -this.effectDistance.x, -this.effectDistance.y);
      vh.Clear();
      vh.AddUIVertexTriangleStream(uiVertexList);
      ListPool<UIVertex>.Release(uiVertexList);
    }
  }

上面的代码是Unity的源代码,我们可以发现,outline 是继承 Shadow 组件,实现的原理就是向四个方向扩张阴影部分。最主要的是


int num = uiVertexList.Count * 5;

这是不是就明了了?假设我只有一个字符时,那么我需要5倍的性能来完成这份工作。

我们看看使用Unity的方案会带来什么效果,我们上图:

DDD

5倍的数据(5 * 6),10个字符就达到了非常恐怖的300数据量。优化时非常有必要的。

下面开始进行优化。前提是先实现一套自己的描边(首先我们要复用左上和右下两个顶点)



优化方案一、网格顶点优化

主要变量:

    [SerializeField] private bool m_EnableOutLine = false;
    [SerializeField] private float m_OutLineWidth = 1;
    ......
    //ModifyMesh(VertexHelper vh)
    private void _ProcessVertices(VertexHelper vh){
    	if (!IsActive())
        {
            return;
        }

        var count = vh.currentVertCount;
        if (count == 0)
            return;

        for (int i = 0; i < count; i++)
        {
            UIVertex vertex = UIVertex.simpleVert;
            vh.PopulateUIVertex(ref vertex, i);
            this.iVertices.Add(vertex);
        }
        vh.Clear();
		for (int i = 0; i < this.iVertices.Count; i += 4)
        {

            UIVertex TL = GeneralUIVertex(this.iVertices[i + 0]);
            UIVertex TR = GeneralUIVertex(this.iVertices[i + 1]);
            UIVertex BR = GeneralUIVertex(this.iVertices[i + 2]);
            UIVertex BL = GeneralUIVertex(this.iVertices[i + 3]);
			
			vh.AddVert(TL);
            vh.AddVert(TR);
			vh.AddVert(BR);
            vh.AddVert(BL);
        }
	        //添加三角面
	    for (int i = 0; i < vh.currentVertCount; i += 4)
        {
            vh.AddTriangle(i + 0, i + 1, i + 2);
            vh.AddTriangle(i + 2, i + 3, i + 0);
        }
    }
        

我们的思路看起来是这样的:

        /*
         *  TL--------TR
         *  |          |^
         *  |          ||
         *  |          ||
         *  |          |v
         *  BL--------BR
         * **/

优化后(顶点下降了10点):

优化

那么,这样的优化就可以了吗?

在用网格的方式来实现的情况下,我认为这应该算是差不多了,不排除有其他的算法。(蚊子再小也是块肉啊。)

那么还有其他方式优化吗?

有!



优化方案二、shader 描边

作者参考了网上许多的方案,觉得shader描边确实可行。参考博客:

CSDN博客链接

该方案确实可行,唯一需要注意的是,它可能会导致你未描边和描边的的对象中断UI合批处理。

最后我们上一下最后Shader的优化后的效果:

在这里插入图片描述

我们可以看到在我们之前的优化基础上shader的描边会更加厚重和清晰,数据量也非常少。这不失为一种办法。

最后我们看下实现逻辑。

代码中有这么一些:

var v1 = graphic.canvas.additionalShaderChannels;
var v2 = AdditionalCanvasShaderChannels.TexCoord1;
if ((v1 & v2) != v2)
{
    base.graphic.canvas.additionalShaderChannels |= v2;
}

v2 = AdditionalCanvasShaderChannels.TexCoord2;
if ((v1 & v2) != v2)
{
    base.graphic.canvas.additionalShaderChannels |= v2;
}

v2 = AdditionalCanvasShaderChannels.TexCoord3;
if ((v1 & v2) != v2)
{
    base.graphic.canvas.additionalShaderChannels |= v2;
}

v2 = AdditionalCanvasShaderChannels.Tangent;
if ((v1 & v2) != v2)
{
    base.graphic.canvas.additionalShaderChannels |= v2;
}

v2 = AdditionalCanvasShaderChannels.Normal;
if ((v1 & v2) != v2)
{
    base.graphic.canvas.additionalShaderChannels |= v2;
}

这里的作用就是把 对应的 UIVertex 里面的对应信息开启,让对应的通道可以通过Shader开启/获取数据,然后通过这样对应的通道,提交一些描边颜色宽度等数据,提交到Shader上面,而shader通过语意来获取如 TEXCOORD0等等。Unity默认好像只开启部分,不一定全开。

shader 接到数据后就开始描边。也不是特别复杂。最后奉献上代码(含有颜色渐变喔):

码云 Unity-TextOutline-and-Color-Gradient



版权声明:本文为kan464872327原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。