World coordinates from depth buffer (gdshader)

Godot Version

4.7

Question

Hi,

I cannot for the LIFE of me figure out how to make a proper world coordinate fragment shader in gdshader. I found a decent amount of posts on the topic in Godot forums, but I’ve tested a bunch of them (they all use variations on the same basic code) and none of them work??

Basically, a world coordinate depth buffer shader should take every pixel of the depth buffer (representing every camera hit on opaque surfaces) and return an xyz for that surface. This should theoretically create a camera-independent colouring, where–other than the change in visible surface with the camera angle–the same point in 3d space on a surface when viewed from different camera angles has the same xyz.

However, no matter what I do, the world position seems to be based around the camera orientation, causing the surface colour (debug visualization) to dramatically change seemingly inconsistently from the world xyz.

I’ve tried a bunch of visualizations of 3D world space too so don’t get too hung up on that part of the code, Ill know it is consistent when it consistently paints the same point on a surface the same colour from distinct angles.

I hope I’m conveying this well. For context, this is for a custom fog volume material that is compatible with the compatibility render pipeline (FogVolumes normally only supported by Forward+)

Here is the basic code Im using:

```gdshader

// Write depth buffer to depth_tex

uniform sampler2D depth_tex : hint_depth_texture, repeat_disable, filter_nearest;

// Called for every pixel the material is visible on.

// TODO: Support for translucent surfaces

void fragment() {

// Get pixel position in view-space coordinates (local/camera relative)

float depth = textureLod(depth_tex, SCREEN_UV, 0.0).r;

vec3 ndc = vec3(SCREEN_UV \* 2.0 - 1.0, depth);

vec4 view_pos = INV_PROJECTION_MATRIX \* vec4(ndc, 1.0);

view_pos.xyz /= view_pos.w;



vec3 world_pos = (INV_VIEW_MATRIX \* view_pos).xyz;



ALBEDO = fract(world_pos);

//ALBEDO = vec3(

    //log(length(world_pos.x)) \* 10.0 - 12.0,

    //log(length(world_pos.y)) \* 10.0 - 12.0,

    //log(length(world_pos.z)) \* 10.0 - 12.0

//);

}

```

That is a complaint my friend, not a question :slightly_smiling_face: What is it exactly that you are looking for?

There is an official tutorial at Advanced post-processing — Godot Engine (stable) documentation in English
If that does not do it then you need to go in and debug the exact situation you have - input known data into the shader and check that transformations produce the values it should after each step. One of them will not be correct and that is how you know what part is wrong. Or all of them might be correct then you know it is just a visualization issue.

I wish it were just a complaint… but no, there is something genuinely wrong with my world coordinates from depth buffer code.

While I did already read the tutorial you provided, I am lucky you sent that link as it seems I didnt catch the difference in calculation of normalized device coordinates for the compatibility renderer on first read.

For anyone in the future reading the issue is that the compatibility renderer specifically uses vec3 ndc = vec3(SCREEN_UV, depth) * 2.0 - 1.0; instead of the normal vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth);

Also confusing: doing the local perspective correction via view_pos.xyz /= view_pos.w is NOT the same as doing perspective, and you cannot correct for the view_pos after. You must do:
vec4 world = INV_VIEW_MATRIX * INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
vec3 world_pos = world.xyz / world.w;

So thank you for the accidental solve I guess? Sometimes you just get lucky with these problems that have been bugging you for days.