How is gl_FragCoord.z computed - depth calc prob

I am currently trying to implement a kind of soft particle rendering. To do that I first render the opaque geometry, then call glFlush() and copy the depth buffer to a texture, then render the particles. To achieve the depth blending, I compare each particle fragment’s z value to the current depth value at the fragment’s screen position. If the fragment is in front of any opaque geometry, it gets rendered. If it’s behind it gets culled or blended depending on distance from the opaque fragment that had been rendered to that position.

It is even the case that some particle shining very softly through some opaque geometry in front of it starts to shine through stronger the further away the viewer gets.

This doesn’t work as intended though: Far away particles shine through opaque geometry far in front of them. It looks like z coordinates get “compressed”, the more the further away from the viewer (or computed using a non-linear function).

To check this, I had the soft blending shader paint the particle pixels depending on their z values. I found out that almost all particles have gl_FragCoord.z values > 0.99, and only those right in front of the viewer have 0.5 < gl_FragCoord.z <= 0.99. There were none with gl_FragCoord.z <= 0.5, although I would have expected gl_FragCoord.z to be almost 0 for those very close to the viewer.

I am using this call ahead of rendering everything:

gluPerspective (90.0, 4.0 / 3.0, 1.0, 5000.0);

(90 deg FOV, 4:3 aspect, z-near 1.0, z-far 5000.0).

This is the fragment shader:

uniform sampler2D partTex;
uniform sampler2D depthTex;
uniform float depthScale;
uniform vec2 screenScale;
uniform float dMax;
void main (void) {
float z = texture2D (depthTex, screenScale * gl_FragCoord.xy).r;
float dz = (gl_FragCoord.z * gl_FragCoord.z - z * z) * depthScale;
dz = clamp (dz, 0.0, dMax);
dz = (dMax - dz) / dMax;
gl_FragColor = texture2D (partTex, gl_TexCoord [0].xy) * gl_Color * dz;

depthScale = (float) (ZFAR - ZNEAR);
screenScale.x = 1.0f / (float) screenWidth;
screenScale.y = 1.0f / (float) screenHeight;

dMax is the max. distance at which a particle will still shine through from behind opaque geometry. The particle fragment’s alpha value depends on the relation of dMax and its distance from geometry in front of it.

The shader does already some re-scaling of depth values by squaring them, but that still doesn’t work perfectly.

Where is my error, what do I have to do to get this working properly?

Thanks, nice article. But where do I get w, wnear and wfar from? (In my coordinates, w is always 1).

The more I read that article, the less I understand it. It starts with z, znear and zfar and then jumps to w, wnear and wfar w/o explaining where these values come from. Apparently w also isn’t constant, so what does it depend on?


After pondering some more on the article, I have come up with the following (provided w = 1):

zHomogenous = zFar / (zFar - zDepthBuffer * (zFar - zNear));


w is not 1 after perspective transformation for example (just look at the glFrustum spec). But w does not change for transformations like translation, rotation or scale.

But, I am like karx11erx, where the hell these wnear/wfar come from? The article is not clear, it just present some facts but demonstrate nothing. I would also be glad to find a comprehensive article about this dark thing that is the depth buffer.
I think ZbuffeR, you are the best person to ask for! :smiley:

My above formula seems to work well enough. Where should I get a w from in a fragment shader only having the depth buffer that has somehow been computed by a transformation?

I’m sorry this article didn’t help you but that was not its purpose (the original goal was to simply explain in graphical terms where the non linearity of zbuffer values came from and how to choose near and far clip planes, that’s all).

There’s an interesting discussion going on there about your problem :

PS : pour les français du thread, il y a aussi l’article sur le ZBuffer en français :
Le depth buffer ou tampon de profondeur - Ce qu’il faut savoir