Assignment 3 - Displacement and Bump Maps

Due - Midnight Wednesday April 7

Having succeeded in making a vase, you are now going to graduate to making a world. Yes, it's a big jump, but not as big as it seems.

You won't actually have to make a whole world, just an artificial terrain scene. The scene will contain two feature types, land and water. The land will be made with a recursive procedural displacement technique based on fractals. The water will be a flat polygon at "sea level" bump mapped to look "wavy".

You will recall that there are several types of maps that can be painted onto a parameterized surface. The basic types are texture mapping in which a color pattern is mapped onto the surface, bump mapping in which a normal displacement pattern is mapped onto the surface, and displacement mapping, in which a pattern of actual surface perturbations are mapped onto a base surface. In this assignment, we will build a real procedural texture with our displacements rather than using a displacement map that is dealt with by the ray tracer. However, the technique that we will use closely mimics one type of displacement map generation technique. We will use a real bump map, the idea will be to create something that looks reasonably similar to real waves. This can be done using a sinusoidal function, although that's likely to look to regular, or it can be done with fbm, a fractal technique, as described in chapter 11 of Pharr and Humphries. Once you've built a scene description with a triangle mesh for the terrain, another flat triangle mesh for the water, and a bump map for the waves, you'll use either pbrt or bmrt to render it. Choose reasonable colors for the terrain and the water. Choose some lighting that makes it look good. If you have time and want to make it really good, make some texture maps that increase the realism (especially for the terrain).

To make the terrain mesh, start with a flat square represented as 4 corner vertices, for example a unit square with vertices (0,0,0) (1,0,0) (1,0,1) and (0,0,1). Randomly displace these four points in the y direction. Now recursively generate new points within this square as follows. First generate a midpoint of the square by averaging the heights (in y) of the four corners, and then adding a random displacement to that height. This gives a point (0.5, ymid, 0.5) with height ymid. Together with the corners it partitions the original square into four triangles as well. For each of these triangles, generate a midpoint of the edge between the two corner vertices whose height is the average of the heights of the three vertices of the triangle plus a random displacement. Once this is done for all four triangles, you've completed one step in the recursion and you've split one square into four subsquares, all vertices of which have random heights. Now you can proceed to apply a similar technique to each subsquare. Note, however, that when interior triangle midpoints are computed, they should have heights that randomly displace the average of all four of the vertices that form the two triangles that they are on the edge of rather than only the three vertices on a single triangle that happen on the edge of the original square.

This scheme is depicted below. Only a selected subset of the points are shown, and not all neighbors needed to generate a given point are shown in all cases. The points are labelled with the recursion depth at which they are created, 0 for initial corners, "na" for "square" midpoints, and "nb" for "diamond" midpoints generated in the first and second phase of each recursive step, respectively. Note that every interior point will be generated from 4 neighbors (diagonal neighbors connected by dotted lines for the "square" midpoints in the "a" phases and horizontal and vertical neigbors connected by solid lines for the "diamond" midpoints generated in the "b" phases) even though not all neighbors are shown here.

There are a number of ways to do the random displacement that produce surfaces that look like real terrains. One of the simplest ways is to generate a Gaussian random number that has a variance proportional to the distance of the newly-generated point to any of its neighbors raised to the power h, where h can range from 1 to 0.5. This makes the displacements get smaller by a factor of about 2 with each step in the recursion, and lets the "roughness" of the terrain be controlled by varying h.

There are other ways to compute displacements that can produce interesting effects. If you write your terrain generator properly, it will be easy to explore veriant displacement generation schemes without changing anything except the routine that actually creates the displacments.

To get water waves, try using fbm noise as described on pages 420-430 of the text. If you want more regular patterns, you could just generate interference patterns using sinusoidal functions.

You should turn in a rendered image of your terrain along with the input file and your terrain and bump map generating code, and a makefile or at README with instructions for building the executable of your generating code. You can turn in more than 1 set of these if you've done some experiments, you'll get extra credit for your initiative.

If you are using C or C++ to create bump maps, you can do this for pbrt by creating monochrome tiff files and converting these to exr format using /p/bin/pbrt/tifftoexr. Here are instructions on using libtiff.