GLSL: How to get pixel x,y,z world position?

Rookie picture Rookie · Feb 4, 2011 · Viewed 44.9k times · Source

I want to adjust the colors depending on which xyz position they are in the world.

I tried this in my fragment shader:

varying vec4 verpos;

void main(){
    vec4 c;
    c.x = verpos.x;
    c.y = verpos.y;
    c.z = verpos.z;
    c.w = 1.0;

    gl_FragColor = c;
}

but it seems that the colors change depending on my camera angle/position, how do i make the coords independent from my camera position/angle?

Heres my vertex shader:

varying vec4 verpos;

void main(){
    gl_Position = ftransform();
    verpos = gl_ModelViewMatrix*gl_Vertex;
}

Edit2: changed title, so i want world coords, not screen coords!

Edit3: added my full code

Answer

kynnysmatto picture kynnysmatto · Feb 4, 2011

In vertex shader you have gl_Vertex (or something else if you don't use fixed pipeline) which is the position of a vertex in model coordinates. Multiply the model matrix by gl_Vertex and you'll get the vertex position in world coordinates. Assign this to a varying variable, and then read its value in fragment shader and you'll get the position of the fragment in world coordinates.

Now the problem in this is that you don't necessarily have any model matrix if you use the default modelview matrix of OpenGL, which is a combination of both model and view matrices. I usually solve this problem by having two separate matrices instead of just one modelview matrix:

  1. model matrix (maps model coordinates to world coordinates), and
  2. view matrix (maps world coordinates to camera coordinates).

So just pass two different matrices to your vertex shader separately. You can do this by defining

uniform mat4 view_matrix;
uniform mat4 model_matrix;

In the beginning of your vertex shader. And then instead of ftransform(), say:

gl_Position = gl_ProjectionMatrix * view_matrix * model_matrix * gl_Vertex;

In the main program you must write values to both of these new matrices. First, to get the view matrix, do the camera transformations with glLoadIdentity(), glTranslate(), glRotate() or gluLookAt() or what ever you prefer as you would normally do, but then call glGetFloatv(GL_MODELVIEW_MATRIX, &array); in order to get the matrix data to an array. And secondly, in a similar way, to get the model matrix, also call glLoadIdentity(); and do the object transformations with glTranslate(), glRotate(), glScale() etc. and finally call glGetFloatv(GL_MODELVIEW_MATRIX, &array); to get the matrix data out of OpenGL, so you can send it to your vertex shader. Especially note that you need to call glLoadIdentity() before beginning to transform the object. Normally you would first transform the camera and then transform the object which would result in one matrix that does both the view and model functions. But because you're using separate matrices you need to reset the matrix after camera transformations with glLoadIdentity().

gl_FragCoord are the pixel coordinates and not world coordinates.