3D Graphic Java: Render fractal landscapes

Get a behind-the-scenes look at 3D graphics rendering with this hands-on discussion of fractals, quaternion transformations, shadows, rasterization, and Gouraud shading

1 2 3 4 Page 4
Page 4 of 4

We now have some triangle coordinates on-screen, and an algorithm for resolving polygon overlap. All we need to do now is draw the triangles. For each triangle we'll use a simple algorithm that sorts the triangle vertices vertically, then steps from top to bottom, computing the left and right edges for each scan line of the triangle and drawing the spans using a simple setPixel() method. We'll draw our triangles into a 32-bit RGB memory buffer and display the result using the java.awt.Image class, just as we did last time.

To further simplify things, we are going to implement an algorithm that assumes that a triangle always has either a flat top or a flat bottom. If a triangle does not have a flat top or bottom, we simply divide it into two triangles, as in the following illustration.

Remember, however, that our triangles are not flat shaded. We must compute a smooth fade between the colors (and depths) of each vertex. This is actually quite easy to resolve: we compute horizontal and vertical color and depth differentials. These are the amounts by which the color and depth change for every pixel that we step along a span, and for every pixel that we step down the left edge. Because our triangles are flat, these values are constant over the triangle. We can then simply modify our rasterizing algorithm to add these differentials during the final drawing process.

The code for this rasterizer is fairly long, and in the interest of brevity, I'll let you examine it on your own time (see Resources for the complete source). The Rasterizer class allows us to rasterize Gouraud-shaded triangles into a z-buffered memory array for display.

Finishing touches

That's it. We've built, lit, shaded, and shadowed some terrains; transformed them according to an arbitrary viewing location; and then rasterized the resulting triangles to screen. What more could we want?

Well, it turns out that there are a couple of things we can do to make our pictures a bit more interesting. Consider, for example, the image at the very start of this article. There is a deep blue space/sky background and a fractal planet. How did I generate these?

First, I modified the rasterizer class to load a pregenerated background image instead of always clearing to black. Then, I used the fractal terrain generator to generate a terrain that faded from blue to black, and changed my display code to view this from above. Presto, a deep blue (unrealistic) space background. A tiny change and we have clouds. If we want the clouds to distort in perspective, as if curving around a planet's horizon, we just need to define an appropriate terrain function.

Fractal clouds

The next change I made was to add oceans. For this I took the fractal generator and modified the getAltitude() method to apply a fixed cutoff so that any altitude below a certain value was reported as a constant sea level (and blue). For interest's sake, I actually allowed a small amount of variation in sea level. I then used this new fractal terrain to generate a planetary surface from above.

A planetary surface

Stick this fractal terrain image in my last article as a texture map and we have a planet. Finally, combine the spacescape and planet as a background, draw an orange-red terrain in front, and voilà, the picture is complete. Well, after adding another light source to soften the shadows...

Conclusion

Computer graphics is a fascinating topic with many different facets. We've looked at two ways of drawing pictures. Last time around, we fired rays into the scene, tried to determine what the rays hit, and drew our picture from the outside in. This time around, we took the opposite approach. We took the elements of our picture and drew them one by one to the screen. The more that you strive for speed and realism, the more that you will end up drawing from these and other techniques. The boundaries are practically limitless, and thanks to current CPU speeds, much more accessible than a few years ago when custom graphics hardware was almost vital.

Some might say that going to the depth I have in this article -- covering details of viewer transformations and rasterizing individual triangles -- is unnecessary; the Java 3D API does it all for you. And, if your goal is simply to produce images, then they would be right. Some of us, however, can't keep our hands out of the mix; we need to know what is going on, right down to the metal.

Merlin blames it all on periodic bouts of alien hand syndrome. And those damned government mind-control lasers. Stop the madness! Patronize your local aluminum foil hatter!

Learn more about this topic

  • JavaSoft's Java 3D APIA comprehensive graphics library http://java.sun.com/products/java-media/3D/
  • UCB CS184Using Quaternions to Represent Rotation http://www.cs.berkeley.edu/~laura/cs184/quat/quaternion.html
  • Stop by Michael Garnland's terrain simplification page, which includes sample terrains http://www.cs.cmu.edu/~garland/scape/
  • Read Ken Musgrave's "Building Fractal Planets" http://www.seas.gwu.edu/faculty/musgrave/article.html
  • Read "Computer Rendering of Stochastic Models" by Alain Fournier, Don Fussell, and Loren Carpenter (Communications of the ACM, Jun 1982, p.371)
  • Download the complete source for this article as a gzipped tar file http://www.javaworld.com/jw-08-1998/step/jw-08-step.tar.gz
  • Download the complete source for this article as a zip file http://www.javaworld.com/jw-08-1998/step/jw-08-step.zip
  • Read all the previous Step by Step articles from Shoffner and Hughes http://www.javaworld.com/topicalindex/jw-ti-step.html
1 2 3 4 Page 4
Page 4 of 4