• Create Account

We're offering banner ads on our site from just \$5!

Like
0Likes
Dislike

# Z-Buffering

By Tom Hammersley | Published Nov 19 1999 08:52 AM in Graphics Programming and Theory

pixel zbuffer value zadd write end using screen clearing
 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:

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
End

If 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
End

So 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...
ClearZBuffer()

for(;;) { // Render loop
Project, Transform, Light Polygons etc...
Clear Screen
Render to zbuffer
ClearZBuffer
}
}

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.