How to do Linear Z buffer

Hi Everyone,

I’m looking to try and do a linear Z buffer depth buffer but see no way to do it other than special commands in a vertex program.

I’ve attempted to use a projection matrix where w is equal to z. But there seems to be a problem that OpenGL expects z to be in -1 to 1 range after the vertex program is finished, except you want w to range from 0 to 1 for those same depths. So it becomes impossible to have equal values for both.

The only way I can think of doing it is through a vertex program where you multiply result.position.z by result.position.w before the end of the vertex program.

I’ve also heard that D3D supports linear Zbuffer, does OpenGL support some command where the final z/w can be avoided?

Thanks any advice is appreciated.

You could try just setting w to 1.0 but it’ll screw up your perspective correction.

Originally posted by JelloFish:
[b]
I’m looking to try and do a linear Z buffer depth buffer but see no way to do it other than special commands in a vertex program.

[/b]

I also, in my own naive way, tried to work this out. Not in a vertex program though. To do this I remapped the z generating row in the projection matrix so it after perspective division became az+b by assigning vertices with w=z^2 instead w=1.
The obvious problem is how to deal with translation. If I separated mv and proj matrix transformations it worked. The solution “only” defeated the whole purpose of using homogeneous coordinates.I later learned that there are very good reasons to a non-linear zbuffer. Which kind of answered my inital questions on why non-linear.

My 2 cents.

[This message has been edited by roffe (edited 06-05-2003).]

Originally posted by dorbie:
You could try just setting w to 1.0 but it’ll screw up your perspective correction.

Nah, it’s better to counteract w by doing
z*=w;

Ahh, of course. What was I thinking :slight_smile:

On the subject of the “non-linearity” of Z buffers: Z buffers give you volume areas (depth resolution quanta intersects pixels) that are uniform size in projected space. Thus, there’s a decent argument that they’re the right solution as-is. J

ust like an object needs to get bigger in X and Y to fill a full pixel when receding into the distance, it should also need to get bigger in Z to do the same. Difference being, you usually have 24 bits of Z resolution, but only 10 bits or so of X/Y :slight_smile:

Hmm, but in practice the non linearity of Z is determined by the near and far clip planes and depth resolution, whereas the size of an object is determined by the field of view and resolution.

It’s the whole ratio of near to far weighting depth resolution that’s at the root of the problem and that goes back to your projection matrix.

Up until recently the math has been too convenient and fast to give a darn, but now it’s less relevant. You can pull whatever value you like from whatever field you like so long as you can do the fragment arithmetic to make the perspective interpolation correct. This very much changes the rules. It’s time to be less clever about the whole thing ane pick the values you want, not the values that are convenient for 2D linear interpolation. Smart people have understood this for a while.

All this nonsense is really about; given the new capability of hardware what should we stuff in the depth buffer for best effect.

If you ask me I’m thinking floating point exponent (think fractional exponent) with 0 = near and 1 = far linear. But the devil’s in the details when you start looking at precision issues when yanking values off the matrix. But this is ONLY where you have less storage precision than you have numerical precision and I’m not really serious about this part :-).

The rules have changed, we have space for fp hardware and 8 bits per pixel framebuffer is irrelevant. There must be a categoric answer to this question in the new order. Given that you have limited transform precision, and you can do whatever fragment arithmetic is within reason, and zbuffer size is not an issue, what’s the best value to grab, manipulate and store. Some analysis about depth storage falls short because they assume that you have infinite precision until you manipulate & cast to the limited precision depth buffer. This just isn’t the modern paradigm.

Start with what precision comes out of projection, try to preserve as much of it as possible and realize that worrying about depth buffer size is a joke. Assume you have all the depth buffer precision you need, don’t try and be a smartass about saving 8 bits of a 32 bit field and let’s assume for a sec you have fp compare interpolation and divide at full precision. Now, what’s the best you can preserve with perspective correction from your matrix transform.

When you answer that, then THAT’S what your depth buffer should be (if it isn’t now it will be in 2-5 years), and to heck with all the wacky schemes to do anything else. 1-z is the last thing any sane person would do under any those circumstances.

Just a suggestion :slight_smile:

