2

I am drawing a stack of decals on a quad. Same geometry, different textures. Z-fighting is the obvious result. I cannot control the rendering order or use glPolygonoffset due to batched rendering. So I adjust depth values inside the vertex shader.

    gl_Position = uMVPMatrix * pos;
    gl_Position.z += aDepthLayer * uMinStep * gl_Position.w;

gl_Position holds clip coordinates. That means a change in z will move a vertex along its view ray and bring it to the front or push it to the back. For normalized device coordinates the clip coords get divided by gl_Position.w (=-Zclip). As a result the depth buffer does not have linear distribution and has higher resolution towards the near plane. By premultiplying gl_Position.w that should be fixed and I should be able to apply a flat amount (uMinStep) to the NDC.

That minimum step should be something like 1/(2^GL_DEPTH_BITS -1). Or, since NDC space goes from -1.0 to 1.0, it might have to be twice that amount. However it does not work with these values. The minStep is roughly 0.00000006 but it does not bring a texture to the front. Neither when I double that value. If I drop a zero (scale by 10), it works. (Yay, thats something!)

But it does not work evenly along the frustum. A value that brings a texture in front of another while the quad is close to the near plane does not necessarily do the same when the quad is close to the far plane. The same effect happens when I make the frustum deeper. I would expect that behaviour if I was changing eye coordinates, because of the nonlinear z-Buffer distribution. But it seems that premultiplying gl_Position.w is not enough to counter that.

  1. Am I missing some part of the transformations that happen to clip coords? Do I need to use a different formula in general? Do I have to include the depth range [0,1] somehow?

  2. Could the different behaviour along the frustum be a result of nonlinear floating point precision instead of nonlinear z-Buffer distribution? So maybe the calculation is correct, but the minStep just cannot be handled correctly by floats at some point in the pipeline?

  3. The general question: How do I calculate a z-Shift for gl_Position (clip coordinates) that will create a fixed change in the depth buffer later? How can I make sure that the z-Shift will bring one texture in front of another no matter where in the frustum the quad is placed?


Some material:
OpenGL depth buffer faq
https://www.opengl.org/archives/resources/faq/technical/depthbuffer.htm
Same with better readable formulas (but some typos, be careful)
https://www.opengl.org/wiki/Depth_Buffer_Precision
Calculation from eye coords to z-buffer. Most of that happens already when I multiply the projection matrix.
http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html
Explanation about the elements in the projection matrix that turn into the A and B parts in most depth buffer calculation formulas.
http://www.songho.ca/opengl/gl_projectionmatrix.html

J-S
  • 425
  • 3
  • 17
  • I think that indeed the problem with your approch is floating point precision. YoOu might be better off not modifying `z_clip` before the division, but directly applying a modification to `gl_FragCoord.z` by writing to `gl_FragDepth` in the fragement shader. Another issue with OpenGL's default clip volume convention is that it maps `z_ndc=0` (area of highest fp precision) somehwere in the middle of the viewing voulme. You might find [this article](https://developer.nvidia.com/content/depth-precision-visualized) also interesting. – derhass Oct 25 '15 at 15:07
  • Thanks. I have that article under my nose for a while already. ;) I cannot use gl_FragDepth because I need this function in GLES20 projects. Do you think precision would be better if I go the "long way" and apply a nonlinear z-shift to the eye coords before projection happens? What do you mean by "modify z_clip before divison"? That is exactly what I am doing. – J-S Oct 25 '15 at 15:08
  • Well, I wrote "better off **not** modiying [...] before division [and use the fragment shader instead]", so I'm aware that that is what you already do... ;) I wasn't aware that `gl_FragDepth` doesn't exist in GLES, so my advice is rather useless, though. Doing it in eye space vs. doing it in clip space will not result in a significant difference for the issue (since clip space and eye space are just linear transforms of each other), you'd just introduce the additional issue that the x and y position would slightly move. So I really don't have any good advice for you here, I'm sorry to say. – derhass Oct 25 '15 at 16:18
  • Jim Blinn explains the oddities and varying precision of depth test calculations in his May-June 1998 column IEEE Computer Graphics & Applications, republished in the book "Jim Blinn's Corner: Notation, Notation, Notation". Might help explain what is happening. General recommendation from the column is to increase near Z as much as possible. – Hugh Fisher Oct 26 '15 at 18:10
  • Thanks. Pushing the near plane out is recommended because the zBuffer does not have linear distribution. Its highest resolution is close to zNear. – J-S Oct 26 '15 at 18:21

0 Answers0