Unity-Shader – 2D sprite stroke effect

foreword

Today we will implement a simple 2D sprite map stroke effect, the effect picture is as follows:

Preparation:

First, we open unity to create a new scene, import a picture, and set the picture as a Sprite type, as shown in the figure:

   

Implementation idea:

The idea is actually very simple. You can judge whether there is a value of transparency of 0 around the pixel. If there is, it means that the pixel is on the edge.

So we need to turn on alpha blend, namely: Blend SrcAlpha OneMinusSrcAlpha, and join the rendering queue,

<pre>
    
     
     Tags
     
     {<!-- -->
     
     
     
     
     "Queue"
     
      
     
     =
     
      
     
     "Transparent"
     
     

     
     }
     
     
Blend SrcAlpha OneMinusSrcAlpha
    
    

Let’s start writing the fragment shader. The vertex shader is a conventional vertex coordinate transformation. I won’t paste the code here. Let’s mainly look at the fragment shader:

<pre>
    
     
     // ---------------------------【Fragment Shader】---------------------------
     
     

     
     fixed4
     
      frag
     
     (
     
     VertexOutput
     
      i
     
     )
     
      
     
     :
     
      SV_Target

     
     {<!-- -->
     
     
    
     
     fixed4
     
      col
     
     =
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
      i
     
     .
     
     uv
     
     )
     
     ;
     
     
    
     
     // Sample 4 points around
     
     
    
     
     float2
     
      up_uv
     
     =
     
      i
     
     .
     
     uv
     
      +
     
      
     
     float2
     
     (
     
     0
     
     ,
     
     1
     
     )
     
      
     
     *
     
      _lineWidth
     
     *
     
      _MainTex_TexelSize
     
     .
     
     xy
     
     ;
     
     
    
     
     float2
     
      down_uv
     
     =
     
      i
     
     .
     
     uv
     
      +
     
      
     
     float2
     
     (
     
     0
     
     ,
     
     -
     
     1
     
     )
     
      
     
     *
     
      _lineWidth
     
     *
     
      _MainTex_TexelSize
     
     .
     
     xy
     
     ;
     
     
    
     
     float2
     
      left_uv
     
     =
     
      i
     
     .
     
     uv
     
      +
     
      
     
     float2
     
     (
     
     -
     
     1
     
     ,
     
     0
     
     )
     
      
     
     *
     
      _lineWidth
     
     *
     
      _MainTex_TexelSize
     
     .
     
     xy
     
     ;
     
     
    
     
     float2
     
      right_uv
     
     =
     
      i
     
     .
     
     uv
     
      +
     
      
     
     float2
     
     (
     
     1
     
     ,
     
     0
     
     )
     
      
     
     *
     
      _lineWidth
     
     *
     
      _MainTex_TexelSize
     
     .
     
     xy
     
     ;
     
     
    
     
     // If there is a point whose transparency is 0, it means it is an edge
     
     
    
     
     float
     
      w
     
     =
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
     up_uv
     
     )
     
     .
     
     a
     
     *
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
     down_uv
     
     )
     
     .
     
     a
     
     *
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
     left_uv
     
     )
     
     .
     
     a
     
     *
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
     right_uv
     
     )
     
     .
     
     a
     
     ;
     
     

    
     
     if
     
     (
     
     w
     
     ==
     
      
     
     0
     
     )
     
     {<!-- -->
     
     
       col
     
     .
     
     rgb
     
     =
     
      _lineColor
     
     ;
     
     
    
     
     }
     
     

    
     
     return
     
      col
     
     ;
     
     

     
     }
    
    

_MainTex_TexelSize: Texture pixel size
We can use this property to calculate the surrounding (up, down, left, and right) pixel coordinates, and then judge whether there is a value with transparency of 0. If it is 0, returns the border color.

The effect is as follows:

Although we have implemented this function, if we observe carefully, we will find that the edges are jagged and not smooth. We can optimize and use interpolation to smooth the transition,

The final complete Shader code is as follows:

<pre>
    
     
     // ---------------------------【2D stroke effect】--------------- ------------
     
     

     
     // create by Longevity but Drunkard
     
     
