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

inserte la descripción de la imagen aquí

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
inserte la descripción de la imagen aquí

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.
inserte la descripción de la imagen aquí

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"
}


Supongo que te gusta

Origin blog.csdn.net/aaa27987/article/details/131229746
Recomendado
Clasificación