Z-Buffer Problem .. but how to solve it ?

hi all,

i read a lot about the z-buffer and now i understand why i get the ‘flimmering’ and the strang occurence of some objects, but … i can´t think of a solution …
first of all a screen of the distance objects showing the problem…

[ Image in Big ]

i am walking on ground in my game, in a hight of 2 … so zpos of ground below camera + 2 .
so i cant start the camera view smaller than 2 as i could see through the ground below my feet than … but for distance objects i get in trouble …

any idea … i know many games ( e.g. minecraft ) where the cliping beginns max 0.6 from the camara away and seems to have a very very far sight as well without flimmering …

can i get ride od the z-buffer problem by shaders ?

you see i am still new to open gl …

thanx a lot
uwi2k2

Multiple things could cause this.

First important thing is that it’s not how near your near clipping plane is that’s important; it’s the difference between your near plane and your far plane that really matters. Even if your near plane is set to a reasonable value (and 2 seems reasonable enough), if your far plane is huuuuuuuuuuuge you’ll still get bad precision. So as well as pushing your near plane out, pulling your far plane back will have the same remedial effect.

Second thing is that you might be getting a 16-bit depth buffer. This depends on how you’re initializing OpenGL, but the key point is that depth buffers commonly come in two varieties: 16-bit or 24-bit. On some hardware unless you explicitly ask for 24-bit you’ll get 16-bit. So to remedy this you should review your startup code and make sure that the depth buffer precision you’re getting actually is what you think it is.

A slight digression, but sometimes your hardware will give you 24-bit depth plus 8-bit stencil, even if you don’t ask for a stencil buffer. You should check for this and, if you’re also getting stencil, make sure that you clear stencil at the same time as you’re clearing depth. This is because depth and stencil are interleaved in a single 32-bit value and your hardware is unable to do a fast clear if you only clear depth.

Back on topic. There are shader tricks you can do that can linearize your depth buffer (one is described here; it’s for D3D/HLSL but you should have no problem porting it) (there’s also w-buffers but they don’t exist on the majority of modern hardware) but - and it’s a big but - the tradeoff is that your precision will fall off dramatically for nearer values of Z. This can give highly undesirable rendering artefacts for nearby objects, which looks even worse.

There’s also polygon offset, but I personally wouldn’t recommend it because the offset factor has different results for different values of Z, and it’s also allowed to have different results on different drivers. It sucks in other words.

I’d say double-check your far plane and your depth buffer precision first before considering anything else.

There are a lot of different techniques to solve z-fighting. Probably the easiest one is to split your scene into several partitions and use different settings for near and far clipping planes for each. Also consider using some LOD technique to increase distance of vertices for distant objects, and enable back-face culling.

I think the way this is worded may give the wrong impression.

Check me on this, but I don’t think it’s the near -> far “difference” that’s key but rather their “ratio”.

For example, it’s not like pushing your near plane out 5 meters will get you the same result as pulling your far plane in 5 meters. No way! And that’s where this gets misunderstood.

Rather, it’s more like “halving” your near value gives you roughly the same loss in depth precision as “doubling” your far value.

For instance, if your near plane is 2 and your far plane is 100,000, pulling your near plane in to 1 gives you about the same loss in close up depth precision as pushing your far clip out to 200,000.

Background: if you represent z_win as a function of z_eye (with the std perspective projection transform), and take the derivative with respect to z_eye, you get:

dz_win / dz_eye = -fn / z_eye^2

So at the near clip plane, the slope is -f/n. At the far clip plane, the slope is -n/f.

Now far is nearly always much greater than near, so the slope out at far is near 0 (meaning you’re getting very few depth steps (z_win) for a very large z_eye distance). With perspective foreshortening, that’s often OK (unless you get your near too small or have polys too close out in the distance).

