Unity shader minimalist practice 5 – 2D picture stroke

Time：2021-12-4

Cyberpunk Judy Alvarez

This article describes how to stroke 2D images (sprite and ugui image in unity)

1. Inner tracing

Idea: in the slice shader, judge the upper, lower, left and right pixels of the current slice (use the numerical width to determine the “how far” from the upper, lower, left and right to obtain the stroke width). The closer the alpha components of the upper, lower, left and right pixels are multiplied by 0, the closer the pixel color is to the stroke color. It can be roughly understood that if one of the upper, lower, left and right pixels of a pixel P is a transparent pixel, P is at the edge and the P pixel is drawn as a stroke color.
The code is as follows:

``````Shader "Custom_Shader/ImageInnerOutline"
{
Properties
{
_MainTex ("Sprite Texture", 2D) = "white" {}
_OutlineWidth ("Outline Width", float) = 1
_OutlineColor ("Outline Color", Color) = (1,1,1,1)
}
{
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:

Inner stroke effect

The inner stroke will occupy the non transparent pixels at the edge of the picture itself. When the stroke width increases, the effect is

Stroke width is large

2. Outer stroke

Idea: in the slice shader, process the pixel P and sample the upper, lower, left and right pixels of P (use a variable width to control the stroke width, that is, process the pixels up, lower, left and right). If P itself is a transparent pixel, then

• If there are non transparent pixels up, down, left and right, the current pixel P returns the stroke color
• If the top, bottom, left and right are transparent pixels, it can be returned to transparent

If P itself is not a transparent pixel, return 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
}
{
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
}
}
}
``````

External stroke effect:

Outside stroke effect

The outer stroke will not occupy the non transparent pixels of the picture, but there should be enough transparent pixels around the picture. When the outer stroke width is adjusted, the effect is as follows:

Stroke width is large
• When the stroke width is large, there are smooth cuts on the top and left of the image, which is the range of the image itself

Hive built-in function summary

1. Related help operation functions View built-in functions: Show functions; Display function details: desc function ABS; Display function extension information: desc function extended concat; 2. Learn the ultimate mental method of built-in function Step 1: carefully read all the functions of the show functions command to establish an overall understanding and impression Step 2: use […]