Assigned Monday, February 13. Due Monday, February 27
For this assignment, you will improve pbrt's support for rendering terrain in scenes like landscapes and seascapes. Terrain is often represented as an elevation map, or what we will call a heightfield: a 2D table indicating the height of a surface above each point of a uniform grid. In other words, a heightfield is tabulated form of a height function z(x,y) stored in a 2D array.
pbrt has no native support for rendering directly from heightfield data. Instead, it converts heightfields into triangle meshes by connecting adjacent height values in the array. This approach is general but relatively inefficient; by converting to triangles we lose the orderly structure of the heightfield, which can be exploited to render more efficiently. Your assignment is to apply techniques similar to those used for grid-based acceleration structures to develop a fast intersection routine for heightfields.
Download this zip file containing several pbrt scene files, heightfield data, and textures. The UNIX version of pbrt should build the heightfield shape plugin by default. If you're using windows, copy the heightfield.vcproj file to pbrt/win32/Projects/ and add the project file in the pbrt solution. After you have built the heightfield plugin, run pbrt on scenes: hftest.pbrt and landsea-0.pbrt, and you should see output images (hftest.exr and landsea-0.exr) that look like this:
The 4x4 heightfield in hftest.pbrt (left image) is designed for debugging; use this file as you develop and test your intersection algorithm. The landsea-0.pbrt contains two 64x64 heightfields (land and sea) and there are several views of this scene (0-3). landsea-big.pbrt contains a 1000x1000 heightfield and will likely take a while to render and consume loads of memory in the process.
Your assignment is to write a fast heightfield intersection algorithm, which will allow you to render higher resolution terrains more quickly than the current implementation. You should do this by modifying the existing 'pbrt/shapes/heightfield.cpp' file; you do not need to change other pbrt files. Specifically, you need to:
Your implementation of Intersect() and IntersectP() will likely use an acceleration structure like a 2D uniform grid, quad-tree or 2D kd-tree. Feel free to peruse the pbrt source for examples, but make sure you understand any code that you borrow.
Once you have the fast intersection routines working, add the following additional features:
In the archive that you downloaded in Step 1, you will find a "submission" directory. Copy this directory to your web space, and edit index.html to include your renderings and a description of your approach. The items that you have to replace are marked in green.
A: The default pbrt implementation does store the tessellated height field in a 3D K-d tree. This default implementation is actually highly optimized. However, you can provide a useful specialized implementation for height fields by exploiting the structure of the height field. For instance, a common approach is to traverse a 2D grid, building the height field triangles in that cell when you need to. This can save a lot of memory for larger height fields. Another approach would be to use a 2D K-d tree. These specializations are possible because of the structure of the height field, and are simpler and often more efficient than more general 3D versions. Choose an acceleration structure that interests you and explore it in this simpler 2D space.
A: On Unix systems, type 'make clean' to delete existing object files and executables. Type 'make OPT=-O2' (there's a dash before the capital O). That builds the system with optimization. On Windows, build the system under "Release" mode.
A: On Linux, you can use commands such as: 'time pbrt hw2-view1.lrt'. After rendering is complete, the time command will print out a line, where the first token is the total time executed in user mode, in seconds.
A: It may be challenging to get your intersection routine as fast as the default version in pbrt, because pbrt has been quite carefully optimized with a 3D K-d tree. It is not a requirement for the assignment that your code run as fast as the default implementation (your routine is still practically useful because it probably uses less memory than the default). Nevertheless, it is possible to get your specialized height field intersection as fast or faster than pbrt, and you can get extra credit if you succeed in doing so.
A: You may get some black regions where the water is reflecting far away from the forward direction. The sky background doesn't actually cover the entire hemisphere, and the water will appear black where the correct reflection direction misses the sky geometry (a cylinder). This doesn't happen in sea1.lrt, but it does in the foreground of landsea-big.pbrt where the water is more bumpy. However, if you are getting black speckles on the crests of your waves in the distance, then this is likely an artifact of your Phong interpolation scheme; this is something you should eliminate.
A: Please send us the write-up for your URL by the end of the day (11:59 pm).
A: The main difficulty is that plugin code is loaded dynamically by pbrt.
Greg recommends:
Matt adds:
This homework will be graded according to the following system:
0 - Little or no work was done
* - Significant effort was put into the assignment, but rendering does not work fully
** - The intersection routine was functionally complete, but step 3 doesn't work at all.
*** - Intersection and texture mapping worked, but normal interpolation didn't.
**** - Intersection and normal interpolation worked, but texture mapping didn't.
***** - All requirements satisfied
****** - All requirements satisfied, and the implementation is faster than the original