The real killer is the slope at the near plane (-f/n). With far >> near, this is usually a big number! Meaning up close to the near plane, you’re eating depth steps like crazy (i.e. there are smaller Z distances between depth steps). The rate that you’re burning them is f/n (trailing off toward near-zero at the far plane). And the more you burn up-close to the near plane, the fewer you have for out in the rest of the scene.

So just to recap, with a perspective projection, it’s the ratio of far/near you really want to watch. Pushing your near clip out just a few units can net you a precision gain roughly equate to pulling your far clip in by thousands or tens of thousands.

With that background, if you think incrementally (additively) when setting your near and far clip planes, depth precision is pretty much all about the near clip. So don’t think that way – look at the ratio.

To the OP’s problem, once you’ve pushed your near out as far as you can stand (and pulled your far in to a reasonable distance) and you’re using the widest depth buffer you can afford, if you have Z-fighting, you basically have to use LOD to increase the distance that front-facing polys (I assume you are telling the GPU to cull back-faces) can be from each other in Z (i.e. reduce level of detail with distance) to prevent flimmering, OR (as Aleksandar said) you need to render the full scene in depth “slices” to give you more effective depth precision across the entire scene depth range.

Of course variants are obviously possible (BSPs, impostors, etc., where you cheat your way out of needing to use the scene-wide depth buffer to get proper occlusion for groupings of objects, but they come with their own limitations).

Note that flipping up to a 32-bit fixed-point depth buffer is not necessary going to solve this problem. Your precision may be limited by depth calculations in the pipe being done in 32-bit float (which has only 23 mantissa bits). I’ve tripped over that one before :slight_smile:

Right. And not only that, from what I gather it can kill Hi-Z (aka Hierarchical Z, ZCULL, Course Z, etc.) performance because it can end up offsetting the Z value by crazy-huge distances at shallow slopes.

The offset projection matrix solution allegedly doesn’t suffer these problems.

I’d actually originally written “ratio” but changed to “difference”. You’re right of course.

Excellent explanation too. :wink:

Overall though the best solution is - if possible - to set up your scene geometry so that it doesn’t have these problems. This means using nice chunky objects, avoiding co-planar polygons, switching off depth-writing where appropriate, etc. Not always possible - or even something you have control over - but if you can anticipate this kind of problem and take preventative measures as early as possible then it’s far cleaner overall.

hi,

and THANX to all of you and the big efford you put in your answers !
It will take me some time to test all this ideas, but i´ll come back to give a feedback !
thanx !

uwi

The page you linked to does not have a valid link to the source code for the modified projection matrix.

An alternative is to modify your projection matrix to perform a z-offset. You can read about this technique in Game Programming Gems 1, Section 4.1, or in Mathematics for 3D Game Programming and Computer Graphics, Section 9.1. Source code can be found here:

http://www.terathon.com/books/code/Listing9.1.txt

Any ideas where we can get the source for this?

[quote=“BionicBytes”]

The page you linked to does not have a valid link to the source code for the modified projection matrix. … Any ideas where we can get the source for this? [/QUOTE]
Interesting. That’s disappeared since I last looked. You can find it here in this forum:

Copied below for posterity:


void LoadOffsetMatrix(GLdouble l, GLdouble r, GLdouble b, GLdouble t,GLdouble n, GLdouble f, GLfloat delta, GLfloat pz)
{
GLfloat matrix[16];

// Set up standard perspective projection
glMatrixMode(GL_PROJECTION);
glFrustum(l, r, b, t, n, f);

// Retrieve the projection matrix
glGetFloatv(GL_PROJECTION_MATRIX, matrix);

// Calculate epsilon with equation (7)
GLfloat epsilon = -2.0F * f * n * delta / ((f + n) * pz * (pz + delta));

// Modify entry (3,3) of the projection matrix
matrix[10] *= 1.0F + epsilon;

// Send the projection matrix back to OpenGL
glLoadMatrixf(matrix);
}

Here’s a link into Google Books and presentation slides where Lengyl describes this trick:

As I recall, the basic idea is bumping the Z value forward to get you 1 window-space Z depth step closer to the eye at that distance.