Search code examples
unity-game-enginegraphicsshadermultipass

Unity: Is it possible to use 2 passes consecutively?


I have a shader with multiple passes inside it, 2 of which are an outline pass and a blur pass. I am trying to create an outline and then blur it. How do I make the blur pass take in the output of the outline pass to blur?

Outline pass:

Pass{
        Name "OUTLINE"
        Cull Off
        ZWrite Off
        ZTest Always
        Blend SrcAlpha OneMinusSrcAlpha
    CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog

    fixed4 frag(v2f IN) : SV_Target
    {
        fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;

        if (c.a > _Cutoff) {
            float totalAlpha = 1.0;

            [unroll(16)]
            for (int i = 1; i < _OutlineSize + 1; i++) {
                fixed4 pixelUp = tex2D(_MainTex, IN.texcoord + fixed2(0, i * _MainTex_TexelSize.y));
                fixed4 pixelDown = tex2D(_MainTex, IN.texcoord - fixed2(0, i *  _MainTex_TexelSize.y));
                fixed4 pixelRight = tex2D(_MainTex, IN.texcoord + fixed2(i * _MainTex_TexelSize.x, 0));
                fixed4 pixelLeft = tex2D(_MainTex, IN.texcoord - fixed2(i * _MainTex_TexelSize.x, 0));

                totalAlpha = totalAlpha * pixelUp.a * pixelDown.a * pixelRight.a * pixelLeft.a;
            }

            if (totalAlpha == 0) {
                c.rgba = fixed4(0.4, 1, 1, 1);
            }
            else
                c.rgba = fixed4(0.5, 0.5, 0.5, .5);
        }

        c.rgb *= c.a;
        return c;
    }

    ENDCG
    }

Blur pass:

Pass{
        Name "BLUR"
        Cull Off
        ZWrite Off
        ZTest Always
        Blend SrcAlpha OneMinusSrcAlpha
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma multi_compile_fog

        fixed4 frag(v2f IN) : SV_Target
    {
        fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;

        if (c.a > _Cutoff) {
            fixed4 pixelUp = tex2D(_MainTex, IN.texcoord + fixed2(0, _MainTex_TexelSize.y));
            fixed4 pixelDown = tex2D(_MainTex, IN.texcoord - fixed2(0,  _MainTex_TexelSize.y));
            fixed4 pixelRight = tex2D(_MainTex, IN.texcoord + fixed2(_MainTex_TexelSize.x, 0));
            fixed4 pixelLeft = tex2D(_MainTex, IN.texcoord - fixed2(_MainTex_TexelSize.x, 0));
            fixed4 pixelUp2 = tex2D(_MainTex, IN.texcoord + fixed2(0, 2 * _MainTex_TexelSize.y));
            fixed4 pixelDown2 = tex2D(_MainTex, IN.texcoord - fixed2(0, 2 * _MainTex_TexelSize.y));
            fixed4 pixelRight2 = tex2D(_MainTex, IN.texcoord + fixed2(2 * _MainTex_TexelSize.x, 0));
            fixed4 pixelLeft2 = tex2D(_MainTex, IN.texcoord - fixed2(2 * _MainTex_TexelSize.x, 0));
            fixed4 pixelUp3 = tex2D(_MainTex, IN.texcoord + fixed2(0, 3 * _MainTex_TexelSize.y));
            fixed4 pixelDown3 = tex2D(_MainTex, IN.texcoord - fixed2(0, 3 * _MainTex_TexelSize.y));
            fixed4 pixelRight3 = tex2D(_MainTex, IN.texcoord + fixed2(3 * _MainTex_TexelSize.x, 0));
            fixed4 pixelLeft3 = tex2D(_MainTex, IN.texcoord - fixed2(3 * _MainTex_TexelSize.x, 0));

            c = c * 0.18
                + 0.15 * (pixelUp + pixelDown + pixelRight + pixelLeft)
                + 0.12 * (pixelUp2 + pixelDown2 + pixelRight2 + pixelLeft2);
                + 0.09 * (pixelUp3 + pixelDown3 + pixelRight3 + pixelLeft3);
        }

        c.rgb *= c.a;
        return c;
    }

        ENDCG
    }

