字体颜色渐变
我们先看看二颜色渐变效果:
我们看看三颜色渐变效果:
左面Scene场景是网格状态。
通过Scene场景我们可以看见,我们需要向中间插入两个点,做个顶点过渡。
顶点部署应该是这样的:
* TL--------TR
* | |
* | |
* CL--------CR
* | |
* | |
* BL--------BR
首先我们要知道,显示文本有个文本区域,而这个区域的坐标是基于局部坐标,所以我们要先转出到屏幕坐标,然后再换算。
准备的变量和枚举:
public enum GradientType
{
TowColor = 0,
ThreeColor,
}
[SerializeField] private GradientType m_GradientType = GradientType.TowColor;
[SerializeField] private Color32 m_TopColor = Color.white;
[SerializeField] private Color32 m_MiddleColor = Color.white;
[SerializeField] private Color32 m_BottomColor = Color.black;
[Range(0f, 1f)] [SerializeField] private float m_ColorOffset = 0.5f;
[SerializeField] private Camera m_UICamera;
private List iVertices = new List();
核心换算代码:
vh.GetUIVertexStream(iVertices);
vh.Clear();
for (int i = 0; i < iVertices.Count; i += 6)
{
UIVertex TL = iVertices[i + 0];
UIVertex TR = iVertices[i + 1];
UIVertex CR = iVertices[i + 2];
UIVertex CL = iVertices[i + 4];
UIVertex BL = iVertices[i + 4];
UIVertex BR = iVertices[i + 2];
Vector2 uvTL = iVertices[i + 0].uv0;
Vector2 uvTR = iVertices[i + 1].uv0;
Vector2 uvBR = iVertices[i + 2].uv0;
Vector2 uvBL = iVertices[i + 4].uv0;
Vector3 TR_World_Pos = this.transform.TransformPoint(TR.position);
Vector3 BR_World_Pos = this.transform.TransformPoint(BR.position);
Vector3 TR_Screen_Pos = TR_World_Pos;
Vector3 BR_Screen_Pos = BR_World_Pos;
if (this.m_UICamera != null)
{
TR_Screen_Pos = this.m_UICamera.WorldToScreenPoint(TR_World_Pos);
BR_Screen_Pos = this.m_UICamera.WorldToScreenPoint(BR_World_Pos);
}
float yHeight = (TR_Screen_Pos.y - BR_Screen_Pos.y) * this.m_ColorOffset;
Vector2 C_Screen_Pos = new Vector2(TR_Screen_Pos.x, TR_Screen_Pos.y - yHeight);
if (this.m_UICamera != null)
{
C_Screen_Pos = this.m_UICamera.ScreenToWorldPoint(C_Screen_Pos);
}
CR.position = this.transform.InverseTransformPoint(C_Screen_Pos);
CL.position.y = CR.position.y;
CR.uv0.y = CR.uv0.y * this.m_ColorOffset;
CL.uv0.y = CL.uv0.y * this.m_ColorOffset;
TL.color = this.m_TopColor;
TR.color = this.m_TopColor;
CR.color = this.m_MiddleColor;
CL.color = this.m_MiddleColor;
BL.color = this.m_BottomColor;
BR.color = this.m_BottomColor;
vh.AddVert(TL);
vh.AddVert(TR);
vh.AddVert(CR);
vh.AddVert(CL);
vh.AddVert(BL);
vh.AddVert(BR);
}
上面的代码意思就是先清理原始数据,然后把坐标转到屏幕坐标系上,需要经过两步 局部坐标 => 世界坐标 => 屏幕坐标,然后给y做某个点上距离缩放:
float yHeight = (TR_Screen_Pos.y - BR_Screen_Pos.y) * this.m_ColorOffset;
在这个距离做个缩放,TR_Screen_Pos.y - yHeight
计算出来差值,就是我们需要的中点高度。(CR\CL的高度是一样的,计算一次即可,X值保留默认。)
接下来添加三角面:
for (int i = 0; i < vh.currentVertCount; i += 6)
{
if (this.m_GradientType == GradientType.ThreeColor)
{
vh.AddTriangle(i + 0, i + 1, i + 2);
vh.AddTriangle(i + 2, i + 3, i + 0);
vh.AddTriangle(i + 3, i + 2, i + 5);
vh.AddTriangle(i + 5, i + 4, i + 3);
}
else
{
vh.AddTriangle(i + 0, i + 1, i + 5);
vh.AddTriangle(i + 0, i + 5, i + 4);
}
}
大致就是这些。
补充,上面版本有些bug,核心算法做个优化:
/*
* TL--------TR
* | |
* | |
* CL--------CR
* | |
* | |
* BL--------BR
* **/
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 = this.GeneralUIVertex(this.iVertices[i + 0]);
UIVertex TR = this.GeneralUIVertex(this.iVertices[i + 1]);
UIVertex BR = this.GeneralUIVertex(this.iVertices[i + 2]);
UIVertex BL = this.GeneralUIVertex(this.iVertices[i + 3]);
TL.color = this.m_TopColor;
TR.color = this.m_TopColor;
BL.color = this.m_BottomColor;
BR.color = this.m_BottomColor;
vh.AddVert(TL);
vh.AddVert(TR);
if (this.m_GradientType == GradientType.ThreeColor)
{
UIVertex CR = this.GeneralUIVertex(this.iVertices[i + 2]);
UIVertex CL = this.GeneralUIVertex(this.iVertices[i + 3]);
CR.position = (TR.position + BR.position) * this.m_ColorOffset;
CL.position = (TL.position + BL.position) * this.m_ColorOffset;
CR.uv0 = (TR.uv0 + BR.uv0) * this.m_ColorOffset;
CL.uv0 = (TL.uv0 + BL.uv0) * this.m_ColorOffset;
CR.color = this.m_MiddleColor;
CL.color = this.m_MiddleColor;
vh.AddVert(CR);
vh.AddVert(CL);
}
vh.AddVert(BR);
vh.AddVert(BL);
}
int step = 4;
if (this.m_GradientType == GradientType.ThreeColor)
{
step = 6;
}
for (int i = 0; i < vh.currentVertCount; i += step)
{
if (this.m_GradientType == GradientType.ThreeColor)
{
vh.AddTriangle(i + 0, i + 1, i + 2);
vh.AddTriangle(i + 2, i + 3, i + 0);
vh.AddTriangle(i + 3, i + 2, i + 4);
vh.AddTriangle(i + 4, i + 5, i + 3);
}
else
{
vh.AddTriangle(i + 0, i + 1, i + 2);
vh.AddTriangle(i + 2, i + 3, i + 0);
}
}
private UIVertex GeneralUIVertex(UIVertex vertex)
{
UIVertex result = UIVertex.simpleVert;
result.normal = new Vector3(vertex.normal.x, vertex.normal.y, vertex.normal.z);
result.position = new Vector3(vertex.position.x, vertex.position.y, vertex.position.z);
result.tangent = new Vector4(vertex.tangent.x, vertex.tangent.y, vertex.tangent.z, vertex.tangent.w);
result.uv0 = new Vector2(vertex.uv0.x, vertex.uv0.y);
result.uv1 = new Vector2(vertex.uv1.x, vertex.uv1.y);
return result;
}
最终代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
using System;
public enum GradientType
{
TowColor = 0,
ThreeColor,
}
///
/// 渐变字体
///
public class Gradient : BaseMeshEffect
{
[SerializeField] private GradientType m_GradientType = GradientType.TowColor;
[SerializeField] private Color32 m_TopColor = Color.white;
[SerializeField] private Color32 m_MiddleColor = Color.white;
[SerializeField] private Color32 m_BottomColor = Color.black;
[Range(0f, 1f)] [SerializeField] private float m_ColorOffset = 0.5f;
[SerializeField] private Camera m_UICamera;
private List iVertices = new List();
public override void ModifyMesh(VertexHelper vh)
{
this.iVertices.Clear();
if (!IsActive())
{
return;
}
var count = vh.currentVertCount;
if (count == 0)
return;
/*
* TL--------TR
* | |
* | |
* CL--------CR
* | |
* | |
* BL--------BR
* **/
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 = this.GeneralUIVertex(this.iVertices[i + 0]);
UIVertex TR = this.GeneralUIVertex(this.iVertices[i + 1]);
UIVertex BR = this.GeneralUIVertex(this.iVertices[i + 2]);
UIVertex BL = this.GeneralUIVertex(this.iVertices[i + 3]);
TL.color = this.m_TopColor;
TR.color = this.m_TopColor;
BL.color = this.m_BottomColor;
BR.color = this.m_BottomColor;
vh.AddVert(TL);
vh.AddVert(TR);
if (this.m_GradientType == GradientType.ThreeColor)
{
UIVertex CR = this.GeneralUIVertex(this.iVertices[i + 2]);
UIVertex CL = this.GeneralUIVertex(this.iVertices[i + 3]);
CR.position = (TR.position + BR.position) * this.m_ColorOffset;
CL.position = (TL.position + BL.position) * this.m_ColorOffset;
CR.uv0 = (TR.uv0 + BR.uv0) * this.m_ColorOffset;
CL.uv0 = (TL.uv0 + BL.uv0) * this.m_ColorOffset;
CR.color = this.m_MiddleColor;
CL.color = this.m_MiddleColor;
vh.AddVert(CR);
vh.AddVert(CL);
}
vh.AddVert(BR);
vh.AddVert(BL);
}
int step = 4;
if (this.m_GradientType == GradientType.ThreeColor)
{
step = 6;
}
for (int i = 0; i < vh.currentVertCount; i += step)
{
if (this.m_GradientType == GradientType.ThreeColor)
{
vh.AddTriangle(i + 0, i + 1, i + 2);
vh.AddTriangle(i + 2, i + 3, i + 0);
vh.AddTriangle(i + 3, i + 2, i + 4);
vh.AddTriangle(i + 4, i + 5, i + 3);
}
else
{
vh.AddTriangle(i + 0, i + 1, i + 2);
vh.AddTriangle(i + 2, i + 3, i + 0);
}
}
}
private UIVertex GeneralUIVertex(UIVertex vertex)
{
UIVertex result = UIVertex.simpleVert;
result.normal = new Vector3(vertex.normal.x, vertex.normal.y, vertex.normal.z);
result.position = new Vector3(vertex.position.x, vertex.position.y, vertex.position.z);
result.tangent = new Vector4(vertex.tangent.x, vertex.tangent.y, vertex.tangent.z, vertex.tangent.w);
result.uv0 = new Vector2(vertex.uv0.x, vertex.uv0.y);
result.uv1 = new Vector2(vertex.uv1.x, vertex.uv1.y);
return result;
}
}