using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; /// /// UGUI描边 /// public class OutlineEx : BaseMeshEffect { [Header("新shader描边")] public Color m_OutlineColor= new Color(0, 0, 0, 0.5f);// Color.black; public Color OutlineColor { get { return m_OutlineColor; } set { m_OutlineColor = value; this._Refresh(); } } QualityTextColType m_ColorType = QualityTextColType.None; public QualityTextColType colorType { get { return m_ColorType; } set { if (m_ColorType != value) { m_ColorType = value; OutlineColor = UIHelper.GetUIOutlineColor(value); } } } [Range(0, 30)] //默认10,不同的颜色(包括调整alpha)视觉上描边粗细有差异,故shader里乘以0.2适应不同情况 public int OutlineWidth = 10; private static List m_VetexList = new List(); protected override void Start() { base.Start(); if (base.graphic) { if (base.graphic.material == null || base.graphic.material.shader.name != "TSF Shaders/UI/OutlineEx") { // #if UNITY_EDITOR var texMaterial = ResManager.Instance.LoadAsset("Materials", "OutlineMat"); if (texMaterial != null) { base.graphic.material = texMaterial; } else { Debug.LogError("没有找到材质OutlineMat.mat"); } // #else // var shader = Shader.Find("TSF Shaders/UI/OutlineEx"); // base.graphic.material = new Material(shader); // #endif } if (base.graphic.canvas) { var v1 = base.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; } } this._Refresh(); } } #if UNITY_EDITOR protected override void OnValidate() { base.OnValidate(); if (base.graphic.material != null) { if (base.graphic.material.shader.name != "TSF Shaders/UI/OutlineEx") { var texMaterial = ResManager.Instance.LoadAsset("Materials", "OutlineMat"); if (texMaterial != null) { base.graphic.material = texMaterial; } else { Debug.LogError("没有找到材质OutlineMat.mat"); } //var shader = Shader.Find("TSF Shaders/UI/OutlineEx"); //base.graphic.material = new Material(shader); } this._Refresh(); } } #endif private void _Refresh() { /*if (base.graphic.material.GetInt("_OutlineWidth") != this.OutlineWidth || base.graphic.material.GetColor("_OutlineColor") != this.OutlineColor) { base.graphic.material.SetColor("_OutlineColor", this.OutlineColor); base.graphic.material.SetInt("_OutlineWidth", this.OutlineWidth); }*/ base.graphic.SetVerticesDirty(); } public override void ModifyMesh(VertexHelper vh) { vh.GetUIVertexStream(m_VetexList); this._ProcessVertices(); vh.Clear(); vh.AddUIVertexTriangleStream(m_VetexList); } private void _ProcessVertices() { for (int i = 0, count = m_VetexList.Count - 3; i <= count; i += 3) { var v1 = m_VetexList[i]; var v2 = m_VetexList[i + 1]; var v3 = m_VetexList[i + 2]; // 计算原顶点坐标中心点 // var minX = _Min(v1.position.x, v2.position.x, v3.position.x); var minY = _Min(v1.position.y, v2.position.y, v3.position.y); var maxX = _Max(v1.position.x, v2.position.x, v3.position.x); 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 pos1 = v1.position; Vector2 pos2 = v2.position; Vector2 pos3 = v3.position; if (Mathf.Abs(Vector2.Dot((pos2 - pos1).normalized, Vector2.right)) > Mathf.Abs(Vector2.Dot((pos3 - pos2).normalized, Vector2.right))) { triX = pos2 - pos1; triY = pos3 - pos2; uvX = v2.uv0 - v1.uv0; uvY = v3.uv0 - v2.uv0; } else { triX = pos3 - pos2; triY = pos2 - pos1; uvX = v3.uv0 - v2.uv0; uvY = v2.uv0 - v1.uv0; } // 计算原始UV框 var uvMin = _Min(v1.uv0, v2.uv0, v3.uv0); var uvMax = _Max(v1.uv0, v2.uv0, v3.uv0); //OutlineColor 和 OutlineWidth 也传入,避免出现不同的材质球 var col_rg = new Vector2(OutlineColor.r, OutlineColor.g); //描边颜色 用uv3 和 tangent的 zw传递 var col_ba = new Vector4(0,0,OutlineColor.b, OutlineColor.a); var normal = new Vector3(0, 0, OutlineWidth); //描边的宽度 用normal的z传递 // 为每个顶点设置新的Position和UV,并传入原始UV框 v1 = _SetNewPosAndUV(v1, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax); v1.uv3 = col_rg; v1.tangent = col_ba; v1.normal = normal; v2 = _SetNewPosAndUV(v2, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax); v2.uv3 = col_rg; v2.tangent = col_ba; v2.normal = normal; v3 = _SetNewPosAndUV(v3, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax); v3.uv3 = col_rg; v3.tangent = col_ba; v3.normal = normal; // 应用设置后的UIVertex // m_VetexList[i] = v1; m_VetexList[i + 1] = v2; m_VetexList[i + 2] = v3; } } private static UIVertex _SetNewPosAndUV(UIVertex pVertex, int pOutLineWidth, Vector2 pPosCenter, Vector2 pTriangleX, Vector2 pTriangleY, Vector2 pUVX, Vector2 pUVY, Vector2 pUVOriginMin, Vector2 pUVOriginMax) { // Position var pos = pVertex.position; var posXOffset = pos.x > pPosCenter.x ? pOutLineWidth : -pOutLineWidth; var posYOffset = pos.y > pPosCenter.y ? pOutLineWidth : -pOutLineWidth; pos.x += posXOffset; pos.y += posYOffset; pVertex.position = pos; // UV var uv = pVertex.uv0; uv += new Vector4((pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1)).x, (pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1)).y, 0, 0); uv += new Vector4((pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1)).x, (pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1)).y, 0, 0); pVertex.uv0 = uv; pVertex.uv1 = pUVOriginMin; //uv1 uv2 可用 tangent normal 在缩放情况 会有问题 pVertex.uv2 = pUVOriginMax; return pVertex; } private static float _Min(float pA, float pB, float pC) { return Mathf.Min(Mathf.Min(pA, pB), pC); } private static float _Max(float pA, float pB, float pC) { return Mathf.Max(Mathf.Max(pA, pB), pC); } private static Vector2 _Min(Vector2 pA, Vector2 pB, Vector2 pC) { return new Vector2(_Min(pA.x, pB.x, pC.x), _Min(pA.y, pB.y, pC.y)); } private static Vector2 _Max(Vector2 pA, Vector2 pB, Vector2 pC) { return new Vector2(_Max(pA.x, pB.x, pC.x), _Max(pA.y, pB.y, pC.y)); } }