I am new to shaders so if there is any advice would be appreciated.


Solution

  • Using a Grab pass, you can take the screen generated so far and sample from it in another pass.

    In your case, put it after the outline pass, and sample from the resulting texture in the blur pass instead of the raw texture.

    Pass{
        // ...
        // no change
        // ...
    }
    
    GrabPass { "_GrabTexture" }        
    
    Pass{ 
        // Note the changes from calling tex2D with _MainTex to _GrabTexture, 
        // matching the string in the GrabPass directive above 
    
        // ...
        // use a vertex shader to get the uvs of the screen grab
    
        struct v2f {
            float4 grabPos : TEXCOORD0;
            float4 pos : SV_POSITION;
        };
    
        v2f vert(appdata_base v) {
            v2f o;
            // use UnityObjectToClipPos from UnityCG.cginc to calculate 
            // the clip-space of the vertex
            o.pos = UnityObjectToClipPos(v.vertex);
            // use ComputeGrabScreenPos function from UnityCG.cginc
            // to get the correct texture coordinate
            o.grabPos = ComputeGrabScreenPos(o.pos);
            return o;
        }
    
        // Note the changes from calling tex2D with _MainTex to _GrabTexture, 
        // matching the string in the GrabPass directive above 
        // as well as IN.grabPos instead of IN.texcoord
        // and tex2Dproj instead of tex2D
    
        fixed4 frag(v2f IN) : SV_Target {
            fixed4 c = tex2Dproj(_GrabTexture, IN.grabPos) * IN.color; 
    
            if (c.a > _Cutoff) {
                fixed4 pixelUp = tex2Dproj(_GrabTexture, IN.grabPos+ fixed2(0, _MainTex_TexelSize.y));
                fixed4 pixelDown = tex2Dproj(_GrabTexture, IN.grabPos - fixed2(0,  _MainTex_TexelSize.y));
                fixed4 pixelRight = tex2Dproj(_GrabTexture, IN.texcoord + fixed2(_MainTex_TexelSize.x, 0));
                fixed4 pixelLeft = tex2Dproj(_GrabTexture, IN.texcoord - fixed2(_MainTex_TexelSize.x, 0));
                fixed4 pixelUp2 = tex2Dproj(_GrabTexture, IN.texcoord + fixed2(0, 2 * _MainTex_TexelSize.y));
                fixed4 pixelDown2 = tex2Dproj(_GrabTexture, IN.texcoord - fixed2(0, 2 * _MainTex_TexelSize.y));
                fixed4 pixelRight2 = tex2Dproj(_GrabTexture, IN.texcoord + fixed2(2 * _MainTex_TexelSize.x, 0));
                fixed4 pixelLeft2 = tex2Dproj(_GrabTexture, IN.texcoord - fixed2(2 * _MainTex_TexelSize.x, 0));
                fixed4 pixelUp3 = tex2Dproj(_GrabTexture, IN.texcoord + fixed2(0, 3 * _MainTex_TexelSize.y));
                fixed4 pixelDown3 = tex2Dproj(_GrabTexture, IN.texcoord - fixed2(0, 3 * _MainTex_TexelSize.y));
                fixed4 pixelRight3 = tex2Dproj(_GrabTexture, IN.texcoord + fixed2(3 * _MainTex_TexelSize.x, 0));
                fixed4 pixelLeft3 = tex2Dproj(_GrabTexture, IN.texcoord - fixed2(3 * _MainTex_TexelSize.x, 0));
    
                c = c * 0.18
                    + 0.15 * (pixelUp + pixelDown + pixelRight + pixelLeft)
                    + 0.12 * (pixelUp2 + pixelDown2 + pixelRight2 + pixelLeft2);
                    + 0.09 * (pixelUp3 + pixelDown3 + pixelRight3 + pixelLeft3);
            }
    
            c.rgb *= c.a;
            return c;
        }
    
            ENDCG
        }