• 11/19/99 02:52 PM
    Sign in to follow this  
    Followers 0

    Z-Buffering

    Graphics and GPU Programming

    Myopic Rhino
    [size="5"]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.


    [size="5"]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:
    1. Clearing the zbuffer
    2. Re-writing pixels that have already been written (overdraw)
    3. Z-compression with distant objects
    4. Jumping at every pixel!

    [size="5"]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.
    0


    Sign in to follow this  
    Followers 0


    User Feedback

    Create an account or sign in to leave a review

    You need to be a member in order to leave a review

    Create an account

    Sign up for a new account in our community. It's easy!


    Register a new account

    Sign in

    Already have an account? Sign in here.


    Sign In Now

    There are no reviews to display.