Unity中实现UI描边
生活随笔
收集整理的這篇文章主要介紹了
Unity中实现UI描边
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一:效果演示
?二:使用
MaterialPath:材質路徑(需要自己創建材質并設置Shader為UI/Outline)
Outline Color:描邊顏色
Outline Width:描邊寬度
三:為什么盡量避免使用UGUI的Outline和Shadaw組件?
UGUI源碼解析——Shadow
UGUI源碼解析——Outline
不論是Outline還是Shadow,它的實現原理都是將原網格數據復制一份并向指定方向移動指定像素,然后再填充到頂點數據中,所以頂點數和三角面數Shadow會增加一倍,Outline會增加四倍
四:代碼實現
Shadar:
Shader "UI/Outline" {Properties{_MainTex ("Main Texture", 2D) = "white" {}_Color ("Tint", Color) = (1, 1, 1, 1)//_OutlineColor ("Outline Color", Color) = (1, 1, 1, 1)//_OutlineWidth ("Outline Width", Int) = 1_StencilComp ("Stencil Comparison", Float) = 8_Stencil ("Stencil ID", Float) = 0_StencilOp ("Stencil Operation", Float) = 0_StencilWriteMask ("Stencil Write Mask", Float) = 255_StencilReadMask ("Stencil Read Mask", Float) = 255_ColorMask ("Color Mask", Float) = 15[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0}SubShader{Tags{ "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane""CanUseSpriteAtlas"="True"}Stencil{Ref [_Stencil]Comp [_StencilComp]Pass [_StencilOp] ReadMask [_StencilReadMask]WriteMask [_StencilWriteMask]}Cull OffLighting OffZWrite OffZTest [unity_GUIZTestMode]Blend SrcAlpha OneMinusSrcAlphaColorMask [_ColorMask]Pass{Name "OUTLINE"CGPROGRAM#pragma vertex vert#pragma fragment frag//Add for RectMask2D #include "UnityUI.cginc"//End for RectMask2D sampler2D _MainTex;fixed4 _Color;fixed4 _TextureSampleAdd;float4 _MainTex_TexelSize;//float4 _OutlineColor;//int _OutlineWidth;//Add for RectMask2D float4 _ClipRect;//End for RectMask2Dstruct appdata{float4 vertex : POSITION;float4 tangent : TANGENT;float4 normal : NORMAL;float2 texcoord : TEXCOORD0;float2 uv1 : TEXCOORD1;float2 uv2 : TEXCOORD2;float2 uv3 : TEXCOORD3;fixed4 color : COLOR;};struct v2f{float4 vertex : SV_POSITION;float4 tangent : TANGENT;float4 normal : NORMAL;float2 texcoord : TEXCOORD0;float2 uv1 : TEXCOORD1;float2 uv2 : TEXCOORD2;float2 uv3 : TEXCOORD3;//Add for RectMask2D float4 worldPosition : TEXCOORD4;//End for RectMask2Dfixed4 color : COLOR;};v2f vert(appdata IN){v2f o;//Add for RectMask2D o.worldPosition = IN.vertex;//End for RectMask2D o.vertex = UnityObjectToClipPos(IN.vertex);o.tangent = IN.tangent;o.texcoord = IN.texcoord;o.color = IN.color * _Color;o.uv1 = IN.uv1;o.uv2 = IN.uv2;o.uv3 = IN.uv3;o.normal = IN.normal;return o;}/*fixed IsInRect(float2 pPos, float4 pClipRect){pPos = step(pClipRect.xy, pPos) * step(pPos, pClipRect.zw);return pPos.x * pPos.y;}*/fixed IsInRect(float2 pPos, float2 pClipRectMin, float2 pClipRectMax){pPos = step(pClipRectMin, pPos) * step(pPos, pClipRectMax);return pPos.x * pPos.y;}fixed SampleAlpha(int pIndex, v2f IN){const fixed sinArray[12] = { 0, 0.5, 0.866, 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5 };const fixed cosArray[12] = { 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5, 0, 0.5, 0.866 };float2 pos = IN.texcoord + _MainTex_TexelSize.xy * float2(cosArray[pIndex], sinArray[pIndex]) * IN.normal.z; //normal.z 存放 _OutlineWidthreturn IsInRect(pos, IN.uv1, IN.uv2) * (tex2D(_MainTex, pos) + _TextureSampleAdd).w * IN.tangent.w; //tangent.w 存放 _OutlineColor.w}fixed4 frag(v2f IN) : SV_Target{fixed4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;if (IN.normal.z > 0) //normal.z 存放 _OutlineWidth{color.w *= IsInRect(IN.texcoord, IN.uv1, IN.uv2); //uv1 uv2 存著原始字的uv長方形區域大小half4 val = half4(IN.uv3.x, IN.uv3.y, IN.tangent.z, 0); //uv3.xy tangent.z 分別存放著 _OutlineColor的rgbval.w += SampleAlpha(0, IN);val.w += SampleAlpha(1, IN);val.w += SampleAlpha(2, IN);val.w += SampleAlpha(3, IN);val.w += SampleAlpha(4, IN);val.w += SampleAlpha(5, IN);val.w += SampleAlpha(6, IN);val.w += SampleAlpha(7, IN);val.w += SampleAlpha(8, IN);val.w += SampleAlpha(9, IN);val.w += SampleAlpha(10, IN);val.w += SampleAlpha(11, IN);color = (val * (1.0 - color.a)) + (color * color.a);color.a = saturate(color.a);color.a *= IN.color.a*IN.color.a*IN.color.a; //字逐漸隱藏時,描邊也要隱藏}//Add for RectMask2D color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect); #ifdef UNITY_UI_ALPHACLIPclip(color.a - 0.001); #endif//End for RectMask2D return color;}ENDCG}} }CS:
using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;/// <summary> /// 描邊 /// </summary> [DisallowMultipleComponent] [AddComponentMenu("LFramework/UI/Effects/Outline", 2)] public class Outline : BaseMeshEffect {protected Outline(){}//描邊顏色[SerializeField]Color m_OutlineColor = Color.white;public Color OutlineColor{get{return m_OutlineColor;}set{m_OutlineColor = value;graphic.SetVerticesDirty();}}//描邊寬度[SerializeField][Range(0, 5)]int m_OutlineWidth = 1;public int OutlineWidth{get{return m_OutlineWidth;}set{m_OutlineWidth = value;graphic.SetVerticesDirty();}}//頂點緩存List<UIVertex> m_VertexCache = new List<UIVertex>();//材質路徑const string MaterialPath = "Assets/Materials/UIOutline.mat";protected override void Awake(){base.Awake();if (graphic != null){if (graphic.material == null|| graphic.material.shader.name != "UI/Outline"){LoadOutlineMat();}}RefreshOutline();}void LoadOutlineMat(){ #if UNITY_EDITORvar mat = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MaterialPath);if (mat != null){graphic.material = mat;}else{Debug.LogError("沒有找到材質Outline.mat");} #elsevar shader = Shader.Find("UI/Outline");base.graphic.material = new Material(shader); #endif}#if UNITY_EDITORprotected override void OnValidate(){base.OnValidate();if (graphic != null){if (graphic.material == null|| graphic.material.shader.name != "UI/Outline"){LoadOutlineMat();}}} #endifpublic void RefreshOutline(){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;}}}public override void ModifyMesh(VertexHelper vh){vh.GetUIVertexStream(m_VertexCache);ApplyOutline();vh.Clear();vh.AddUIVertexTriangleStream(m_VertexCache);m_VertexCache.Clear();}void ApplyOutline(){for (int i = 0, count = m_VertexCache.Count - 3; i <= count; i += 3){var v1 = m_VertexCache[i];var v2 = m_VertexCache[i + 1];var v3 = m_VertexCache[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(m_OutlineColor.r, m_OutlineColor.g);var col_ba = new Vector4(0, 0, m_OutlineColor.b, m_OutlineColor.a);var normal = new Vector3(0, 0, m_OutlineWidth);//為每個頂點設置新的Position和UV,并傳入原始UV框v1 = SetNewPosAndUV(v1, m_OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);v1.uv3 = col_rg;v1.tangent = col_ba;v1.normal = normal;v2 = SetNewPosAndUV(v2, m_OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);v2.uv3 = col_rg;v2.tangent = col_ba;v2.normal = normal;v3 = SetNewPosAndUV(v3, m_OutlineWidth, posCenter, triX, triY, uvX, uvY, uvMin, uvMax);v3.uv3 = col_rg;v3.tangent = col_ba;v3.normal = normal;//應用設置后的UIVertexm_VertexCache[i] = v1;m_VertexCache[i + 1] = v2;m_VertexCache[i + 2] = v3;}}static UIVertex SetNewPosAndUV(UIVertex vertex, int width,Vector2 pPosCenter,Vector2 triangleX, Vector2 triangleY,Vector2 uvX, Vector2 uvY,Vector2 uvOriginMin, Vector2 uvOriginMax){//Positionvar pos = vertex.position;var posXOffset = pos.x > pPosCenter.x ? width : -width;var posYOffset = pos.y > pPosCenter.y ? width : -width;pos.x += posXOffset;pos.y += posYOffset;vertex.position = pos;//UVvar uv = vertex.uv0;uv += uvX / triangleX.magnitude * posXOffset * (Vector2.Dot(triangleX, Vector2.right) > 0 ? 1 : -1);uv += uvY / triangleY.magnitude * posYOffset * (Vector2.Dot(triangleY, Vector2.up) > 0 ? 1 : -1);vertex.uv0 = uv;vertex.uv1 = uvOriginMin;vertex.uv2 = uvOriginMax;return vertex;}static float Min(float pA, float pB, float pC){return Mathf.Min(Mathf.Min(pA, pB), pC);}static float Max(float pA, float pB, float pC){return Mathf.Max(Mathf.Max(pA, pB), pC);}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));}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));} }總結
以上是生活随笔為你收集整理的Unity中实现UI描边的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 中软的面试,原来是这样,严重鄙视。
- 下一篇: java毕业设计——基于JSP+sqls