Reconstructing world coordinates from depth buffer and arbitrary view-projection matrix
Asked Answered
M

3

9

I'm trying to reconstruct 3D world coordinates from depth values in my deferred renderer, but I'm having a heck of a time. Most of the examples I find online assume a standard perspective transformation, but I don't want to make that assumption.

In my geometry pass vertex shader, I calculate gl_Position using:

gl_Position = wvpMatrix * vec4(vertexLocation, 1.0f);

and in my lighting pass fragment shader, I try to get the world coordinates using:

vec3 decodeLocation()
{
  vec4 clipSpaceLocation;
  clipSpaceLocation.xy = texcoord * 2.0f - 1.0f;
  clipSpaceLocation.z = texture(depthSampler, texcoord).r;
  clipSpaceLocation.w = 1.0f;
  vec4 homogenousLocation = viewProjectionInverseMatrix * clipSpaceLocation;
  return homogenousLocation.xyz / homogenousLocation.w;
}

I thought I had it right, and indeed, objects near the camera appear to be lit correctly. But I recently realized as I move further away, objects are lit as if they're further from the camera than they actually are. I've played around with my lighting pass and verified my world coordinates are the only thing being miscalculated.

I can't help but think my clipSpaceLocation.z and clipSpaceLocation.w are the source of the problem, but I've tried every variation I can think of to calculate them, and the above code results in the most-correct results.

Any ideas or suggestions?

Marteena answered 12/3, 2014 at 18:35 Comment(1)
how did you calculate your inverse view projection matrix? i'm having the issue with light space position grabbing and im taking the assumption its my inverse view projection matrix.Ruler
M
8

I only needed to make a tiny fix. The line:

clipSpaceLocation.z = texture(depthSampler, texcoord).r;

should read:

clipSpaceLocation.z = texture(depthSampler, texcoord).r * 2.0f - 1.0f;

The way I understand it, projection matrices are designed so they map the near and far planes to [-1,1], not [0,1] like I had always assumed. OpenGL then normalizes them to the range [0,1] (a.k.a. "Window Space"), so I needed to perform the inverse of that normalization.

This is all assuming glDepthRange(0, 1), which it is by default, and there's little reason to change it.

Marteena answered 12/3, 2014 at 20:30 Comment(2)
It has since OpenGL 2.1 (GLSL 1.20.8). See opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf Section 4.1.4Marteena
Even though your answer is more than a year "too late", you're right. Seems like it was only a problem because I forgot to put "#version xyz" in the shaders.Aquiculture
H
1

Your general approach is correct, you just did not invert the window space transform correctly. Window space z (which you probably wtrot into your depth texture) is [0,1] (by default, more general would be glDepthRange()), but NDC space z is [-1,1]. So you could change that line analogous to your x and y mappings to

clipSpaceLocation.z = texture(depthSampler, texcoord).r * 2.0 - 1.0;
Hematology answered 12/3, 2014 at 20:16 Comment(0)
A
1

See my answer on gamedev.stackexchange for a more efficient way to reconstruct world and view space positions:

https://gamedev.stackexchange.com/a/111885/24009

Aquiculture answered 30/11, 2015 at 5:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.