will do
will do

Reputation: 1

(UNITY, SHADER)Can you draw the same texture multiple times in a single pass?

I'm new to shaders, and I'm trying to make an outline of a tk2d spine object(a 2d character). I know there are other ways to obtain the outline effect, but this approach got me curious. (Please don't suggest another way to draw outlines, respectfully, because that is not my question.)

So basically how I'm trying to get the outline effect is by getting the vertexes of my object, input a color, and draw it 8 directions(top, down, left, right, 4 diagonal directions) by setting offsets to each direction.

I got this working, and it looks fine, but my shader code doesn't, because I have a total of 9 passes, 8 of which do the exact same thing(draw the same texture), with the only difference being the offset direction, and the last pass drawing the tk2d spine character. I don't like that I have 8 almost-exactly repeated codes, it seems to be a waste because there is an area where the same calculation is done 8 times, and I suppose performance would be affected as well.

So, my question is : can I compress the 8 passes into one? i.e. saving the vertex position for each direction, and passing that information to the fragment shader.

I tried upscaling the vertexes, but this didn't get me the effect I wanted, because the characters do not have smooth edges, and therefore the "outline" became rigged.

this is my current code:

Shader "Custom/Shader_Spine_Battle_Red" 
{
   Properties 
   {
      
      _Color ("Color", color) = (1, 0, 0, 1)
      _MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
      _OutlineWidth ("OutlineWidth", float) = 1
   }
   
   SubShader
   {
      Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
      ZWrite Off Lighting Off Cull Off Fog { Mode Off } Blend SrcAlpha OneMinusSrcAlpha
      LOD 110
      
      Stencil{
         Comp Equal
         }
      
      Pass 
      {
         CGPROGRAM
         #pragma vertex vert_vct
         #pragma fragment frag_mult 
         #pragma fragmentoption ARB_precision_hint_fastest
         #include "UnityCG.cginc"

         sampler2D _MainTex;
         fixed4 _Color;
         float _OutlineWidth;

         struct vin_vct 
         {
            float4 vertex : POSITION;
            float4 color : COLOR;
            float2 texcoord : TEXCOORD0;
         };

         struct v2f_vct
         {
            float4 vertex : SV_POSITION;
            fixed4 color : COLOR;
            float2 texcoord : TEXCOORD0;
         };

         v2f_vct vert_vct(vin_vct v)
         {
            v2f_vct o;

            // the only difference in the 8 passes is the two lines below
            v.vertex.x += _OutlineWidth * 0.1;
            v.vertex.y += _OutlineWidth * 0.1;
            o.vertex = UnityObjectToClipPos(v.vertex);
            o.color = _Color;
            o.texcoord = v.texcoord;
            return o;
         }

         fixed4 frag_mult(v2f_vct i) : SV_Target
         {
            fixed4 col = tex2D(_MainTex, i.texcoord);
            _Color.a *= ceil(col.a);
            col.rgb = _Color;
            col.a *= _Color.a;
            return col;
         }
         
         ENDCG
      }
      // (x 8 with only the rows mentioned above different

Pass { CGPROGRAM #pragma vertex vert_vct #pragma fragment frag_mult #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc"

     sampler2D _MainTex;
     fixed4 _Color;
     float _OutlineWidth;

     struct vin_vct 
     {
        float4 vertex : POSITION;
        float4 color : COLOR;
        float2 texcoord : TEXCOORD0;
     };

     struct v2f_vct
     {
        float4 vertex : SV_POSITION;
        fixed4 color : COLOR;
        float2 texcoord : TEXCOORD0;
     };

     v2f_vct vert_vct(vin_vct v)
     {
        v2f_vct o;

        o.vertex = UnityObjectToClipPos(v.vertex);
        o.color = v.color;
        o.texcoord = v.texcoord;
        return o;
     }

     fixed4 frag_mult(v2f_vct i) : SV_Target
     {
        fixed4 col = tex2D(_MainTex, i.texcoord) * i.color;
        return col;
     }
     
     ENDCG
  } 

} }




Upvotes: 0

Views: 542

Answers (1)

rain
rain

Reputation: 1

If you want to approach this by storing multiple vertex data, then this could be done either on the CPU side constructing the mesh with different UV offsets or GPU side by using what is called a geometry shader. You could run however into sorting issues with the base color, and personally I would not recommend that solution. If you really need to do it this way for some reason, this way exists.

What I could recommend is performing this operation on a per-fragment basis rather than in multiple passes and sampling it multiple times as your title question mentioned. This has the limitation of being limited to the mesh of your sprite so keep in mind that you might need to alter it to fit. This can be either by adding some whitespace and changing import settings (Mesh Type, Extrude Edges). If it's a wide outline a modification of uv mapping in fragment shader might be in order to ensure it always fits.

        static const float2 _dirs[8] = {
            float2(1,0),
            float2(-1,0),
            float2(0,-1),
            float2(0,1),
            float2(-1,1),
            float2(-1,-1),
            float2(1,1),
            float2(1,1)
        };

        fixed4 frag(v2f IN) : SV_Target
        {
            fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color; //original color
            fixed a = c.a; //original alpha
            for(int i = 0; i < 8; i++){
                fixed4 o = tex2D(_MainTex, IN.texcoord + _dirs[i] * _OutlineWidth); //sample in this direction
                c.a = max(c.a, o.a); //set the greater alpha
                c.rgb = (a * c.rgb) + ((1-a) * _OutlineColor); //mix original color with outline one based on original alpha
            }              
            c.rgb *= c.a;
            return c;
        }

Attached code is of fragment shader with variables for directions sampled. The for loop iterates over the directions and overrides the alpha value if higher and mixes the original and outline color based on original alpha value.

Upvotes: 0

Related Questions