Skip to content

Advanced post processing tutorial - Incorrect depth texture linearization for Vulkan #5276

@briansemrau

Description

@briansemrau

Godot version

4.0.dev (db028ac700)

System information

Windows 10, Vulkan (Clustered), RTX 2060

Issue description

As it stands, the advanced post processing tutorial suggests to use the following code to get the linear depth from the depth texture:

void fragment() {
  float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
  vec3 ndc = vec3(SCREEN_UV, depth) * 2.0 - 1.0;

  vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
  view.xyz /= view.w;
  float linear_depth = -view.z;
}

This was correct in Godot 3.x, but this is not longer true with Vulkan. (Tested with the clustered renderer, haven't tested mobile renderer.)

The correct code, as tested, is now:

void fragment() {
  float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
  vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth); // <<< This line changed!

  vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
  view.xyz /= view.w;
  float linear_depth = -view.z;
}

It looks like the depth texture in 3.x was mapped between 0.5 and 1.0 (seems very strange), while in 4.0 it is now mapped between 0.0 and 1.0, as I would normally expect.

I believe this is just a documentation issue, but it would be good to have someone familiar with the renderer give some assurance.

Steps to reproduce

This issue is best demonstrated by a post processing shader (screen filling mesh) to compare the normalized device coordinates by overlaying the world position of existing geometry as a color.

When working correctly, the colors should not move when you spin the camera. They should appear like a texture.

Code to showcase this:

shader_type spatial;
render_mode cull_disabled, unshaded;

void vertex() {
	POSITION = vec4(VERTEX, 0.0);
}

void fragment() {
	float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
	//vec3 ndc = vec3(SCREEN_UV, depth) * 2.0 - 1.0; // bad
	vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth); // good
	
	vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
	view.xyz /= view.w;
	float linear_depth = -view.z;
	
	vec4 world = CAMERA_MATRIX * INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
	vec3 world_position = world.xyz / world.w;
	
	if (linear_depth > 100.0) // just to prevent coloring the sky
		discard;
	ALBEDO = world_position;
}
Comparison between correct and incorrect results:

Good:
image

Bad:
image

Minimal reproduction project

DepthLinearizationReprod.zip

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions