Reading SubViewport texture in shader

Godot Version

v4.4.1.stable.official [49a5bc7b6]

Question

I’m trying to render a scene in different passes to isolate objects.

I have, in order inside the same SubViewportContainer, three different viewports:
the first one draws depth using a full screen quad, as there doesn’t seem to be any other way of accessing a viewport’s depth buffer
the second one renders part of the scene
the third one renders the rest and through another fullscreen quad takes the previous depth and color to produce the final scene.

But when I run the project, the textures appear as the black and magenta placeholder, instead of the render.


(image shows the second viewport’s texture as seen by the shader).

Why does this happen? Am I misunderstanding something about the rendering order?

If what I’m trying to do is not possible, is it possible to disable the cameras clearing the depth buffer? render_target_clear_mode seems to apply only to color.

Just so us readers have a clear picture of your setup, would it be possible to share the shader you’re using for the Compose and DepthReader quads as well as the script on the SubViewportContainer?

I feel inclined to help but being presented with this missing information makes it hard to ensure that my guidance will be applicable.

1 Like

Thanks, I actually managed to find and fix the issue before my post was approved, so couldn’t retract it. The problem was that I was assigning the Viewport’s texture to the material through the inspector, which doesn’t seem to work when running the project.

For reference I’ll still provide the shaders and the script (which I now edited to assign the texture on init).

The script:

func _ready() -> void:
	var root = get_tree().root
	$Environment.size = root.size
	$Entities.size = root.size
	$Depth.size = root.size
	
	$Depth/Camera3D.environment = $Environment/Camera3D.environment
	
	$Depth/Camera3D/DepthReader.visible = true
	$Entities/Camera3D/Compose.visible = true
	
	var mat: Material = $Entities/Camera3D/Compose.get_active_material(0)
	mat.set("shader_parameter/environment_color", $Environment.get_texture())
	mat.set("shader_parameter/environment_depth", $Depth.get_texture())

The DepthReader shader (probably incorrect as I haven’t checked if the color depth is enough to store depth):

shader_type spatial;
render_mode depth_draw_never, unshaded, fog_disabled;

uniform sampler2D depth_texture: hint_depth_texture, repeat_disable, filter_nearest;

void vertex() {
	POSITION = vec4(VERTEX.xy, 1.0, 1.0);
}

void fragment() {
	ALBEDO.r = texture(depth_texture, SCREEN_UV).r;
}

The Compose shader:

shader_type spatial;
render_mode depth_test_disabled, unshaded, fog_disabled;

uniform sampler2D environment_color: repeat_disable, filter_nearest;
uniform sampler2D environment_depth: repeat_disable, filter_nearest;
uniform sampler2D entities_color: hint_screen_texture, repeat_disable, filter_nearest;
uniform sampler2D entities_depth: hint_depth_texture, repeat_disable, filter_nearest;

void vertex() {
	POSITION = vec4(VERTEX.xy, 1.0, 1.0);
}

void fragment() {
	float curr_depth = texture(entities_depth, SCREEN_UV).r;
	float scene_depth = texture(environment_depth, SCREEN_UV).r;

	if (curr_depth > scene_depth) {
		ALBEDO = texture(entities_color, SCREEN_UV).rgb;
		DEPTH = curr_depth;
	} else {
		ALBEDO = texture(environment_color, SCREEN_UV).rgb;
		DEPTH = scene_depth;
	}
}
1 Like