P.S. this answer does not change unles syou start adding precision to your modelview and projection transformation. Ultimately you’re gated by the fp eyespace z coordinates, you can’t do better than that. So start with 32bit IEEE eyespace Z, if you’re trying to do better than that or trying to store fp 0.0 at the far plane then you’re seriously out of luck, at least for resolving one object w.r.t. another after transformation.

Hmm, I think we have an answer, 32 bit IEEE eyespace z.

The original question is not clear to me.

The standard OpenGL z-buffer is an occlusion buffer whose interpolant is linear in window space.

The w-buffer (which OpenGL was never extended to support “natively”, AFAIK) is an occlusion buffer whose interpolant is linear in eye space.

[Note that computing parameters that are linear in eye space requires perspective correction - and such parameters generally require a fair amount of per-pixel floating point math.]

You can do any variety of linear/nonlinear/piecewise z you want with depth replacing fragment programs, but you’ll pay a signficant performance penalty for using them.

That’s because most modern GPUs are optimized around the idea that most geometry is linear in window space z - and there are lots of optimizations you can make around that (good) assumption.

Thanks -
Cass

The fundamental observation or at least hope / article of faith with the W buffer was that you could finally afford the perspective correction per pixel.

The schemes to squeeze the last nuance of depth precision into some 24 bit or lesser format are pretty strained and look downright silly with suggestions like 1-z stored in eyespace. My post was just an appeal (or observation) that kind of led full circle all the way back to the obvious, a full precision eyespace Z (not a w buffer (what was I thinking) but true eye z pre projection) interpolated with perspective correction, obviously requiring a few extra bits of precision while doing the math. Any scheme implemented isn’t going to do any better for obvious reasons, although maybe you could use your hardware more wisely. Those are the eyespace numbers that come out of the modelview and that (or worse) is all you have to work with.

I was really just drawn to this longwinded monologue rediscovering the obvious by the thread and the preamble in another depth buffer related thread.

Naively replacing z with a linear depth buffer will cause some serious artifacts without perspective corrections and for example coplanar faces that don’t share vertices will incorrectly occluded. Hmm… a 32 bit 1D floating point texture texgen’d along eyespace Z fed into the depth buffer output register in a fragment shader. That would do it I think, unfortunately it’s a bloody big texture without texture filters, so forget it (some day). At least you can pick the texture format of your choice that has a decent filter. In a 1D texture you could apply any depth ramp you like (even the fractional exponents I mentioned earlier). All 100% perspective correct and totally functional. Makes you glad some clever hardware has been applied to the texture interpolators even if the same gates haven’t been spent on the depth buffer fragment interpolation. The devil is very much in the implementation details, but at least it’s clear that the vaunted ‘difficult’ part of the problem is tractible and has been solved elsewhere in hardware where it’s seen as a priority.

[This message has been edited by dorbie (edited 06-06-2003).]

I’m not sure I was entirely clear on this point before, but lots of occlusion culling and depth buffer compression techniques make use of the assumption that z is linear in window space.

You can remap z however you like in your fragment program, but it will cost you more than just the extra shader computation.

I’m relatively agnostic about the whole this-mapping-is-better-than-that-one debates. I’m happy with 24 bits.

Cass

Interesting point, but why is 24 bits still the magic number? Surely this is really a legacy value imposed by what was considered to be a generous per pixel ammount on laughable hardware 20 years ago. How about having a whopping 48 bit z? With a fixed point representation generated from a floating point input you could certainly use it. I suppose ‘compression’ schemes have already done this (probably with less than 48 bits) and gone back to some non linear storage prior to the compare & store at some smaller precision, or after compare as long as the depth read does the conversion back to fixed point (no the compare has to be done with the whacky representation due to rounding).

Here’s a crazy idea, how about a depth LUT (or gamma or whatever). You do interpolation at some decently high precison, then you convert to the pseudo representation prior to storage, but allow that conversion algorithm to be weighted with a lut or exponent (akin to gamma). It’s like the above scheme but let’s you go with any distribution of precision. I’d like to see you get glPolygonOffset working with that scheme :-).

