[Unity Combat] Use shader and shader Graph to achieve 2d picture stroke effect (with source code)

foreword

Recently, I am learning shader Graph related content. In fact, I can see many tutorials on the Internet about realizing the 2D picture stroke effect, but I found that most of them are based on the relatively old 2018unity version, but our actual development and use may be relatively new 2021 And above versions, there are still some differences. Actually, in the process of upgrading or using, you will encounter many problems, and few people will share the source code of the shader graph connection diagram

If not, I want to sort out and share my study notes and source code, so I have this article

Shader

1. Inner stroke

Idea: In the fragment shader, judge the upper, lower, left, and right pixels of the current fragment (use the value width to determine the "how far" up, down, left, and right, so as to obtain the width of the stroke), and the alpha components of the upper, lower, left, and right pixels are multiplied closer 0, the closer the pixel color is to the stroke color. It can be roughly understood as if one of the top, bottom, left, and right pixels of a certain pixel p is a transparent pixel, then p is on the edge, and the p pixel is drawn as the stroke color.

code show as below:

Shader "Custom_Shader/ImageInnerOutline"
{
    
    
    Properties
    {
    
    
        _MainTex ("Sprite Texture", 2D) = "white" {
    
    }
        _OutlineWidth ("Outline Width", float) = 1
        _OutlineColor ("Outline Color", Color) = (1,1,1,1)
    }
    SubShader
    {
    
    
        Blend SrcAlpha OneMinusSrcAlpha
        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            half4 _MainTex_TexelSize;
            float _OutlineWidth;
            float4 _OutlineColor;

            struct appdata
            {
    
    
                float4 vertex   : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
    
    
                float4 vertex   : SV_POSITION;
                half2 uv  : TEXCOORD0;
                half2 left : TEXCOORD1;
                half2 right : TEXCOORD2;
                half2 up : TEXCOORD3;
                half2 down : TEXCOORD5;
            };

            v2f vert(appdata i)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.uv = TRANSFORM_TEX(i.uv, _MainTex);
                o.left = o.uv + half2(-1, 0) * _MainTex_TexelSize.xy * _OutlineWidth;
                o.right = o.uv + half2(1, 0) * _MainTex_TexelSize.xy * _OutlineWidth;
                o.up = o.uv + half2(0, 1) * _MainTex_TexelSize.xy * _OutlineWidth;
                o.down = o.uv + half2(0, -1) * _MainTex_TexelSize.xy * _OutlineWidth;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
    
    
                fixed4 c = tex2D(_MainTex, i.uv);
                float transparent = tex2D(_MainTex, i.left).a * tex2D(_MainTex, i.right).a * tex2D(_MainTex, i.up).a * tex2D(_MainTex, i.down).a;
                c.rgb = lerp(_OutlineColor.rgb, c.rgb, transparent);
                
                return c;
            }
            ENDCG
        }
    }
}

Stroke effect:
insert image description here
The inner stroke will occupy the non-transparent pixels on the edge of the image itself. When the stroke width increases, the effect will be
insert image description here

2. Outer stroke

Idea: In the fragment shader, process the pixel p, and sample the top, bottom, left, and right four pixels of p (use a variable width to control the stroke width, that is, process the pixels up, down, left, and right), if p itself is a transparent pixel ,but

If there are non-transparent pixels on the top, bottom, left, and right, the current pixel p returns the stroke color.
If there are transparent pixels on the top, bottom, left, and right, it returns transparent.
If p itself is non-transparent pixels, it returns its own color.
The code is as follows:

Shader "Custom_Shader/ImageOuterOutline"
{
    
    
    Properties
    {
    
    
        _MainTex ("Sprite Texture", 2D) = "white" {
    
    }
        _OutlineWidth ("Outline Width", float) = 1
        _OutlineColor ("Outline Color", Color) = (1.0, 1.0, 1.0, 1.0)
        _AlphaValue ("Alpha Value", Range(0, 1)) = 0.1
    }
    SubShader
    {
    
    
        Blend SrcAlpha OneMinusSrcAlpha

        Pass 
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;
            half4 _MainTex_TexelSize;
            float _OutlineWidth;
            float4 _OutlineColor;
            float _AlphaValue;

            struct appdata
            {
    
    
                float4 vertex   : POSITION;
                float2 uv : TEXCOORD0;
                float4 normal : NORMAL;
            };

            struct v2f
            {
    
    
                float4 vertex   : SV_POSITION;
                half2 uv  : TEXCOORD0;
                half2 left : TEXCOORD1;
                half2 right : TEXCOORD2;
                half2 up : TEXCOORD3;
                half2 down : TEXCOORD5;
            };

            v2f vert(appdata i)
            {
    
    
                v2f o;
                o.vertex = o.vertex + i.normal * _OutlineWidth;
                o.vertex = UnityObjectToClipPos(i.vertex);
                o.uv = TRANSFORM_TEX(i.uv, _MainTex);
                o.left = o.uv + half2(-1, 0) * _MainTex_TexelSize.xy * _OutlineWidth;
                o.right = o.uv + half2(1, 0) * _MainTex_TexelSize.xy * _OutlineWidth;
                o.up = o.uv + half2(0, 1) * _MainTex_TexelSize.xy * _OutlineWidth;
                o.down = o.uv + half2(0, -1) * _MainTex_TexelSize.xy * _OutlineWidth;
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
    
    
                float transparent = tex2D(_MainTex, i.left).a + tex2D(_MainTex, i.right).a + tex2D(_MainTex, i.up).a + tex2D(_MainTex, i.down).a;
                fixed4 col = tex2D(_MainTex, i.uv);

                if (col.a < 0.1) {
    
    
                    return step(_AlphaValue, transparent) * _OutlineColor;
                } else {
    
    
                    return col;
                }
            }
            ENDCG
        }
    }
}

Outer stroke effect:
insert image description here
The outer stroke will not occupy the non-transparent pixels of the image, but it is required to have enough but transparent pixels around the image. When adjusting the outer stroke width, the effect: when the
insert image description here
stroke width is larger, the top and left of the Image will have A smooth cut is the extent of the picture itself

Shader Graph

1. 2d image stroke

(ps: It doesn’t matter if the picture is not clear. I will let go of the demo at the end of the file 源码. If you are not clear, you can directly check the source code for learning.)
insert image description here
The effect on different pictures
insert image description here

2. 2D image stroke with glare

insert image description here
Effects on different pictures
insert image description here

Final presentation

insert image description here

source code

https://gitcode.net/unity1/unity2d-shader-picturestroke

reference

  • 【视频】https://www.youtube.com/watch?v=Eyx3EfqqfMw&list=PLzDRvYVwl53tqzN5R-j33Sd7kf_f6b6z4
  • 【Article】https://www.jianshu.com/p/c68a730e9a8b

end

If you find an error in the article or have a better solution, please comment and private message me.

Well, I am Xiang Yu, https://xiangyu.blog.csdn.net/

A developer who worked silently in a small company, out of interest, only recently started to study unity by himself. If you have any questions, you are welcome to comment and private message me. Although I may not necessarily know some questions, I will check the information of all parties and try to give the best advice. I hope it can help more people who want to learn programming. , encourage each other~
insert image description here

Guess you like

Origin blog.csdn.net/qq_36303853/article/details/131624412