Intersection Shader, cull front minus cull back?

I’m trying to create a shader which will ultimately create a greyscale mask for a (desaturate) post processing effect. I’m completely new to shaders so sorry about the huge gaps in my knowledge.
This is my progress so far, I want just the bits in dark green.

I’m doing 2 passes with ZTest Greater, the first pass with Cull Front (dark green) and then Cull Back (light green).
How do I remove the (light green) pixels from rendering on the 2nd pass?

I tried calling “discard;” but that didn’t work.
Please pick this to pieces if I’m doing it all wrong!

Shader "Spakment/Intersect/TestShaderTwoPass"
{
    Properties
    {
        [HDR] _Color("Color", Color) = (1,1,1,1)
        [HDR] _ColorTint("ColorTint", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "Geometry+10" }
        LOD 100
        ZWrite Off

        Pass
        {
            Cull Front
            ZTest Greater
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
          
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };


            float4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                return _Color;
            }

          
            ENDCG
        }


        Pass
        {
            Cull Back
            ZTest Greater
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
          
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };


            float4 _ColorTint;


            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                //discard;
                return _ColorTint;
            }

          
            ENDCG
        }
    }
}

Just don’t do the second pass?

I’m guessing the issue is where the green pixels are you want the original image to remain untouched, and that second pass is hiding some dark green areas, correct? You should look into using stencils then. You’ll want to do the “light green” pass first, but have it write to a stencil value, and use ColorMask 0 (which will prevent it from writing to the color, only to the depth and/or stencil buffers). Then render the “dark green” pass with a stencil test to only render where the first pass did not write.

@bgolus - thank you for this, you were correct the light green was covering the dark green behind it.

I had an idea I needed the stencil but the re-ordering of the passes was catching me out. I was confusing myself, your reply got me out of going down the wrong route.

Also thanks for you responses on here Stencil Shader with Depth as they helped me out stop the stencil from other objects clashing with each other.

It worked!

This is the current working shader - does this seem a sensible (performant) way to achieve the stencil clearing?
Not sure if there’s any way to reduce down the 3 passes?

Shader "Beanpug/Intersect/PassesWithStencil"
{
    Properties
    {
        [HDR] _Color("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        //Rendertype: Opaque, Transparent or Overlay all give same result...
        //Queue is important here! Must be over 2500 to get front to back rendering, Overlay = 4000 (Transparent also works..)
        Tags { "RenderType"="Opaque" "Queue" = "Overlay" }
        LOD 100
        ZWrite Off
        //first pass sets the mask for the "front geometry"
        Pass
        {
            Cull Back
            ZTest Greater
            ColorMask 0

            Stencil {
                Ref 1
                Comp Always
                Pass Replace
            }

        }
        //second pass turn off culling, could be Cull Off or Cull Front (both acheive the same thing)
        //use the mask from the first pass and draw the color
        Pass
        {
            Cull Off
            ZTest Greater

            Stencil {
                Ref 1
                Comp NotEqual
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };


            float4 _Color;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                return _Color; // just return it
            }
            ENDCG
        }
// reset the stencil buffer so other objects dont mask out this one
        Pass
        {
            //Cull Back
            ZTest Greater
            ColorMask 0

            Stencil {
                Ref 0
                Comp Always
                Pass Zero
            }

        }
    }
}

Any ideas how to make it work in URP?

As I understand it, anything that requires multiple passes is a bit of a problem in URP, or just impossible.

Found some info on multiple passes https://discussions.unity.com/t/784755

After going through that thread I feel dumber, and as though I know less, not more, than before I read it.

Haha, yeah I get the feeling.

Anyway I found this topic https://forum.unity.com/threads/lwrp-and-multipass-effect-using-scriptablerenderpass.580498/#post-4524073

which says that multiple passes are done with adding RenderObjects

Also this issue tracker states it is “by design” to only render the first pass https://issuetracker.unity3d.com/issues/lwrp-only-first-pass-gets-rendered-when-using-multi-pass-shaders

