分享

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

 敲代码的小Y 2021-12-01

  • 📢本文由 呆呆敲代码的小Y 原创🙉

  • 📢未来很长,值得我们全力奔赴更美好的生活✨

✨目录

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

        • 📢前言

    • 🍉图片边框制作

      • 🏳️‍🌈Shader代码

      • 🏳️‍🌈使用方法操作

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

    • 💬总结



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



📢前言

  • 🎄闲来无事,正好碰到一个需求需要给图片加上一个边框

  • 🎄加边框的方式有很多种,可以用遮罩来做,也可以自定义修改图片等

  • 🎄那我今天就选择来用Shader搞一个图片边框

🍉图片边框制作

🏳️‍🌈Shader代码

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

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

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,进行一个测试

代码如下:

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);
   }}

💬总结

  • 🌻今日份的简单图片边框操作的你学会了吗!

  • 🌻感觉还不错的可以来一波三连支持一下哦

  • 🌻你的三连就是博主更新的最大的动力😀

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多