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!
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.
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
}
}
}
}
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.
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.
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:
Added 3 Rendering Features with various stencil and depth overrides to match the custom shader:
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.