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 cacheVertexList = new List(); 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)); } }