Problem with GrabPass and screenPos

Hello, Unity Community!
Brace yourselves for my first question on this forum :stuck_out_tongue:

I’m trying to write a shader that, for every pixel it wants to draw, checks what color is already there. If that color is the same as the color I’ve specified in a property, the pixel is drawn, otherwise it is discarded.

It may sound like a strange thing to do, but it’s the only way I can come up with to what I need. Shouldn’t this work?

I should add that I have “Queue”=“Geometry-2” on the shader drawing the colors I want to replace to make sure this is rendered after, and what’s happening is that ‘_FillColor’ is drawn all over, apparently ignoring the colors in _GrabTexture.

Shader "Custom/Clipping Cap" {
    Properties {
        _ReplaceColor ("Replace Color", Color) = (1,0,1,1)
        _FillColor ("Fill Color", Color) = (0,0,0,1)
    }
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Geometry-1"}
        LOD 200

        GrabPass { }

        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _GrabTexture;
        half4 _ReplaceColor;
        half4 _FillColor;

        struct Input {
            float4 screenPos;
        };

        void surf (Input IN, inout SurfaceOutput o) {

            float2 screenUV = IN.screenPos.xy / IN.screenPos.w;

            half4 c = tex2D (_GrabTexture, screenUV);

            if(c.r  != _ReplaceColor.r || c.g != _ReplaceColor.g || c.b != _ReplaceColor.b)
                discard;

            o.Emission = _FillColor.rgb;
            o.Alpha = 1;
        }
        ENDCG
    } 
    FallBack "Diffuse"
}

A few things are wrong with your shader:

  • Geometry-2 draws before geometry, whereas you probably want it to draw after there’s some colour already there.
  • You should put a bit of tolerance in your discard condition

I did these things because I was curious to see how it looked:

Shader "Custom/Clipping Cap" {
	Properties {
		_ReplaceColor ("Replace Color", Color) = (1,0,1,1)
		_FillColor ("Fill Color", Color) = (0,0,0,1)
	}
	SubShader {
		Tags { "RenderType"="Opaque" "Queue"="Geometry+1"}
		LOD 200
		
		GrabPass { }
		
		CGPROGRAM
		#pragma surface surf Lambert
		
		sampler2D _GrabTexture;
		half4 _ReplaceColor;
		half4 _FillColor;
		
		struct Input {
			float4 screenPos;
		};
		
		void surf (Input IN, inout SurfaceOutput o) {
			
			float2 uv = IN.screenPos.xy / IN.screenPos.w;
			half4 c = tex2D (_GrabTexture, uv);
			
			half3 difference = c.rgb  - _ReplaceColor.rgb;
			
			difference = step(0.05, abs(difference));
			
			if (any(difference)) {
				discard;
			}
			
			o.Emission = _FillColor.rgb;
			
			o.Alpha = 1;
		}
		ENDCG
	} 
	FallBack "Diffuse"
}

Hmm, still not working here.

I’m sure you are right about the tolerance thing, but the reason I had Geometry-1 here, is that I had Geometry-2 on the object I want to draw before this one, and I want them both to render before all other solid geometry. I think I’ll need to explain what I’m trying to do a little better.

Basically, I want to create a cross-section of my terrain, that can be controlled in real-time, to allow looking into buildings and underground structures from a birds-eye-view. I have a shader for all the geometry making up the terrain, that discards any pixels above a certain worldPos.y value, and renders backfaces in a solid color. This does the cross-section nicely, but if there are any objects intersecting the terrain, it gets ugly.

So I had an idea for creating a cap, or lid, on the cross-section which goes like this. First, I’ll render the backfaces in a color I’ll never use for anything else - bright pink - and then I’ll add a large plane, covering the entire terrain, which is always positioned at the height of the cross section and uses the shader above. Meaning it should only render where the bright pink of the backfaces shows though. But, alas, it still renders the entire plane.

Did you test it with your fixes above? Did it work for you?

I did test it, and was able to get arbitrary colours from behind an object replaced where they were seen through the object.

That’s odd. I still can’t get it to work on my end. And, strangely, I just noticed the object still renders black, even if I set a different _FillColor.

I don’t understand what’s going on.

Seems there is some issue with GrabPass on certain systems…
http://forum.unity3d.com/threads/166244-GrabPass-doesen-t-work-correctly-in-Unity-4?p=1182933