How can i get the world coordinates in a GLSL pixel shader by using the depth value?

Started by
10 comments, last by nullsquared 15 years, 9 months ago
How can i get the world coordinates in a GLSL pixel shader by using the depth value?
Advertisement
Vary the world coordinates of your vertices?

// Vertex shadervarying vec4 worldCoord;void main(void){    worldCoord = gl_ModelViewMatrix * gl_Vertex;    ...}
Quote:Original post by biggoron
Vary the world coordinates of your vertices?

// Vertex shadervarying vec4 worldCoord;void main(void){    worldCoord = gl_ModelViewMatrix * gl_Vertex;    ...}



no... i want to get the world coordinate of each pixel to perform lighting and some other opertions on them.

i know this is possible by using the window position of the pixel and depth value, but i don't know how...
Quote:no... i want to get the world coordinate of each pixel

Guess what varying each vertex's world coordinate between its fragments does :)
Quote:Original post by biggoron
Quote:no... i want to get the world coordinate of each pixel

Guess what varying each vertex's world coordinate between its fragments does :)


i'm not exactly sure what you mean. you gave me a line of vertex shader that transforms the vertex...


What I want in my pixel shader, is to be able to get the actual world coordinate of that specific pixel to use it in lighting and other effects.

I read somewhere that I need the window coordinate and depth value of the pixel to do this.
You are correct in thinking you can reconstruct the world coordinates from screen position and depth (i actually clicked this thread with hopes of finding out how). But it is not entirely necessary.

As mentioned before, using a *Varying* variable will get the job done as well. The only time you need to use your method is when reconstructing the position from a depth map or something (deferred shading).

The important line that you missed in BigGoron's code is
"varying vec4 worldCoord;"

This is a kind of communication between the vertex and pixel shaders. worldCoord will be available in the pixel shader and its value will be interpolated between its corresponding values for each of the face's vertices.

It's the same system (varying) used to interpolate the texture coordinates, the normal, and every other vertex attribute across the face, use it if you can.
aha, i see...

thanks, i didn't know it was that simple :)
Quote:Original post by bzroom
You are correct in thinking you can reconstruct the world coordinates from screen position and depth (i actually clicked this thread with hopes of finding out how).

There are several methods to do it. The main two ones are either by reverse raycasting or by simple matrix math. They both give the same results (minus floating point rounding errors), and performance depends on your situation (especially in what coordsys you need your results to be).

I'll quickly outline the second method. The idea is to reverse the screenspace remapping, the perspective divide, the projection transform and the view transform. All that in the pixel shader, as fast as possible. While that seems a lot to do, it's actually very simple, because almost all operations can be concatenated in a single matrix beforehand.

First, get the current screenspace coordinates of the pixel under consideration. Note that gl_FragCoord returns values in viewport range. We first need to remap them to [0..1], so that we can use them to index the depth texture. We later need to remap them to [-1..1] range for the matrix math.
vec2 screen = (vec2(gl_FragCoord.x, gl_FragCoord.y) - viewport.xy) * viewport.zw;

Here, viewport.xy contain the lower-left offset of your viewport (usually 0,0), and zw contains the reciprocal of your screen size. Look at the values you supplied to glViewport for reference.

Next, get the depth from the depth buffer at the position of our current fragment. I assume a correctly set up and bound depth texture. This will return a depth value in the 0..1 range. Again, we will later need to remap this to [-1..1]:
float depth = texture2D(DepthTexture, screen).x;


Now comes to magic. We build a homogeneous vector from our normalized screenspace coordinates, and multiply it with the inverse transform matrix IM. Look down below on how to construct it:
vec4 world = vec4(screen, depth, 1.0) * IM;


Finally, we need to undo the perspective divide, by dividing our vector by its w component. See it as a 'dehomogenisation' ;)
world.xyz /= world.w;

world.xyz now contains the worldspace position of the current fragment.

Now, what remains is that IM matrix. Essentially, what we need is a matrix that first remaps our [0..1] coordinates to [-1..1], then undos the projection, and finally undos the view transform (camera matrix).

Assuming column major order, the matrix is constructed as follows:

IM = inverse(ProjectionMatrix * CameraMatrix) * RemapMatrix;

RemapMatrix is a simple scale + translate matrix, that will remap an input vector from [0..1] to [-1..1] range.
Quote:Original post by Yann L
Quote:Original post by bzroom
You are correct in thinking you can reconstruct the world coordinates from screen position and depth (i actually clicked this thread with hopes of finding out how).

There are several methods to do it. The main two ones are either by reverse raycasting or by simple matrix math.

I'll cover the first one, which is extremely easy.

1) find the 4 view-space corners of the camera frustum (the 4 corners of the visible far plane) - this is application specific, but if you're using a render library (like Ogre [grin]), changes are you'll have something like Camera::getFrustumCorners() [wink]

2) pass in these 4 corners as normals/texcoords/tangents/etc. to the corresponding 4 vertices of your screen-space quad

3) interpolate this value from the quad vertex shader down to the pixel shader using a texcoord

4) normalize it there, and multiply by your depth to find the view space position. if you want the world-space position, multiply this value by the inverse of your view matrix

(depth should be the distance from the fragment to the camera, that is, the length of the view-space fragment position - you do not want post-projection depth in this case)

There's another way to do it, by using the .z coordinate of the fragment instead of its length, MJP will probably cover that [grin]. But both versions result in practically the same math.
Yann L, thanks for the amazing explination. That should surely be converted to reference material in the wiki or something.

agi_shi, thanks also, ..but that was a tuff post to follow.

edit:

Just to make sure, both of your methods will work with both the actual geometry, and reconstruction during a deferred pass correct?

As long as your input matrices and viewport dimensions are for the correct view. In deferred this would be the view used to produce the G Buffer, and for forward shading it would just be the current view.

Please correct me if I am wrong.

This topic is closed to new replies.

Advertisement