15-864 Assignment 4: Photon mapping


In this assignment, you will learn about monte carlo sampling for solving the rendering equation and acceleration techniques.


The assignment description can be found in PDF form here. Let us know if you have any questions.

Rendering Contest

The entries for the optional rendering contest can be seen here. They look great; congratulations to everyone that entered!


We are providing you with a large code base to start from that should be of significant help. It can be found at /afs/cs.cmu.edu/academic/class/15864-s04/photonMappingStarterSpr04.tar.gz (due to the restrictions noted below, we cannot post it on the web).

Note: Please do not distribute this code. Various portions of this code are provided courtesy of the University of Washington graphics group and Henrik Wann Jensen, and they have requested that we make it available only for the purposes of this class.

Caveat #2: this code is a bit of a mixed bag; at various points in time dozens of Washington undergraduate and graduate TAs have made changes to this code, without any sort of unifying style guidelines. I apologize for the lack of coherency; however, the code is fairly well documented. I have also spent the last couple of weeks rewriting and shaking out all the bugs that I could find in the code (and will continue to do so), but if you find any please let me know ASAP so that I can alert the class! Thanks.

Update 26 April 2004: There was a small but significant error in the diffuse scattering derivation which has been fixed; the 2π normalizing term should have been just π (thanks to Jernej for pointing this out). Obviously since this was my mistake you won't be penalized for using the incorrect scaling term (and you'll get perfectly reasonable looking images either way). My apologies for any confusion this may have caused. I'd also like to take this opportunity to remind you of the usefulness of Philip Dutré's online Global Illumination Compendium, which contains the correct normalization term. I've added references to this and other useful pubs at the bottom of this page, but you should still consider Doug's page to be the master list.

Update 26 April 2004: I've updated the sample solution again slightly to correct for an underestimate in diffuse-diffuse transfer. The correct formula is derived on the FAQ page, I strongly suggest taking a look at this if your diffuse-diffuse transfer doesn't look as you expect it to.

Update 23 April 2004: The sample solution had a bug in the Fresnel code that caused it to reflect twice as much radiance at normal incidence than it was supposed to. This has been fixed now in the version in the class directory.

Update 23 April 2004: For cornellBoxSpheres.ray, you'll get a prettier picture if you crank the reflective coefficient of the first sphere to (1,1,1) and the transmissive coefficient of the second sphere to (1,1,1) (this will make the scene look a little brighter).

Update 22 April 2004: A quick tip: If you're seeing a lot of totally blown out photons in your map, consider the possibility that you have a divide by zero somewhere in your code. I find it helpful to sprinkle my code liberally with asserts to catch these as close as possible to the place they occur.

Update 21 April 2004: FLTK seems to have mysteriously vanished from the lab machines, so I installed it in the class directory. Let me know if you have any problems with it.

Update 19 April 2004: I have added the beginnings of a FAQ here; please take a look at it if you have any questions about the assignment.

Update 19 April 2004: I've created another test scene with water caustics (see above right); you can download it here. Thank Maya for the watery displacement map. Note that this scene uses a polymesh for caustics, so if your code only handles spheres this may be a problem (and hence it is not required that you be able to render it). If you want to, though, it's pretty easy to build the caustic map using simple rejection sampling.

Update 15 April 2004: Quick tip on Henrik's photon map implementation: don't forget to call Photon_map::balance() after you've stored all your photons because I don't think it will return correct radiance estimates otherwise.

Update 11 April 2004: I've posted a sample solution at /afs/cs.cmu.edu/academic/class/15864-s04/photonMappingSolution (or photonMappingSolution.exe for Windows users). Note that you have to set the depth to at least 1 to get it to sample the diffuse reflections rather than read the irradiance directly from the photon map (and you'll want to send out more than 1 sample/pixel to get any decent images).

