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

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

作者头像
呆呆敲代码的小Y
发布2021-08-19 10:38:46
1.1K0
发布2021-08-19 10:38:46
举报
文章被收录于专栏:呆呆敲代码的小Y 公众号

?前言

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

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

?那我今天就选择来用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
    }

        SubShade
            {
                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) // contou
                                {
                                    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 contou
                                            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) // Contou
                            {
                                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 orcontou
                                        (
                                            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 : MonoBehaviou
{
    //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);
    }
}

?总结

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

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

?你的三连就是博主更新的最大的动力?

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/08/17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档