How to frustrum call objects in front of a plane for a single Camera3D

Godot Version

4.3

Question

My problem is exactly the same as this users question on reddit which has been left unanswered for 2 years.

I have implemented a “mirror” camera to render water reflections, i.e. a second camera that is mirrored, in relation to the main camera, below the water surface looking upwards. The effect works, except that the under water camera will of course render all objects below water, as well as tall objects sticking through the water, which is undesired.

Adjusting the near clipping plane seems like a no brainer, however since the camera is angled (in my case 45 degrees upwards), this does not work. It feels like manually specifying a culling box should do the trick, but I don’t know whether that’s possible.

Lastly, adding custom shaders to all objects to simply not render anything below water surface is also not great, because my above water cameras do want to render the full object.

I would change the reflection camera to exclude some layers, and change the objects to be on that specific “not_drawn_in_reflection” layer


EDIT: I just realized this would not work for things that are going through the water surface :grimacing:

Okay so this captivated me more than it should have :sweat_smile:

I replicated the situation and tried to mess around to see how it could be done and I came to this solution:

1- I replicated the scene as I think you built it

2- I added a plane, facing the “mirror” backwards, on render layer 3

3- I added a separate viewport and camera, which can see the blocker mesh

4- The “underflow” viewport therefore sees what should not be reflected:

5- I modified the reflection shader to subtract the image generated by the “underflow” viewport from the reflection viewport

shader_type spatial;

uniform sampler2D reflection_viewport_tex;
uniform sampler2D underflow_viewport_tex;
void fragment() {
	vec3 reflection_texture = texture(reflection_viewport_tex, vec2(1.0-SCREEN_UV.x, SCREEN_UV.y)).rgb;
	vec3 underflow_texture = texture(underflow_viewport_tex, vec2(1.0-SCREEN_UV.x, SCREEN_UV.y)).rgb;
	
	ALBEDO = reflection_texture - underflow_texture;
	ALPHA = .5;
}

The results are not perfect …

As you can see there is still some work to do, but I think this is the way to go about it. Modifying the underflow texture subtraction so that the remnant is just replaced with the water color would probably do the trick.

I put the whole thing on GitHub here:

1 Like

Have you tried using a custom clipping plane with a shader or via manual frustration culling? In some engines, you can define a specific cutting plane for a single camera without affecting the others. Otherwise, a separate rendering layer could help filter objects underwater.

Tutuapp

@vonpanda Thanks for sharing your interesting approach. Disabling “receive shadows” on the blocker_mesh helped a bit, but ultimately as you pointed out one would have to refine the shader a bit to get rid of the greyed out part that was subtracted. Also having to render the scene thrice is something to mull over :thinking:

@atinaa Afaik that’s not possible in Godot, but I’m new to Godot and happy to be proven wrong. I noticed you can change the frustrum offset on cameras when setting the projection mode to “Frustrum” but this something slightly different from defining a specific cutting plane.