The HDRP makes explicit use of multi-pass shaders for transparency, but it’s a feature of that specific SRP which has multiple different "LightMode" passes that individually will only be rendered once.

Multi-pass shaders like what’s usually done for stencils with multiple unlit passes are explicitly not supported by either SRP. Render Objects aka Render Features have been pushed as “you use these now”, but they’re woefully insufficient for more complex setups you might need / want for things like stencils. Using multiple materials or multiple render components is unfortunately the only option left for the included SRPs when you need more fine control for when passes render / how they’re sorted.

1 Like

I would like to highlight geometry intersections like in the last screenshot or like in this example http://answers.unity.com/answers/1487831/view.html

@bgolus so are you saying it is doable but requires multiple materials?

Yes. You’d want to use a separate material per-pass. Though if that’s the only thing you’re trying to do, then Render Features can work too.

1 Like

Without Vertex shaders?

Sorry if this is a stupid question. The last time I looked at Shader Graph and SRPs, it seemed like vertex shaders weren’t a thing in that world.

It is not exposed as vertex and fragment stage in older version.
Newer version of shader graph shows explictly like here’s screenshot

Shader Graph is just a vertex fragment shader generator. Almost all of Unity’s built in shaders for the SRPs are straight vertex fragment shaders and not Shader Graph based. There were a few in the past for the HDRP that started out as Shader Graphs that they’ve since converted to vertex fragment shaders.

Because vertex fragment shaders are inherently more powerful and customizable. Shader Graph shaders can only do the narrow range of things they’ve been designed to support.

Looking for a solution with shader graphs too when it comes to these intersections.

I managed to get this result using custom shader lab code

Shader:

Shader "Custom/Intersect/PassesWithStencil"
{
    Properties
    {
        [HDR] _Color("Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        //Rendertype: Opaque, Transparent or Overlay all give same result...
        //Queue is important here! Must be over 2500 to get front to back rendering, Overlay = 4000 (Transparent also works..)
        Tags
        {
            "RenderType"="Opaque" "Queue" = "Overlay"
        }
        LOD 100
        ZWrite Off
        //first pass sets the mask for the "front geometry"
        Pass
        {
            Cull Back
            ZTest Greater
            ColorMask 0

            Stencil
            {
                Ref 1
                Comp Always
                Pass Replace
            }

        }
        //second pass turn off culling, could be Cull Off or Cull Front (both acheive the same thing)
        //use the mask from the first pass and draw the color
        Pass
        {
            Cull Off
            ZTest Greater

            Stencil
            {
                Ref 1
                Comp NotEqual
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };


            float4 _Color;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                return _Color; // just return it
            }
            ENDCG
        }
        // reset the stencil buffer so other objects dont mask out this one
        Pass
        {
            //Cull Back
            ZTest Greater
            ColorMask 0

            Stencil
            {
                Ref 0
                Comp Always
                Pass Zero
            }

        }
    }
}

This is how far I’ve come using Universial Render Pipeline / URP:
8276556--1084629--upload_2022-7-14_6-58-2.png

Added 3 Rendering Features with various stencil and depth overrides to match the custom shader:
8276556--1084632--upload_2022-7-14_7-0-40.png

1: Depth on, Test Greater, Don’t write, Stencil 1 Always Replace
2. Depth on, Test Greater, Don’t write, Stencil 1 Not Equal Keep
3. Depth on, Test Greater, Don’t write, Stencil 0 Always Zero

I’m doing some experiments with culling in shader graph to maybe create several materials in case that is whats missing.

Just got it working with multiple materials!

8276637--1084635--upload_2022-7-14_7-53-29.png

Each pass in the custom shader code goes into a shader and being used by a material.

However, that intersect_pass2 is tied up with the old shader code way of doing things because it needs Stencil.

Hey

Can you share some more info on this? I dont understand your render feature settings exactly