Update 10 April 2004: For those who were unhappy that the BSP tree code wasn't working, I've posted another patch that replaces the BSP code with my own acceleration code (it's an axis-aligned bounding box heirarchy, and due to some fancy template stuff it now uses the same code that the trimesh acceleration code uses). As I noted below it won't make a difference on your standard trimesh type stuff but it might make a difference if you decide you want to find out what caustics a sphereflake has. The patch can be found at /afs/cs/academic/class/15864-s04/patch-photonMappingStarter-p2 and instructions for its use can be found in the same location, here.

Update 9 April 2004: I've added a project roadmap here. Also, please disable BSP tree acceleration whever you're doing renders; I have my own acceleration code in there which should be plenty fast enough and I seem to have broken the older BSP tree code during the rewrite. I'll post the solution tomorrow, I just need to test it in the lab.

Update 9 April 2004: When you add in the proper falloff term for the area light source, you will probably find that due to the relative sizes of the geometry everything will be much too dark. I recommend setting the light intensity to 250.0 like this:

// light
translate( 2.78, 5.48, 2.295,
	scale( 1.3, 1.0, 1.05,
		rotate( 1.0, 0.0, 0.0, 1.5708,
		square {
			material = {
				emissive = (250.0, 250.0, 250.0);
	} ) ) )
I haven't updated the code in the source directory because I want people to be able to at least render the sample scenes out of the box.

Update 8 April 2004: I have updated the starter code significantly, most especially with respect to the user interface which now contains a variety of options which may help make testing easier. In particular, this update includes:

You can either download the new updated code, or if you've already started working on the project I've posted a patch which you can apply to your existing code base at /afs/cs/academic/class/15864-s04/patch-photonMappingStarter-p1. Intructions to use this patch can be found here.

Update 7 April 2004: You may need to add the following to your .cshrc (or .login) file to get Linux to find all the runtime libraries:

% setenv LD_LIBRARY_PATH /afs/cs/academic/class/15864-s04/lib/


The major components of the code consists of (1) a parser, which converts .ray files into scene graphs, (2) a user interface, which you can use to adjust various ray tracer parameters and also to visualize the scene, and (3) the actual ray tracer implementation itself, which is a full recursive ray tracer in the style of Whitted.

The parser

The parser reads in .ray files, which are unfortunately a rather proprietary format, but which luckily support most of the stuff you would hope for including nested transforms, full scene graphs, arbitrary Phong materials and texture mapping, etc. The full documentation for this format is available here, but the best way to learn is by looking at the supplied samples. In particular, pay attention to the various Cornell box scenes that can be found in the scenes/photonMapping directory in the source code distribution.

The user interface

The user interface for the ray tracer uses the Fast Light toolkit. This is a cross-platform toolkit that allows for the use of OpenGL. It's also a little bit ugly (both in terms of the code you write and the look of the interface) compared to e.g. QT but its license is significantly less encumbered.

I have provided a couple of buttons and sliders in the UI for supporting both photon mapping and distribution ray tracing. In particular, you can select how many samples you want to cast per pixel (use the "samples/pixel edge" slider) and how many photons to send into the scene (use the "photons/light source" slider). The "Recompute photon map" button calls the recomputePhotonMap() function in Scene, which you have to implement. If you need to add any more sliders, you should add them in the GraphicalUI constructor and have an appropriate callback function (use the existing sliders/buttons as examples). Full documentation for FLTK 1.1 is also available on their web site.

The second (and most glamorous) part of the user interface is the debugging view, which you should hopefully find very useful. Most of the code that you might want to touch is in the DebuggingView::draw() function. A write-up giving more details for this interface can be found here.

The ray tracer

Most of the action in the ray tracer itself happens in the RayTracer::traceRay function. This implements a full recursive Whitted ray tracer with reflection and refraction and you should use this code as a reference while you are working on extending it to handle photon mapping. In particular, it makes extensive use of the Scene::intersect function, which casts a ray into the scene and returns with both the location of the intersection, a pointer to the intersected object, and the surface material.

The RayTracer::tracePixel function samples the pixel using stratified sampling as a proper distribution ray tracer should. It calls RayTracer::traceRay to get irradiance estimates for a given ray direction.

For other details on the code, please see this roadmap.

At center, the photon map for the Cornell box scene; at left, rendering the scene using the 2-pass algorithm suggested in Henrik's book; at right, the "official" rendering of the Cornell box, from this page. I'm not sure what is causing the differences between my box and the official rendering, but I suspect they are in part due to our use of a very simple RGB color model as opposed to a proper spectral model.

The Photon Map

Henrik has graciously provided us with his photon map implementation. You can find it in photonmap.h and photonmap.cpp. All the documentation we have for this is in the header file; more details may appear later.


I will periodically be adding assignment tips below.