OutlineEx.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. //————————————————————————————————————————————
  2. // OutlineEx.cs
  3. //
  4. // Created by Chiyu Ren on 2018/9/12 23:03:51
  5. // Modify by zhenmu on 2019/3/26
  6. //————————————————————————————————————————————
  7. using UnityEngine;
  8. using UnityEngine.UI;
  9. using System.Collections.Generic;
  10. namespace UnityEngine.UI {
  11. /// <summary>
  12. /// UGUI描边
  13. /// </summary>
  14. public class OutlineEx : BaseMeshEffect {
  15. public Color OutlineColor = Color.white;
  16. [Range(0, 6)]
  17. public float OutlineWidth = 0;
  18. private static List<UIVertex> m_VetexList = new List<UIVertex>();
  19. protected override void Awake() {
  20. base.Awake();
  21. if (base.graphic) {
  22. if (base.graphic.material == null || base.graphic.material.shader.name != "TSF Shaders/UI/OutlineEx") {
  23. var shader = Shader.Find("TSF Shaders/UI/OutlineEx");
  24. base.graphic.material = new Material(shader);
  25. }
  26. if (base.graphic.canvas) {
  27. var v1 = base.graphic.canvas.additionalShaderChannels;
  28. var v2 = AdditionalCanvasShaderChannels.TexCoord1;
  29. if ((v1 & v2) != v2) {
  30. base.graphic.canvas.additionalShaderChannels |= v2;
  31. }
  32. v2 = AdditionalCanvasShaderChannels.TexCoord2;
  33. if ((v1 & v2) != v2) {
  34. base.graphic.canvas.additionalShaderChannels |= v2;
  35. }
  36. v2 = AdditionalCanvasShaderChannels.TexCoord3;
  37. if ((v1 & v2) != v2) {
  38. base.graphic.canvas.additionalShaderChannels |= v2;
  39. }
  40. v2 = AdditionalCanvasShaderChannels.Tangent;
  41. if ((v1 & v2) != v2) {
  42. base.graphic.canvas.additionalShaderChannels |= v2;
  43. }
  44. v2 = AdditionalCanvasShaderChannels.Normal;
  45. if ((v1 & v2) != v2) {
  46. base.graphic.canvas.additionalShaderChannels |= v2;
  47. }
  48. }
  49. this._Refresh();
  50. }
  51. }
  52. private void _Refresh() {
  53. /*if (base.graphic.material.GetInt("_OutlineWidth") != this.OutlineWidth || base.graphic.material.GetColor("_OutlineColor") != this.OutlineColor)
  54. {
  55. base.graphic.material.SetColor("_OutlineColor", this.OutlineColor);
  56. base.graphic.material.SetInt("_OutlineWidth", this.OutlineWidth);
  57. }*/
  58. base.graphic.SetVerticesDirty();
  59. }
  60. public override void ModifyMesh(VertexHelper vh) {
  61. vh.GetUIVertexStream(m_VetexList);
  62. this._ProcessVertices();
  63. vh.Clear();
  64. vh.AddUIVertexTriangleStream(m_VetexList);
  65. }
  66. private void _ProcessVertices() {
  67. for (int i = 0, count = m_VetexList.Count - 3; i <= count; i += 3) {
  68. var v1 = m_VetexList[i];
  69. var v2 = m_VetexList[i + 1];
  70. var v3 = m_VetexList[i + 2];
  71. // 计算原顶点坐标中心点
  72. //
  73. var minX = _Min(v1.position.x, v2.position.x, v3.position.x);
  74. var minY = _Min(v1.position.y, v2.position.y, v3.position.y);
  75. var maxX = _Max(v1.position.x, v2.position.x, v3.position.x);
  76. var maxY = _Max(v1.position.y, v2.position.y, v3.position.y);
  77. var posCenter = new Vector2(minX + maxX, minY + maxY) * 0.5f;
  78. // 计算原始顶点坐标和UV的方向
  79. //
  80. Vector2 triX, triY, uvX, uvY;
  81. Vector2 pos1 = v1.position;
  82. Vector2 pos2 = v2.position;
  83. Vector2 pos3 = v3.position;
  84. if (Mathf.Abs(Vector2.Dot((pos2 - pos1).normalized, Vector2.right)) >
  85. Mathf.Abs(Vector2.Dot((pos3 - pos2).normalized, Vector2.right))) {
  86. triX = pos2 - pos1;
  87. triY = pos3 - pos2;
  88. uvX = v2.uv0 - v1.uv0;
  89. uvY = v3.uv0 - v2.uv0;
  90. }
  91. else {
  92. triX = pos3 - pos2;
  93. triY = pos2 - pos1;
  94. uvX = v3.uv0 - v2.uv0;
  95. uvY = v2.uv0 - v1.uv0;
  96. }
  97. // 计算原始UV框
  98. var uvMin = _Min(v1.uv0, v2.uv0, v3.uv0);
  99. var uvMax = _Max(v1.uv0, v2.uv0, v3.uv0);
  100. //OutlineColor 和 OutlineWidth 也传入,避免出现不同的材质球
  101. var col_rg = new Vector2(OutlineColor.r, OutlineColor.g); //描边颜色 用uv3 和 tangent的 zw传递
  102. var col_ba = new Vector4(OutlineWidth, 0, OutlineColor.b, OutlineColor.a);
  103. // 为每个顶点设置新的Position和UV,并传入原始UV框
  104. v1 = _SetNewPosAndUV(v1, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
  105. v1.uv3 = col_rg;
  106. v1.tangent = col_ba;
  107. v2 = _SetNewPosAndUV(v2, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
  108. v2.uv3 = col_rg;
  109. v2.tangent = col_ba;
  110. v3 = _SetNewPosAndUV(v3, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);
  111. v3.uv3 = col_rg;
  112. v3.tangent = col_ba;
  113. // 应用设置后的UIVertex
  114. //
  115. m_VetexList[i] = v1;
  116. m_VetexList[i + 1] = v2;
  117. m_VetexList[i + 2] = v3;
  118. }
  119. }
  120. private static UIVertex _SetNewPosAndUV(UIVertex pVertex, float pOutLineWidth,
  121. Vector2 pPosCenter,
  122. Vector2 pTriangleX, Vector2 pTriangleY,
  123. Vector2 pUVX, Vector2 pUVY,
  124. Vector2 pUVOriginMin, Vector2 pUVOriginMax) {
  125. // Position
  126. var pos = pVertex.position;
  127. var posXOffset = pos.x > pPosCenter.x ? pOutLineWidth : -pOutLineWidth;
  128. var posYOffset = pos.y > pPosCenter.y ? pOutLineWidth : -pOutLineWidth;
  129. pos.x += posXOffset;
  130. pos.y += posYOffset;
  131. pVertex.position = pos;
  132. // UV
  133. Vector2 uv = pVertex.uv0;
  134. uv += pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1);
  135. uv += pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1);
  136. pVertex.uv0 = uv;
  137. pVertex.uv1 = pUVOriginMin; //uv1 uv2 可用 tangent normal 在缩放情况 会有问题
  138. pVertex.uv2 = pUVOriginMax;
  139. return pVertex;
  140. }
  141. private static float _Min(float pA, float pB, float pC) {
  142. return Mathf.Min(Mathf.Min(pA, pB), pC);
  143. }
  144. private static float _Max(float pA, float pB, float pC) {
  145. return Mathf.Max(Mathf.Max(pA, pB), pC);
  146. }
  147. private static Vector2 _Min(Vector2 pA, Vector2 pB, Vector2 pC) {
  148. return new Vector2(_Min(pA.x, pB.x, pC.x), _Min(pA.y, pB.y, pC.y));
  149. }
  150. private static Vector2 _Max(Vector2 pA, Vector2 pB, Vector2 pC) {
  151. return new Vector2(_Max(pA.x, pB.x, pC.x), _Max(pA.y, pB.y, pC.y));
  152. }
  153. public static void SetCanvasShaderChannels(Canvas canvas) {
  154. canvas.additionalShaderChannels = AdditionalCanvasShaderChannels.Normal
  155. | AdditionalCanvasShaderChannels.Tangent
  156. | AdditionalCanvasShaderChannels.TexCoord1
  157. | AdditionalCanvasShaderChannels.TexCoord2
  158. | AdditionalCanvasShaderChannels.TexCoord3;
  159. }
  160. }
  161. }