If you find this article contains errors or problems rendering it unreadable (missing images or files, mangled code, improper text formatting, etc) please contact the editor so corrections can be made. Thank you for helping us improve this resource |
Introduction
ZBuffering is a very handy graphics algorithm. Invented by Catmull in '79, it allows us to paint objects to the screen without sorting, without performing intersection calculations where objects interpenetrate, to paint in whatever order we like, and to paint any kind of object we like. But there is one big problem with it: unless in hardware, its slow and inefficient. But you might as well add it to your engine, its very useful. A ZBuffer type system is also used in Radiosity, if you are calculating form-factors in the hemicube algorithm.
How It Works
The basic idea behind zbuffer is very simple: If the pixel we are currently painting is the closest yet painted, write it. If not, don't write it. To identify if the pixel is the nearest, we need a memory buffer - the Z buffer. Here we store the Z of each pixel painted so far. Pseudo-code for a naive implementation of this would be:
Note that we needn't be using solely polygons. The implementation could equally be:
However, there are four main problems with the z-buffer:
Improvements
There are a number of improvements we can make to the z-buffer algorithm. The first is to use 1/z values. Such values are linear in screen space, are always in the range -1 to +1, and do not suffer from z-compression. Clearing the zbuffer simply becomes a case of memset(zbuffer, 0, zbuffersize), because 0 in floating point is 00000000h. We must however invert our condition, from:
The penalty of clearing the Z-buffer can be improved with dirty rectangles. The idea behind this is that you just clear the part of your screen (and Z-buffer) that you have written to. However, this is not much use if you're doing full screen animation. Alternatively, you can try adding some value to your Z every frame. Eg if you are using a 32-bit integer zbuffer, try adding on a value greater than the far clipping limit; for example 65536. Apparently, using this technique, you can avoid clearing the frame buffer. I think pseudo code would go something like:
ZBuffering is a very handy graphics algorithm. Invented by Catmull in '79, it allows us to paint objects to the screen without sorting, without performing intersection calculations where objects interpenetrate, to paint in whatever order we like, and to paint any kind of object we like. But there is one big problem with it: unless in hardware, its slow and inefficient. But you might as well add it to your engine, its very useful. A ZBuffer type system is also used in Radiosity, if you are calculating form-factors in the hemicube algorithm.
How It Works
The basic idea behind zbuffer is very simple: If the pixel we are currently painting is the closest yet painted, write it. If not, don't write it. To identify if the pixel is the nearest, we need a memory buffer - the Z buffer. Here we store the Z of each pixel painted so far. Pseudo-code for a naive implementation of this would be:
Clear Z-Buffer to a certain value For every polygon For every scanline of that polygon For every pixel If this is nearest pixel { Write Pixel Write Z into zbuffer } End End EndIf you are wondering where to get your Z from, then its simply a case of linear interpolation. There are lots of documents available to cover that, so I won't go into that right now. Simply take the Z values at the vertices, and interpolate across the edges. Then, take the Z value at either edge, and interpolate that, getting you a Z value for each pixel.
Note that we needn't be using solely polygons. The implementation could equally be:
Clear Z-Buffer to a certain value For every primitive For every scanline of that primitive For every pixel If this is nearest pixel { Write Pixel Write Z into zbuffer } End End EndSo we could build a graphics engine that handles things such as real spheres, torii etc. Such engines are very versatile.
However, there are four main problems with the z-buffer:
- Clearing the zbuffer
- Re-writing pixels that have already been written (overdraw)
- Z-compression with distant objects
- Jumping at every pixel!
Improvements
There are a number of improvements we can make to the z-buffer algorithm. The first is to use 1/z values. Such values are linear in screen space, are always in the range -1 to +1, and do not suffer from z-compression. Clearing the zbuffer simply becomes a case of memset(zbuffer, 0, zbuffersize), because 0 in floating point is 00000000h. We must however invert our condition, from:
If CurrentZ <= ZBuffer[y][x] To: If CurrentZ >= ZBuffer[y][x]An advantage of using 1/z is that operations can be done in parallel with the FPU. So for example while you are stepping through your Z in the FPU, you can be calculating the next address, decrementing your counters etc in the IPU.
The penalty of clearing the Z-buffer can be improved with dirty rectangles. The idea behind this is that you just clear the part of your screen (and Z-buffer) that you have written to. However, this is not much use if you're doing full screen animation. Alternatively, you can try adding some value to your Z every frame. Eg if you are using a 32-bit integer zbuffer, try adding on a value greater than the far clipping limit; for example 65536. Apparently, using this technique, you can avoid clearing the frame buffer. I think pseudo code would go something like:
// Init Stuff... zadd = 0 ClearZBuffer() for(;;) { // Render loop Project, Transform, Light Polygons etc... Clear Screen Add "zadd" to every Z value to be interpolated Render to zbuffer Add 65536 to zadd if zadd overflows { ClearZBuffer zadd = 0 } }Using this method, you can avoid clearing your buffer for literally hours, if you select the correct value. For those of you (like me) who use a floating point 1/z representation, I think this may still work; but I'm not sure if you have to do 1 / (z + zadd) or (1 / z) + zadd. Here I would expect a zadd increment of 1 should work well.