I'm having trouble understanding the behaviour of my OpenGL program; and in drawing my Z-Buffer out to the screen.

It will probably be best to just start with code, here is my GLSL shader:

in vec3 vVertex;
smooth out vec4 vSmoothColor;

vec4 vPosition;

uniform mat4 MVP;

void main() {
    vPosition = MVP * vec4(vVertex, 1);

    float z = abs(vPosition.z);
    vSmoothColor = vec4(z, z, z, 1);

    gl_Position = vPosition;

My assumption was thus, after reading completely through the OpenGL SuperBible (blue book) and several other resources; I was under the impression the vPosition.z above should be mapped to [-1, 1]; which is then clamped to [0, 1] by the driver (assumedly).

My results were different, I expected; while using vSmoothColor as my output color; and a box moving along the Z axis from 1.f to 100.f, and the perspective matrix set up with a near and far of 1.f and 100.f, that my colors should have corresponded to the following (assuming Z mapped to [0, 1]):

Z Coord.    vSmoothColor
10          0.1, 0.1, 0.1, 1
20          0.2, 0.2, 0.2, 1
80          0.8, 0.8, 0.8, 1

Instead, my color values (ie my clipspace coordinate "vPosition.z") corresponded to almost exactly the world space Z coordinate value:

Z Coord.    vSmoothColor
10          10, 10, 10, 1
20          20, 20, 20, 1
80          80, 80, 80, 1

If that is the result in vPosition.z (ie the clipspace Z), how does the driver clamp the depth values for representation?

Although its been verified as correct against other implementations, here is my perspective matrix creation code:

inline Matrix4 perspective(float fovy, float aspect, float n, float f) {
const float t = n * tanf(radians(fovy / 2));
const float r = t * aspect;

const float d = f - n;

return Matrix4(
    n / r, 0, 0, 0,
    0, n / t, 0, 0,
    0, 0, -(f + n) / d, -1,
    0, 0, -(f * n * 2) / d, 0


Ref: http://www.songho.ca/opengl/gl_projectionmatrix.html


You haven't done the division by w. Also, the [-1, 1] range after projection needs to be linearly remapped to [0, 1], not clipped. Try this:

vec4 vPosition= MVP * vec4(vVertex, 1);
vec3 vPositionDivided = vPosition.xyz / vPosition.w;

float z = vPositionDivided.z * 0.5 + 0.5;
vSmoothColor = vec4(z, z, z, 1);

gl_Position = vPosition;

Finally, Z is not mapped linearly in a perspective projection, so you're not going to get a values like Z = 20 -> vSmoothColor = 0.2. Instead you'll find that more of the values are concentrated close to the near plane. If you want to get linear depth, use the Z in camera space before multiplying by the projection matrix.

  • \$\begingroup\$ I wasnt aiming for the linearity, just didnt understand why Z was relatively unchanged. Of course! I forgot all about w. Cheers. \$\endgroup\$ – deceleratedcaviar Oct 22 '11 at 7:11
  • \$\begingroup\$ Using the code directly did not produce what I was after, but you have definitely raised an issue. I think a linear gradient (z_far / vPosition.z) will be sufficient for what I need at the moment. \$\endgroup\$ – deceleratedcaviar Oct 24 '11 at 2:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.