Lab 4: Interactive terrain

Goal: In this lab, you will render a 3D terrain with objects that interact with it. The terrain is given as a heightmap image.

Since this is the last lab, it is more flexible than usual. There are several interesting non-mandatory tasks at the end.

1) Load and inspect the heightmap.

Lab files:



A "bare bones" program is provided as lab4-1.c, with two basic shaders, terrain.vert and terrain.frag.

Two of the included images are intended as heightmaps.

You will also need files from earlier labs (textures, helpers). The makefile is similar to old ones.

The initial lab shell loads the small terrain "terrain-44.tga" and draws it with texture. There is, however, no light and no normal vectors.

To compile this assignment, perform make lab4-1 on the command line.

Run the assignment by performing ./lab4-1.


2) Navigating the heightmap

Goal: To move around a large heightmap.

Copy lab4-1.c to lab4-2.c. Make this section's changes to lab4-2.c.

Try loading one of the larger heightmaps. You may need to scale the map differently.

Use your code from earlier labs for placing the camera so you can move around it nicely. (If your camera movement is good, this is easy.)


3) Calculate normal vectors and apply lighting

Goal: To make the heightmap look better by using lighting.

Copy lab4-2.c to lab4-3.c. Make this section's changes to lab4-3.c.

Lighting should be familiar to you by now. Generate a normal vector for every vertex in the surface to make a decent lighting of the scene.

Apply lighting on the terrain by using a shader from earlier labs.

The triangle positions must be used for finding the normal vector. You can choose from two methods:

1) High precision version. For a vertex, calculate normal vectors for all neighbor triangles. Then calulate a weighted average of these. For maximum precision, this should depend on the angle of the triangle. This is fairly easy for a terrain since the angle, projected on Y, is always 45 or 90 degrees.

2) Good, fast approximation. Pick three neighbor vertices, on all sides of the vertex, and use these three for calculating a normal vector.

In all cases, you must check for edges and use some special case there. If you are significantly behind schedule, it is OK to just skip these normals, leaving them unchanged. (You don't do that in "real life" though, where the terrain is often a patch out of many.)


4) Calculating map height for a point

Goal: To find the height of the heightmap for any given point and place objects in the scene using that information

Copy lab4-3.c to lab4-4.c. Make this section's changes to lab4-4.c.

Your height map is drawn as a set of triangles. For a given point (x, z), find the y value.

This is done in three steps:

1) Calculate what quad the point falls into.

2) The quad is built from two triangles. Figure out which one to use.

3) Calculate the height value. There are several ways to do it. You can interpolate over the surface, or you can use the plane equation.

Using this function, place objects on the surface. You may use anything but the models "groundsphere.obj" and "octagon.obj" can be nice.



The object should slide smoothly along the surface without jumps, and must not go between exact vertices but interpolate nicely. (Note: Interpolate on the CPU and use the height for translating the object. This is not a shader interpolation problem.) You can use the low-res terrain for testing; it can be pretty revealing.

Hint: When debugging, it can be worthwhile to return to the small terrain, possibly scaling it up so it is easy to see what happens. Otherwise you may have so many polygons that you can't really tell why the object moves in a certain way.

You only need to check height for the bottom point of the object. This means that it might overlap the surface in other places. We accept this error for now.


5) Pick one of the "extras" below as final part of the lab.

If you get here quickly, well within the lab time, I expect you to make two of these. If you are short of time, you only have to do one.

Remember that some nice features can be part of your project even if you don't finish them here, in case there are several features below that you feel are desirable (and there surely are).

Copy lab4-4.c to lab4-5.c. Make this section's changes to lab4-5.c.

5a) Multitextured terrain [medium]

Goal: To make the terrain look even nicer through multitexturing.

Using at least two textures and a shader, blend the textures in some appropriate way (height, slope) to make the terrain more realistic, interesting and/or beautiful.


5b) Multiple objects [easy]

Goal: To put many objects in the "world"

Introduce many objects, either many of the same or different objects. In any case, you may use spheres for collision detection. Handle collisions between the objects and between all objects and the surface.


5c) Objects with terrain dependent slope [medium]

Goal: To measure the slope of the terrain and apply it on objects

Every polygon has a slope, that is its normal vector. Use that information to draw objects that are appropriately tilted to match the slope. Test using the low-res terrain.


5d) The lake [easy (with optional hard extensions)]

Goal: To handle a part of the terrain separately to make a lake

One of the terrains, "fft-terrain.ppm", has a big flat area in the middle of the map. This area could be considered a "lake". Identify the appropriate "water surface" polygons and paint them blue.

Luxury variants (teasers for advanced students, but generally more interesting for your projects):

Again, this list is just a "teaser" list. The basic assignment can be very easy.


5e) Camera movement [easy]

Goal: To move the camera over the terrain

Adapt your camera movement to make the camera move over the terrain without intersecting it.


5f) Frustum culling for objects [fairly hard]

Goal: To optimize model drawing by only drawing them if visible

In large worlds, models will often be outside the viewing frustum. Then drawing can be speeded up significantly by excluded out-of-view models. Several ways to handle this are outlined in the book.

For doing this, you need to find normal vectors for all frustum planes. You can safely forget the near plane, though.

To use this for a model you should use a bounding sphere.


5g) Frustum culling for large world terrain [hard]

Goal: To optimize terrain drawing by only drawing visible parts

When using large terrains, large parts of the terrain will often be outside the viewing frustum. Then drawing can be speeded up significantly by excluded out-of-view polygons. Several ways to handle this are outlined in the book.

To use this for a terrain, you must split the terrain into at least four sections, or (better) extend the terrain to a much bigger world. The frustum culling will limit drawing to at most four sections (granted that your frustum isn't too long).

For the lab, note that the provided terrain is wrap-around so you can just reuse it for additional sections.


That concludes lab 4. We hope that this gives you a start in programming geometry yourself as needed, and maybe gives you tools and ideas to use in your projects.