前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Unity Shader入门】☀️ | 使用Shader实现一个 图片边框 ✨制作!

【Unity Shader入门】☀️ | 使用Shader实现一个 图片边框 ✨制作!

原创
作者头像
呆呆敲代码的小Y
修改2021-08-26 10:20:59
1.4K0
修改2021-08-26 10:20:59
举报

使用Shader实现一个 图片边框 ✨制作!

请添加图片描述
请添加图片描述
📢前言
  • 🎄闲来无事,正好碰到一个需求需要给图片加上一个边框
  • 🎄加边框的方式有很多种,可以用遮罩来做,也可以自定义修改图片等
  • 🎄那我今天就选择来用Shader搞一个图片边框

🍉图片边框制作

🏳️‍🌈Shader代码

新建一个 Shader ,然后改个名字,如下所示

在这里插入图片描述
在这里插入图片描述

然后打开这个Shader脚本,将以下代码复制进去!

代码语言:javascript
复制
Shader "Custom/SpritesOutline"
{
	Properties
	{
		[PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
		_Color("Main texture Tint", Color) = (1,1,1,1)

		[Header(General Settings)]
		[MaterialToggle] _OutlineEnabled("Outline Enabled", Float) = 1
		[MaterialToggle] _ConnectedAlpha("Connected Alpha", Float) = 0
		[HideInInspector] _AlphaThreshold("Alpha clean", Range(0, 1)) = 0
		_Thickness("Width (Max recommended 100)", float) = 10
		[KeywordEnum(Solid, Gradient, Image)] _OutlineMode("Outline mode", Float) = 0
		[KeywordEnum(Contour, Frame)] _OutlineShape("Outline shape", Float) = 0
		[KeywordEnum(Inside under sprite, Inside over sprite, Outside)] _OutlinePosition("Outline Position (Frame Only)", Float) = 0

		[Header(Solid Settings)]
		_SolidOutline("Outline Color Base", Color) = (1,1,1,1)

		[Header(Gradient Settings)]
		_GradientOutline1("Outline Color 1", Color) = (1,1,1,1)
		_GradientOutline2("Outline Color 2", Color) = (1,1,1,1)
		_Weight("Weight", Range(0, 1)) = 0.5
		_Angle("Gradient Angle (General gradient Only)", float) = 45
			//[KeywordEnum(General, Frame directed)] _FrameMode("Frame Mode (Frame Only)", Float) = 0

			[Header(Image Settings)]
			_FrameTex("Frame Texture", 2D) = "white" {}
			_ImageOutline("Outline Color Base", Color) = (1,1,1,1)
			[KeywordEnum(Stretch, Tile)] _TileMode("Frame mode", Float) = 0
	}

		SubShader
			{
				Tags
				{
					"Queue" = "Transparent"
					"IgnoreProjector" = "True"
					"RenderType" = "Transparent"
					"PreviewType" = "Plane"
					"CanUseSpriteAtlas" = "True"
				}

				Cull Off
				Lighting Off
				ZWrite Off
				Blend One OneMinusSrcAlpha

				Pass
				{
				CGPROGRAM
					#pragma vertex vert
					#pragma fragment frag
					#pragma multi_compile _ PIXELSNAP_ON
					#pragma exclude_renderers d3d11_9x

					#include "UnityCG.cginc"

					struct appdata_t
					{
						float4 vertex   : POSITION;
						float4 color    : COLOR;
						float2 texcoord : TEXCOORD0;
					};

					struct v2f
					{
						float4 vertex   : SV_POSITION;
						fixed4 color : COLOR;
						float2 texcoord  : TEXCOORD0;
					};

					fixed4 _Color;
					fixed _Thickness;
					fixed _OutlineEnabled;
					fixed _ConnectedAlpha;
					fixed _OutlineShape;
					fixed _OutlinePosition;
					fixed _OutlineMode;

					fixed4 _SolidOutline;

					fixed4 _GradientOutline1;
					fixed4 _GradientOutline2;
					fixed _Weight;
					fixed _AlphaThreshold;
					fixed _Angle;
					//fixed _FrameMode;

					fixed4 _ImageOutline;
					fixed _TileMode;

					v2f vert(appdata_t IN)
					{
						v2f OUT;
						OUT.vertex = UnityObjectToClipPos(IN.vertex);
						OUT.texcoord = IN.texcoord;
						OUT.color = IN.color * _Color;
						#ifdef PIXELSNAP_ON
						OUT.vertex = UnityPixelSnap(OUT.vertex);
						#endif

						return OUT;
					}

					sampler2D _MainTex;
					sampler2D _AlphaTex;
					float _AlphaSplitEnabled;
					uniform float4 _MainTex_TexelSize;

					sampler2D _FrameTex;
					uniform float4 _FrameTex_TexelSize;
					uniform float4 _FrameTex_ST;

					fixed4 SampleSpriteTexture(float2 uv)
					{
						float2 offsets;
						if ((_OutlinePosition != 2 && _OutlineShape == 1) || _OutlineEnabled == 0) // not outside and frame
						{
							offsets = float2(0, 0);
						}
						else
						{
							offsets = float2(_Thickness * 2, _Thickness * 2);
						}
						float2 bigsize = float2(_MainTex_TexelSize.z, _MainTex_TexelSize.w);
						float2 smallsize = float2(_MainTex_TexelSize.z - offsets.x, _MainTex_TexelSize.w - offsets.y);

						float2 uv_changed = float2
						(
							uv.x * bigsize.x / smallsize.x - 0.5 * offsets.x / smallsize.x,
							uv.y * bigsize.y / smallsize.y - 0.5 * offsets.y / smallsize.y
						);

						if (uv_changed.x < 0 || uv_changed.x > 1 || uv_changed.y < 0 || uv_changed.y > 1)
						{
							return float4(0, 0, 0, 0);
						}

						fixed4 color = tex2D(_MainTex, uv_changed);

		#if UNITY_TEXTURE_ALPHASPLIT_ALLOWED
						if (_AlphaSplitEnabled)
							color.a = tex2D(_AlphaTex, uv).r;
		#endif //UNITY_TEXTURE_ALPHASPLIT_ALLOWED

						return color;
					}

					bool CheckOriginalSpriteTexture(float2 uv, bool ifZero)
					{
						float thicknessX = _Thickness / _MainTex_TexelSize.z;
						float thicknessY = _Thickness / _MainTex_TexelSize.w;
						int steps = 100;
						float angle_step = 360.0 / steps;

						float alphaThreshold = _AlphaThreshold / 10;
						float alphaCount = _AlphaThreshold * 10;

						// check if the basic points has an alpha to speed up the process and not use the for loop
						bool outline = false;
						float alphaCounter = 0;

						if (ifZero)
						{

						}
						else
						{
							outline = SampleSpriteTexture(uv + fixed2(0, +thicknessY)).a > alphaThreshold ||
										SampleSpriteTexture(uv + fixed2(0, -thicknessY)).a > alphaThreshold ||
										SampleSpriteTexture(uv + fixed2(+thicknessX, 0)).a > alphaThreshold ||
										SampleSpriteTexture(uv + fixed2(-thicknessX, 0)).a > alphaThreshold ||
										SampleSpriteTexture(uv + fixed2(+thicknessX * cos(3.14 / 4), -thicknessY * sin(3.14 / 4))).a > alphaThreshold ||
										SampleSpriteTexture(uv + fixed2(-thicknessX * cos(3.14 / 4), +thicknessY * sin(3.14 / 4))).a > alphaThreshold ||
										SampleSpriteTexture(uv + fixed2(-thicknessX * cos(3.14 / 4), -thicknessY * sin(3.14 / 4))).a > alphaThreshold ||
										SampleSpriteTexture(uv + fixed2(+thicknessX * cos(3.14 / 4), +thicknessY * sin(3.14 / 4))).a > alphaThreshold;
						}
						if (outline) return outline;

						for (int i = 0; i < steps; i++) // high number and not a variable to avoid stupid compiler bugs
						{
							float angle = i * angle_step * 2 * 3.14 / 360;
							if (ifZero && SampleSpriteTexture(uv + fixed2(thicknessX * cos(angle), thicknessY * sin(angle))).a == 0)
							{
								alphaCounter++;
								if (alphaCounter >= alphaCount)
								{
									outline = true;
									break;
								}
							}
							else if (!ifZero && SampleSpriteTexture(uv + fixed2(thicknessX * cos(angle), thicknessY * sin(angle))).a > alphaThreshold)
							{
								outline = true;
								break;
							}
						}

						return outline;
					}

					fixed4 frag(v2f IN) : SV_Target
					{
						float thicknessX = _Thickness / _MainTex_TexelSize.z;
						float thicknessY = _Thickness / _MainTex_TexelSize.w;

						fixed4 c = SampleSpriteTexture(IN.texcoord) * IN.color;

						c.rgb *= c.a;

						fixed alpha;

						fixed4 outlineC = fixed4(0, 0, 0, 1);

						if (_OutlineEnabled != 0)
						{
							if (_OutlineMode == 0) // Solid
							{
								outlineC = _SolidOutline;

								if (_ConnectedAlpha != 0)
								{
									outlineC.a *= _Color.a;
								}
								outlineC.rgb *= outlineC.a;
							}
							else if (_OutlineMode == 1) // Gradient
							{
								float x = IN.texcoord.x;
								float y = IN.texcoord.y;

								float ratio1 = 0;
								float ratio2 = 0;

								if (_OutlineShape == 0) // contour
								{
									if (
										((_OutlinePosition != 2 && _OutlineShape == 1) && c.a != 0 &&  // inside and frame
											(
												IN.texcoord.y + thicknessY > 1 ||
												IN.texcoord.y - thicknessY < 0 ||
												IN.texcoord.x + thicknessX > 1 ||
												IN.texcoord.x - thicknessX < 0 ||
												CheckOriginalSpriteTexture(IN.texcoord, true)
											)
										)
										||
										((_OutlinePosition == 2 || _OutlineShape != 1) && c.a == 0 &&   // outside or contour
											CheckOriginalSpriteTexture(IN.texcoord, false)
										)
									)
									{
										if (_Angle >= 360)
										{
											int div = _Angle / 360;
											_Angle = (_Angle / 360 - div) * 360;
										}
										_Angle *= 2 * 3.14 / 360;

										ratio1 = (0.5 - x) * cos(_Angle) + (0.5 - y) * sin(_Angle) + 0.5;
										ratio2 = (x - 0.5) * cos(_Angle) + (y - 0.5) * sin(_Angle) + 0.5;

										ratio1 *= 2 * _Weight;
										ratio2 *= 2 * (1 - _Weight);

										if (_ConnectedAlpha != 0)
										{
											_GradientOutline1.a *= _Color.a;
											_GradientOutline2.a *= _Color.a;
											//outlineC.rgb *= outlineC.a;
										}
										_GradientOutline1.rgb *= _GradientOutline1.a;
										_GradientOutline2.rgb *= _GradientOutline2.a;
										outlineC = _GradientOutline1 * ratio1 + _GradientOutline2 * ratio2;
									}
								}
								else if (_OutlineShape == 1) // frame
								{
									if (IN.texcoord.y + thicknessY > 1 ||
										IN.texcoord.y - thicknessY < 0 ||
										IN.texcoord.x + thicknessX > 1 ||
										IN.texcoord.x - thicknessX < 0)
									{
										// between down left to up left
										if (y * thicknessX - x * thicknessY > 0 &&
											y * thicknessX + x * thicknessY - thicknessX < 0 &&
											x < 0.5f)
										{
											ratio1 = 1 - x / thicknessX;
											ratio2 = x / thicknessX;
										}
										// between down left to down right
										else if (y * thicknessX - x * thicknessY < 0 &&
												y * thicknessX + x * thicknessY - thicknessY < 0 &&
												y < 0.5f)
										{
											ratio1 = 1 - y / thicknessY;
											ratio2 = y / thicknessY;
										}
										// between down right to up right
										else if (y * thicknessX - x * thicknessY - thicknessX + thicknessY < 0 &&
												y * thicknessX + x * thicknessY - thicknessY > 0 &&
												x > 0.5f)
										{
											ratio1 = (x - 1) / thicknessX + 1;
											ratio2 = -(x - 1) / thicknessX;
										}
										// between up left to up right
										else if (y * thicknessX - x * thicknessY - thicknessX + thicknessY > 0 &&
												y * thicknessX + x * thicknessY - thicknessX > 0 &&
												y > 0.5f)
										{
											ratio1 = (y - 1) / thicknessY + 1;
											ratio2 = -(y - 1) / thicknessY;
										}

										ratio1 *= 2 * _Weight;
										ratio2 *= 2 * (1 - _Weight);

										if (_ConnectedAlpha != 0)
										{
											_GradientOutline1.a *= _Color.a;
											_GradientOutline2.a *= _Color.a;
											//outlineC.rgb *= outlineC.a;
										}
										_GradientOutline1.rgb *= _GradientOutline1.a;
										_GradientOutline2.rgb *= _GradientOutline2.a;
										outlineC = _GradientOutline1 * ratio1 + _GradientOutline2 * ratio2;
									}
								}
							}
							else if (_OutlineMode == 2) // Image
							{
								outlineC = _ImageOutline;
								fixed2 frame_coord;

								if (_TileMode == 0)
								{
									frame_coord = IN.texcoord;
								}
								else if (_TileMode == 1)
								{
									frame_coord = fixed2
									(
										_FrameTex_ST.x * IN.texcoord.x * _MainTex_TexelSize.z / _FrameTex_TexelSize.z - _FrameTex_ST.z,
										_FrameTex_ST.y * IN.texcoord.y * _MainTex_TexelSize.w / _FrameTex_TexelSize.w - _FrameTex_ST.w
									);

									if (frame_coord.x > 1)
									{
										frame_coord = fixed2
										(
											frame_coord.x - floor(frame_coord.x),
											frame_coord.y
										);
									}
									if (frame_coord.y > 1)
									{
										frame_coord = fixed2
										(
											frame_coord.x,
											frame_coord.y - floor(frame_coord.y)
										);
									}
								}
								fixed4 text = tex2D(_FrameTex, frame_coord);

								text.rgb *= text.a;

								outlineC.rgb *= text.rgb;
								outlineC.a *= text.a;

								if (_ConnectedAlpha != 0)
								{
									outlineC.a *= _Color.a;
								}
								outlineC.rgb *= outlineC.a;
							}

							if (_OutlineShape == 1) // Frame
							{
								if (IN.texcoord.y + thicknessY > 1 ||
									IN.texcoord.y - thicknessY < 0 ||
									IN.texcoord.x + thicknessX > 1 ||
									IN.texcoord.x - thicknessX < 0)
								{
									if (_OutlinePosition == 0 && c.a != 0 && _Thickness > 0)
									{
										return c;
									}
									else
									{
										return outlineC;
									}
								}
								else
								{
									return c;
								}
							}
							else if (_OutlineShape == 0 && _Thickness > 0) // Contour
							{
								if ((_OutlinePosition != 2 && _OutlineShape == 1) && c.a != 0 && // inside and frame
									(
										IN.texcoord.y + thicknessY > 1 ||
										IN.texcoord.y - thicknessY < 0 ||
										IN.texcoord.x + thicknessX > 1 ||
										IN.texcoord.x - thicknessX < 0 ||
										CheckOriginalSpriteTexture(IN.texcoord, true)
									)
								)
								{
									return outlineC;
								}
								else if ((_OutlinePosition == 2 || _OutlineShape != 1) && c.a == 0 && // outside orcontour
										(
											CheckOriginalSpriteTexture(IN.texcoord, false)
										)
									)
								{
									return outlineC;
								}
								else
								{
									return c;
								}
							}
							else
							{
								return c;
							}
						}
						else
						{
							return c;
						}

						return c;
						//return c;
					}
				ENDCG
				}
			}
}

🏳️‍🌈使用方法操作

新建一个材质Material

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后在Image的材质添加上就好了

在这里插入图片描述
在这里插入图片描述

效果:

在这里插入图片描述
在这里插入图片描述
请添加图片描述
请添加图片描述

🏳️‍🌈使用代码控制边框显示与隐藏

搭建一个简单的UI,进行一个测试

请添加图片描述
请添加图片描述

代码如下:

代码语言:javascript
复制
using UnityEngine;
using UnityEngine.UI;

public class TexDemo : MonoBehaviour
{
    //2D图片边框材质
    public Material outLineMaterial;
    public Button texBtn1;
    public Button texBtn2;

    private void Awake()
    {
        texBtn1.onClick.AddListener(ShowOutline);
        texBtn2.onClick.AddListener(HideOutline);
    }
    /// <summary>
    /// 显示边框
    /// </summary>
    private void ShowOutline()
    {
        outLineMaterial.SetFloat("_Thickness", 10);
    }

    /// <summary>
    /// 隐藏边框
    /// </summary>
    private void HideOutline()
    {
        outLineMaterial.SetFloat("_Thickness", 0);
    }
}

💬总结

  • 🌻今日份的简单图片边框操作的你学会了吗!
  • 🌻感觉还不错的可以来一波三连支持一下哦
  • 🌻你的三连就是博主更新的最大的动力😀

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 📢前言
  • 🍉图片边框制作
    • 🏳️‍🌈Shader代码
      • 🏳️‍🌈使用方法操作
        • 🏳️‍🌈使用代码控制边框显示与隐藏
        • 💬总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档