A Real-Time Procedural Universe, Part Three: Matters of Scale
July 12, 2002
Page 2 of 3
>Problems of Scale: Z-Buffer Precision
I've already explained how to expand your Z-Buffer's precision by scaling planets down both by distance and by size. However, this won't solve all your Z-Buffer problems. You'll find that in scaling large numbers down, small changes in distance are lost. A moon that should be behind a planet may be rendered in front of it, or vice versa. Even when you're close to a planet, you can still have triangles break through the Z-Buffer because the far clipping plane is to far away. Bringing it in farther just makes those other problems worse.
Since you can't change the hardware in your video card, and since it's unlikely that chipset designers will provide a 64-bit Z-Buffer, this is a tough problem to solve. I have found that using impostor rendering makes this problem much easier to manage. To create an impostor, you render an object all by itself to the back buffer (or to a separate pixel buffer), then copy (or bind) that object into a texture map. Then the object is rendered as a single rectangle using the generated texture map. Because most objects look different from different viewpoints, the texture map must be updated as the camera moves around. In essence, an impostor is just a billboard that is generated in real-time as needed.
At this point many of you will be wondering how this helps you with Z-Buffer precision. The answer lies in the fact that you must use a special projection matrix when rendering your impostor texture, and this projection matrix has its own front and back clipping planes. This projection matrix creates a view frustum that fits tightly around your object on all 6 sides, which gives you the best Z-Buffer precision you can have for that object alone. Once that texture map is generated for a planet, you really don't need to worry about Z-Buffer precision. Because impostor texture maps are partially transparent, you need to render them in reverse Z order. This means that you can turn the Z-Buffer off completely when drawing the impostored planets.
Problems with Impostors
Impostors not only improve Z-Buffer precision for the objects being rendered as impostors, they also offer great performance improvements. Instead of rendering every planet in your scene every frame, you only need to render two triangles per planet on most frames. Every now and then you will have to update an impostor texture, but even then you will rarely need to update more than one planet's impostor in any given frame, and most frames won't need anything updated at all. Since you're not rendering all those triangles, you also don't need to check their ROAM priorities or update their triangle meshes.
Unfortunately, nothing comes without a price. There are a few problems that can crop up when using impostors. The first problem is that differences between the resolution of the impostor and the screen resolution can cause aliasing artifacts. This can be minimized by choosing an appropriate texture size based on the amount of view angle or screen space taken up by the object. Changing the texture resolution for an object will cause a visual shift that looks like the video card switching between mip-map levels. Because you can control the resolution/size trade-off, this problem is usually acceptable. However, this problem can become really bad when the camera gets so close that the object's edges extend beyond the edges of the screen.
Impostors can also cause problems related to the Z-Buffer. Because you're taking a 3D object and rendering it as a 2D rectangle, you are changing the Z-Buffer values that the object would normally generate. Worse yet, you are changing the Z-Buffer values for the entire rectangle, including the transparent portions of it. This can cause some really bad problems when an impostor gets too close to other objects. The rectangle can end up hiding objects that should be visible. It can even chop objects in half that lie in the rectangle's plane.
Luckily, these problems aren't too bad when dealing with planets. Inter-planetary distances are so large that we really only have to worry about these problems when the camera gets close to a planet. Even then we don't have to worry about objects being drawn on the surface of the planet, or in the planet's atmosphere, as these can all be rendered directly into the impostor if necessary. Still, at some point using the impostor will become more trouble than it's worth. The easiest way to deal with this is to switch from impostor rendering to normal rendering when the camera gets within a certain distance of the planet. In my demo I switch to normal rendering when a planet takes up 90 degrees or more of the field of view.
I have one last thing to mention about impostors. They are also used for rendering clouds, forests, cities, and several other large-scale details that you might want to render on a planet. But care must be taken in how you manage them, or you will quickly find yourself out of video memory. In my demo, I choose impostor resolutions from 512x512 all the way down to 8x8 based on the planet's distance to the camera. If I start to use impostors more extensively, I will need to create a texture cache for them.