2025-11-03 10:59:33 +08:00

184 lines
6.5 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
using XLua.Cast;
[RequireComponent(typeof(Text))]
public class UIOutlineEffect : BaseMeshEffect
{
[SerializeField]
private Color outlineColor = Color.black;
public Color OutlineColor
{
get { return outlineColor; }
set { outlineColor = value; }
}
[Range(0, 5)]
public int OutlineWidth = 0;
private List<UIVertex> cacheVertexList = new List<UIVertex>();
protected override void Awake()
{
base.Awake();
if (base.graphic)
{
if (base.graphic.canvas)
{
var shaderChannelOrigin = base.graphic.canvas.additionalShaderChannels;
var needChannel = AdditionalCanvasShaderChannels.TexCoord1;
if ((shaderChannelOrigin & needChannel) != needChannel)
{
base.graphic.canvas.additionalShaderChannels |= needChannel;
}
needChannel = AdditionalCanvasShaderChannels.TexCoord2;
if ((shaderChannelOrigin & needChannel) != needChannel)
{
base.graphic.canvas.additionalShaderChannels |= needChannel;
}
needChannel = AdditionalCanvasShaderChannels.TexCoord3;
if ((shaderChannelOrigin & needChannel) != needChannel)
{
base.graphic.canvas.additionalShaderChannels |= needChannel;
}
needChannel = AdditionalCanvasShaderChannels.Tangent;
if ((shaderChannelOrigin & needChannel) != needChannel)
{
base.graphic.canvas.additionalShaderChannels |= needChannel;
}
needChannel = AdditionalCanvasShaderChannels.Normal;
if ((shaderChannelOrigin & needChannel) != needChannel)
{
base.graphic.canvas.additionalShaderChannels |= needChannel;
}
}
base.graphic.SetVerticesDirty();
}
}
public override void ModifyMesh(VertexHelper vh)
{
vh.GetUIVertexStream(cacheVertexList);
vh.Clear();
ProcessVertexs();
vh.AddUIVertexTriangleStream(cacheVertexList);
}
//扩充顶点 并将计算信息缓存在顶点不需要但可以传递的参数中
private void ProcessVertexs()
{
var total = cacheVertexList.Count;
for (var i = 0; i < total; i += 3)
{
var v1 = cacheVertexList[i];
var v2 = cacheVertexList[i + 1];
var v3 = cacheVertexList[i + 2];
//计算原顶点中心点
var minX = Min(v1.position.x, v2.position.x, v3.position.x);
var maxX = Max(v1.position.x, v2.position.x, v3.position.x);
var minY = Min(v1.position.y, v2.position.y, v3.position.y);
var maxY = Max(v1.position.y, v2.position.y, v3.position.y);
var posCenter = new Vector2(minX + maxX, minY + maxY) * 0.5f;
//uv 顶点三角形最小包围盒 直角三角形特殊处理
Vector2 triX, triY, uvX, uvY;
Vector2 vPos1 = v1.position;
Vector2 vPos2 = v2.position;
Vector2 vPos3 = v3.position;
//点乘计算哪两个顶点为三角形最x最大offset y最大offset
if (Mathf.Abs(Vector2.Dot((vPos2 - vPos1).normalized, Vector2.right)) >
Mathf.Abs(Vector2.Dot((vPos3 - vPos2).normalized, Vector2.right)))
{
triX = vPos2 - vPos1;
triY = vPos3 - vPos2;
uvX = v2.uv0 - v1.uv0;
uvY = v3.uv0 - v2.uv0;
}
else
{
triX = vPos3 - vPos2;
triY = vPos2 - vPos1;
uvX = v3.uv0 - v2.uv0;
uvY = v2.uv0 - v1.uv0;
}
//uv框
var uvOriginMin = Min(v1.uv0, v2.uv0, v3.uv0);
var uvOriginMax = Max(v1.uv0, v2.uv0, v3.uv0);
var color_rg = new Vector2(outlineColor.r, outlineColor.g);
var color_ba = new Vector4(0 ,0 , outlineColor.b, outlineColor.a);
var normal = new Vector3(0, 0, OutlineWidth);
v1 = SetNewPosAndUV(v1, OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOriginMin, uvOriginMax);
v1.uv3 = color_rg;
v1.tangent = color_ba;
v1.normal = normal;
v2 = SetNewPosAndUV(v2, OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOriginMin, uvOriginMax);
v2.uv3 = color_rg;
v2.tangent = color_ba;
v2.normal = normal;
v3 = SetNewPosAndUV(v3, OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOriginMin, uvOriginMax);
v3.uv3 = color_rg;
v3.tangent = color_ba;
v3.normal = normal;
cacheVertexList[i] = v1;
cacheVertexList[i + 1] = v2;
cacheVertexList[i + 2] = v3;
}
}
private UIVertex SetNewPosAndUV(UIVertex vertex, int outlineWidth,
Vector2 vertexsCenter,
Vector2 triangleXMax, Vector2 triangleYMax,
Vector2 uvX, Vector2 uvY,
Vector2 uvOriginMin, Vector2 uvOriginMax)
{
//顶点位置扩充
var pos = vertex.position;
var posXOffset = pos.x > vertexsCenter.x ? outlineWidth : -outlineWidth;
var posYOffset = pos.y > vertexsCenter.y ? outlineWidth : -outlineWidth;
pos.x += posXOffset;
pos.y += posYOffset;
vertex.position = pos;
//uv 最后 * uvx方向纠正
var uv = vertex.uv0;
uv += (Vector4)uvX / triangleXMax.magnitude * posXOffset * (Vector2.Dot(triangleXMax, Vector2.right) > 0 ? 1 : -1);
uv += (Vector4)uvY / triangleYMax.magnitude * posYOffset * (Vector2.Dot(triangleYMax, Vector2.up) > 0 ? 1 : -1);
vertex.uv0 = uv;
vertex.uv1 = uvOriginMin;
vertex.uv2 = uvOriginMax;
return vertex;
}
private static float Min(float a, float b, float c)
{
return Mathf.Min(Mathf.Min(a, b), c);
}
private static float Max(float a, float b, float c)
{
return Mathf.Max(Mathf.Max(a, b), c);
}
private static Vector2 Min(Vector2 a, Vector2 b, Vector2 c)
{
return new Vector2(Min(a.x, b.x, c.x), Min(a.y, b.y, c.y));
}
private static Vector2 Max(Vector2 a, Vector2 b, Vector2 c)
{
return new Vector2(Max(a.x, b.x, c.x), Max(a.y, b.y, c.y));
}
}