一:效果演示


二:使用


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描边相关推荐

  1. 【Unity3D】在Unity中实现UI指向箭头

    本问转载自http://ghostyii.com/uiarrow/,为什么转载呢,怕以后找不到了! ps:博主写到超级详细,复制粘贴即可使用,超赞的! 0x0.引言 屏幕UI指示箭头,非常常见的游戏U ...

  2. Unity中制作UI光晕效果(发光效果)

    Unity中,我们怎么制作UI物体发光的渐隐渐现的效果呢? 比如说我们有一张月亮光晕的精灵图片 我们可以给它添加一个CanvasGroup组件 我们可以发现,组件上的Alpha值可以控制图片的透明度, ...

  3. Unity中的UI相关组件

    一:Canvas:渲染UI --Overlay:覆盖模式 类似于手机贴膜,所有UI都会显示在场景中2D,3D物体的上层 在同一个Canvas下可以调整Canvas子物体的先后顺序,层级面板中越靠上则先 ...

  4. unity中的UI状态机,用于各界面之间的切换和跳转

    首先感谢姜雪松先生,大家可以去他的博客查看注释以及代码等,http://jxwgame.blog.51cto.com/943299/1613585 言归正传: 1.在开发项目的过程中,总是会遇到这样的 ...

  5. Unity 中实现UI左右滑动效果

    借助DoTween实现功能非常方便哦~,记得导入DoTween插件进项目 1.生成所有UI元素 根据配置面板之间的距离,大小,将UI生成到指定位置,这里是将第一个元素生成到屏幕的正中间 public ...

  6. 基于 Unity 中的 NGUI 插件,通用的 UI 如何设计

    以我的项目经历来说,要保证通用性必须分清需求是框架需要还是项目需要.举一个例子,所有的项目都需要一个弹窗提示的接口,但是不同项目弹窗都不一样,当时做的时候我没有想好怎么分离,那就放到项目类库里,保证框 ...

  7. Unity中UI界面颤抖解决方法

    Unity中UI界面颤抖解决方法 参考文章: (1)Unity中UI界面颤抖解决方法 (2)https://www.cnblogs.com/Study088/p/7290909.html 备忘一下.

  8. unity中ui界面介绍

    unity中ui界面的介绍 ui 又称gui,它比较适合做一些简单的界面. 可以在层级视图中右键单击就会出现很多选项卡,其中有一个就是ui单击就会出现如上图片中的内容 1.text: 这就是一个文本输 ...

  9. ui动效 unity_基于Unity中的NGUI插件,通用的UI如何设计?

    整理自知乎,文/王致远 以我的项目经历来说,要保证通用性必须分清需求是框架需要还是项目需要.举一个例子,所有的项目都需要一个弹窗提示的接口,但是不同项目弹窗都不一样,当时做的时候我没有想好怎么分离,那 ...

最新文章

  1. 01 http协议概念及工作流程
  2. 高并发下秒杀商品,你必须知道的9个细节
  3. unity 200.8m yoy_没错,Unity双十一来了
  4. 最大公共子序列、子串、可重叠重复子串
  5. centos磁盘空间满查询和移动命令小记
  6. Unity3d截图两种方式
  7. Windows Server 2012 新特性:IPAM的配置
  8. python爬虫实例手机_Python爬虫实现爬取京东手机页面的图片(实例代码)
  9. 单机 amp; 弱联网手游 防破解、金币改动 简单措施
  10. android 开源 高斯模糊_Android实现带毛玻璃效果(高斯模糊)背景的Dialog
  11. 如何完全卸载oracle和删除oracle在注册表中的注册信息
  12. Unity Web前端研究
  13. 搜索。深搜学习。深度优先搜索之数字全排列。nyoj,组合数
  14. ffmpeg解析出的视频参数PAR,DAR,SAR的意义
  15. 中学-知识与能力【9】
  16. 《网络安全等级保护基本要求》(GB/T 22239-2019)标准解读
  17. 【艾琪出品】《数据库课程设计》【参考】
  18. 微信小程序模仿拼多多APP地址选择样式
  19. linux服务器停止步骤,停止和重新启动许可服务器的步骤
  20. 当前目录下有一个文件名为 score1.txt 的文本文件,存放着某班学生的计算机课成绩,共有学号、平时成绩、期末成绩三列。请根据平时成绩占 40%,期末成绩占 60%的比例计算总评成绩。

热门文章

  1. h5案例分享--雀巢咖啡--传奇创世
  2. 服务器攒机指南-屌丝如何搭建测试平台
  3. java基础第12期——反射、注解
  4. 看我如何使用 剪草为马,撒豆成兵 之术(一)
  5. 聚类算法之K均值聚类
  6. Gitlab修改项目备注
  7. tableau跨库创建并集_刮擦柏林青年旅舍,并以此建立一个Tableau全景。
  8. Ubuntu 18.04 安装惠普打印机驱动后出现异常 No System tray detected on this system
  9. RPA如何助力新加坡破解生产力难题
  10. 阿里技术团队编写的对标金九银十大厂面试指南又在 git 上火了