Shader de trazo interno 2D de Unity
I. Introducción
Hoy, implementemos un efecto de trazo interno 2D. Trazo interior: Cambia los píxeles del borde al color del trazo, ocupando los píxeles originales.
Idea: Podemos lograr este efecto en el sombreador de fragmentos: cuando un píxel en sí mismo no es transparente (alfa>0), y el producto de los valores alfa de sus cuatro píxeles arriba, abajo, izquierda y derecha es igual a 0 , luego podemos determinar que el píxel está en el borde, simplemente conviértalo en el color del trazo.
Dos, contenido Shader
2.1 Sombreador inicial
Shader "Custom/2DInline"
{
Properties
{
_MainTex("MainTex",2D) = "white"{}
_InlineColor("InlineColor",Color) = (0,0,0,1)
_InlineWidth("InlineWidth",Range(0,10)) = 1
}
SubShader{
Tags { "Queue" = "Transparent" "RenderType" = "Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
float4 _InlineColor;
float _InlineWidth;
v2f vert(appdata a){
v2f v;
v.pos = UnityObjectToClipPos(a.vertex);
v.uv = TRANSFORM_TEX(a.uv,_MainTex);
return v;
}
fixed4 frag(v2f v) : SV_TARGET{
float4 col = tex2D(_MainTex,v.uv);
//获取周围上下左右4个点的uv
float2 up_uv = v.uv + float2(0,_InlineWidth * _MainTex_TexelSize.y);
float2 down_uv = v.uv + float2(0,-_InlineWidth * _MainTex_TexelSize.y);
float2 left_uv = v.uv + float2(-_InlineWidth * _MainTex_TexelSize.x,0);
float2 right_uv = v.uv + float2(_InlineWidth * _MainTex_TexelSize.x,0);
//根据uv,获取周围上下左右4个点的alpha乘积
float arroundAlpha = tex2D(_MainTex,up_uv).a *
tex2D(_MainTex,down_uv).a *
tex2D(_MainTex,left_uv).a *
tex2D(_MainTex,right_uv).a;
//让描边变色
float4 result = lerp(_InlineColor,col,arroundAlpha);
//使用原来的透明度
result.a = col.a;
return result;
}
ENDCG
}
}
FallBack "Sprites/Default"
}
2.2 Efecto
2.3 Análisis
Aquí no lo escribimos completamente de acuerdo con la idea, porque si lo escribimos de esa manera, necesitamos agregar una declaración if, y después de agregar if, todavía tenemos que optimizarlo.
Aquí hay un punto de conocimiento útil: _MainTex_TexelSize es una variable incorporada para obtener el ancho y la altura de la textura. El valor es: Vector4 (1 / ancho, 1 / alto, ancho, alto), usamos esta variable para ayudarnos a obtener el uv circundante
//Usamos este código para cambiar el color del trazo. Pero además, todos los píxeles transparentes también se convertirán en el color del trazo
float4 result = lerp(_InlineColor,col,arroundAlpha);
//Luego usamos el alfa original, para que los píxeles cubiertos por el código en la oración anterior continúen volviéndose transparentes
result.a = col.a;
2.4 Optimización
2.4.1 Resolver el problema del trazo cuando _InlineWidth es igual a 0
El efecto básicamente se completó arriba, pero hay un pequeño problema: cuando _InlineWidth = 0, no queremos tener un efecto de trazo, pero podemos ver que todavía hay un efecto de trazo. Esto se debe a que la transparencia de los píxeles circundantes es relativamente baja y el producto está cerca de 0, por lo que se convierte en el color del trazo.
Podemos usar la función de paso para resolver este problema, cuando _InlineWidth es pequeño, use el color original
//让描边变色
float4 result = lerp(_InlineColor,col,arroundAlpha);
//使用原来的透明度
result.a = col.a;
//上面的代码在_InlineWidth = 0时,仍然有一丝描边的颜色。这里控制一下
float threshold = step(0.000001,_InlineWidth);
result = lerp(col,result,threshold);
return result;
De esta forma, _InlineWidth = 0, no hay efecto de trazo
2.4.2 Aumentar el brillo
Debido a que el mejor trazo es el abultado, entonces sería mejor ajustar el brillo.
A continuación, simplemente agregamos la siguiente variable: _Light(“Light”,Range(1,5)) = 1
//让描边变色
float4 result = lerp(_InlineColor * _Light,col,arroundAlpha);
//使用原来的透明度
result.a = col.a;
Se puede ver el efecto de que el trazo es más evidente.
3. Código completo
En este punto, nuestro código está terminado. A continuación adjunta el código completo
Shader "Custom/2DInline"
{
Properties
{
_MainTex("MainTex",2D) = "white"{}
_InlineColor("InlineColor",Color) = (0,0,0,1)
_InlineWidth("InlineWidth",Range(0,10)) = 1
_Light("Light",Range(1,5)) = 1
}
SubShader{
Tags { "Queue" = "Transparent" "RenderType" = "Transparent"}
Blend SrcAlpha OneMinusSrcAlpha
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _MainTex_TexelSize;
float4 _InlineColor;
float _InlineWidth;
float _Light;
v2f vert(appdata a){
v2f v;
v.pos = UnityObjectToClipPos(a.vertex);
v.uv = TRANSFORM_TEX(a.uv,_MainTex);
return v;
}
fixed4 frag(v2f v) : SV_TARGET{
float4 col = tex2D(_MainTex,v.uv);
//获取周围上下左右4个点的uv
float2 up_uv = v.uv + float2(0,_InlineWidth * _MainTex_TexelSize.y);
float2 down_uv = v.uv + float2(0,-_InlineWidth * _MainTex_TexelSize.y);
float2 left_uv = v.uv + float2(-_InlineWidth * _MainTex_TexelSize.x,0);
float2 right_uv = v.uv + float2(_InlineWidth * _MainTex_TexelSize.x,0);
//根据uv,获取周围上下左右4个点的alpha乘积
float arroundAlpha = tex2D(_MainTex,up_uv).a *
tex2D(_MainTex,down_uv).a *
tex2D(_MainTex,left_uv).a *
tex2D(_MainTex,right_uv).a;
//让描边变色
float4 result = lerp(_InlineColor * _Light,col,arroundAlpha);
//使用原来的透明度
result.a = col.a;
//上面的代码在_InlineWidth = 0时,仍然有一丝描边的颜色。这里控制一下
float threshold = step(0.000001,_InlineWidth);
result = lerp(col,result,threshold);
return result;
}
ENDCG
}
}
FallBack "Sprites/Default"
}