OTOH it’s making a meal of the whole thing, just go with the high precision full depth buffer. The effort ain’t worth the memory you’re trying to save these days… ah precision = bandwidth.

[This message has been edited by dorbie (edited 06-06-2003).]

Originally posted by cass:

I’m not sure I was entirely clear on this point before, but lots of occlusion culling and depth buffer compression techniques make use of the assumption that z is linear in window space.

What do you mean? the z-buffer is stored compressed in memory all the time?
And how to disable it if I want to?

PS: the idea here is to not store a perspective z into the depth buffer but to store a orthographics z in there. The depth testing will work fine. I have a paper on this.

[This message has been edited by V-man (edited 06-06-2003).]

V-Man ortho z will need perspective correct interpolation. For example projected eye space texture gives you this.

Originally posted by JelloFish:
[b] I’m looking to try and do a linear Z buffer depth buffer but see no way to do it other than special commands in a vertex program.

[/b]

try http://developer.nvidia.com/docs/IO/1331/ATT/ZBuffering2.pdf
and
read here http://www.sjbaker.org/steve/omniv/love_your_z_buffer.html

V-man,

There’s no way for users to disable buffer compression or early occlusion hardware. They’re interesting hw features in that they have no place in the logical operation of OpenGL, but they’re really important for increasing effective memory bandwidth and effective fragment and pixel processing rates.

I understand the desire to store a perspective-corrected eye-space Z term in the depth buffer (which is non-linear in window space). That’s what w-buffering is. Just be aware that while you will probably get better precision distribution, you lose the nice property of window-space linearity - and that may hurt performance.

Dorbie,

I just mean that 24-bit fixed-point z gives artifact-free depth resolution for all of the [near,far] ranges I ever use. I realize opinions differ on this. Especially flight sim types developers.

Thanks -
Cass

Hmm… someone’s been talking.

I just thought it was pretty interesting to explore the limits of what you could theoretically get, and confuse myself in the process. It would be nice do dispense with all the whacky depth buffer schemes and say you’re using all you can get from the modelview (z being pre projection eye z).

The whole business of directly interpolating and storing a larger fixed point format would be very interesting, but I have no intuition for the hardware costs. Of course it’d just be a larger depth buffer.

Can you tell us what precision is in the interpolators now before the conversion to ‘compressed’? Or do you interpolate the ‘compressed’ representation?

[This message has been edited by dorbie (edited 06-06-2003).]

Originally posted by dorbie:
Can you tell us what precision is in the interpolators now before the conversion to ‘compressed’? Or do you interpolate the ‘compressed’ representation?

There are a lot of factors that affect the precision of actual interpolators. Particularly non-visible factors, like method of interpolation and maximum resolution and sub-pixel precision.

We don’t publish buffer compression details, but as long as the compression is lossless, the effects are invisible - except for their performance characteristics.

Thanks -
Cass

Hmm… you imply that this is real compression. My understanding is that these types of compression simply change the distribution of precision using some pseudo floating point representation. So if you took the MSB of your interpolation result and compared & stored it that would be an ‘uncompressed’ Z but it’s lossy, obviously. In a compressed scheme you take the same number and mangle it with some kind of bastardised float and compare / store, it too is lossy but it preserves the depth information that’s more interesting, so it doesn’t really make sense in any conventional sense to call this lossless, although it’s no more lossy than a traditional approach.

Interpolating the compressed format is no different conceptually w.r.t. lossiness, you just cast sooner.

I appreciate that these things are often dependent on many things, I was just looking for ‘register’ size more than an absolute metric of meaningful bits.

For clarification,

We are reading in the depth buffer and trying to use it to perform post scene effects. However since as distance of an object increases z values get closer to 1 faster. It doesnt seem like the pixelshader math exists to work with the nonlinear distance values. Ideally I would want to use a special projection matrix to get z to end up being linear but it didnt seem possible.

Also after making everything have a linear z in the vertex program most scenes look fine(even better), except for a small amount of scenes where the polygons are large and are sometimes off screen. I don’t think the resulting z/w values are interpolating the way I would expect them to. Perhaps this is a problem with something not being perspectively correct as some of you have mentioned.