Shader
     
     "lcl/shader2D/outline"
     
     

     
     {<!-- -->
     
     
    Properties
    
     
     {<!-- -->
     
     
        _MainTex
     
     (
     
     "Texture"
     
     ,
     
      
     
     2D
     
     )
     
      
     
     =
     
      
     
     "white"
     
      
     
     {<!-- -->
     
     }
     
     
        
     
     _lineWidth
     
     (
     
     "lineWidth"
     
     ,
     
     Range
     
     (
     
     0
     
     ,
     
     10
     
     )
     
     )
     
      
     
     =
     
      
     
     1
     
     
        
     
     _lineColor
     
     (
     
     "lineColor"
     
     ,
     
     color
     
     )
     
     =
     
     (
     
     1
     
     ,
     
     1
     
     ,
     
     1
     
     ,
     
     1
     
     )
     
     
    
     
     }
     
     
    
     
     // ---------------------------【Subshader】---------------- -----------
     
     
    SubShader
    
     
     {<!-- -->
     
     
        
     
     // The rendering queue adopts transparent
     
     
        Tags
     
     {<!-- -->
     
     
            
     
     "Queue"
     
      
     
     =
     
      
     
     "Transparent"
     
     
        
     
     }
     
     
        Blend SrcAlpha
     
     OneMinusSrcAlpha
     
     

        Pass
        
     
     {<!-- -->
     
     
            CGPROGRAM
            
     
     #
     
     pragma
     
      vertex vert
     
     
            
     
     #
     
     pragma
     
      fragment frag
     
     

            
     
     #include
     
     "UnityCG.cginc"
     
     
            
     
     //Vertex shader input structure
     
     
            
     
     structure
     
      
     
     VertexInput
     
     
            
     
     {<!-- -->
     
     
                
     
     float4
     
      vertex
     
     :
     
      POSITION
     
     ;
     
     
                
     
     float2
     
      uv
     
     :
     
      TEXCOORD0
     
     ;
     
     
            
     
     }
     
     ;
     
     
            
     
     //Vertex shader output structure
     
     
            
     
     structure
     
      
     
     VertexOutput
     
     
            
     
     {<!-- -->
     
     
                
     
     float2
     
      uv
     
     :
     
      TEXCOORD0
     
     ;
     
     
                
     
     float4
     
      vertex
     
     :
     
      SV_POSITION
     
     ;
     
     
            
     
     }
     
     ;
     
     

            
     
     // --------------------------【Vertex Shader】---------------- -----------
     
     
            
     
     VertexOutput
     
      vert
     
     (
     
     VertexInput
     
      v
     
     )
     
     
            
     
     {<!-- -->
     
     
                
     
     VertexOutput
     
      o
     
     ;
     
     
                o
     
     .
     
     vertex
     
     =
     
      
     
     UnityObjectToClipPos
     
     (
     
     v
     
     .
     
     vertex
     
     )
     
     ;
     
     
                o
     
     .
     
     uv
     
     =
     
      v
     
     .
     
     uv
     
     ;
     
     
                
     
     return
     
      o
     
     ;
     
     
            
     
     }
     
     

            
     
     sampler2D
     
      _MainTex
     
     ;
     
     
            
     
     float4
     
      _MainTex_TexelSize
     
     ;
     
     
            
     
     float
     
      _lineWidth
     
     ;
     
     
            
     
     float4
     
      _lineColor
     
     ;
     
     

            
     
     // ---------------------------【Fragment Shader】---------------------------
     
     
            
     
     fixed4
     
      frag
     
     (
     
     VertexOutput
     
      i
     
     )
     
      
     
     :
     
      SV_Target
            
     
     {<!-- -->
     
     
                
     
     fixed4
     
      col
     
     =
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
      i
     
     .
     
     uv
     
     )
     
     ;
     
     
                
     
     // Sample 4 points around
     
     
                
     
     float2
     
      up_uv
     
     =
     
      i
     
     .
     
     uv
     
      +
     
      
     
     float2
     
     (
     
     0
     
     ,
     
     1
     
     )
     
      
     
     *
     
      _lineWidth
     
     *
     
      _MainTex_TexelSize
     
     .
     
     xy
     
     ;
     
     
                
     
     float2
     
      down_uv
     
     =
     
      i
     
     .
     
     uv
     
      +
     
      
     
     float2
     
     (
     
     0
     
     ,
     
     -
     
     1
     
     )
     
      
     
     *
     
      _lineWidth
     
     *
     
      _MainTex_TexelSize
     
     .
     
     xy
     
     ;
     
     
                
     
     float2
     
      left_uv
     
     =
     
      i
     
     .
     
     uv
     
      +
     
      
     
     float2
     
     (
     
     -
     
     1
     
     ,
     
     0
     
     )
     
      
     
     *
     
      _lineWidth
     
     *
     
      _MainTex_TexelSize
     
     .
     
     xy
     
     ;
     
     
                
     
     float2
     
      right_uv
     
     =
     
      i
     
     .
     
     uv
     
      +
     
      
     
     float2
     
     (
     
     1
     
     ,
     
     0
     
     )
     
      
     
     *
     
      _lineWidth
     
     *
     
      _MainTex_TexelSize
     
     .
     
     xy
     
     ;
     
     
                
     
     // If there is a point whose transparency is 0, it means it is an edge
     
     
                
     
     float
     
      w
     
     =
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
     up_uv
     
     )
     
     .
     
     a
     
     *
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
     down_uv
     
     )
     
     .
     
     a
     
     *
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
     left_uv
     
     )
     
     .
     
     a
     
     *
     
      
     
     tex2D
     
     (
     
     _MainTex
     
     ,
     
     right_uv
     
     )
     
     .
     
     a
     
     ;
     
     

                
     
     // if(w == 0){<!-- -->
     
     
                    
     
     // col.rgb = _lineColor;
     
     
                
     
     // }
     
     
                
     
     // Interpolate with the original image
     
     
                col
     
     .
     
     rgb
     
     =
     
      
     
     lerp
     
     (
     
     _lineColor
     
     ,
     
     col
     
     .
     
     rgb
     
     ,
     
     w
     
     )
     
     ;
     
     
                
     
     return
     
      col
     
     ;
     
     
            
     
     }
     
     
            ENDCG
        
     
     }
     
     
    
     
     }
     
     

     
     }
    
    

Comparison of final renderings:

at last

Finally welcome to my
GitHub
Star, thank you! There are some special effect demos that I usually realize in the process of learning unity shader.