Roadmap

This is intended to help guide you through the project, since I've been getting a lot of questions about how the code is organized.

Step 1: add soft shadows

This is the easiest part of the assignment, and it doesn't have any dependence on the photon map, so I recommend you start here. First, take a look at the file Material.cpp. You should find the following function,

Vec3f PhongMaterial::shade( const Scene &scene, const ray& r, const isect& i ) const
{
 ...
This function gets called by the recursive ray tracer every time it wants to find the shading for a surface. Notice that it consists of a big loop over all light sources,
for (vector< boost::shared_ptr<Light> >::const_iterator l = scene.beginLights(); l != scene.endLights(); ++l)
{
	std::pair lightPair = (*l)->getDirection(P);
	// ...
}
What we are doing here is evaluating the direct component of illumination for a surface -- which in the basic Whitted raytracing formulation is the only part that gets counted. Now, you'll need to do two things here:

Additional note: the Light::color function returns the emitted radiance for that light.

Compute the global photon map

There is already a Scene::recomputePhotonMaps() function. You can use this to bounce photons around the scene appropriately. To do this correctly, you'll need to be able to compute (1) diffuse reflections, (2) specular reflections, and (3) specular refractions. Code for the second two is already in the RayTracer::traceRay function; you may want to borrow it (or for extra cachet consider refactoring this code out so you don't have code duplication). You'll have to write code for the first, but this is not too hard; you just need to use the cosine weighted distribution and then rotate the resulting vector into the local frame of the intersection's normal (i.N).

You should be using Russian roulette sampling when bouncing photons around. See the slides in this document for how to do this with three color channels. The extension to also handle transmission is fairly easy, if we define kd, kr, and kt as in the ray tracer (the diffuse and specular reflectance, and transmitted value, respectively), this this just becomes

float xi = traceUI->rayTracer().uniform01();
if( xi < average(kd) )
  // diffuse case
else if( xi < (average(kd) + average(kr)) )
  // reflected case
else if( xi < (average(kd) + average(kr) + average(kt) ) )
  // transmitted case
else
  // photon is absorbed

Visualize the global photon map

This part is pretty easy, you just substitute an irradiance calculation from the global photon map for the sum over the emitted radiances for all lights. Remember that you still have to post-multiply using the diffuse BRDF since the photon map stores irradiance and not outgoing radiance.

Two pass algorithm for indirect illumination

Here you need to add an additional case to the forward raytracer (e.g., in RayTracer::traceRay) that sends out a random ray using the cosine-weighted PDF. The key bit here is that once the ray hits a diffuse surface you should perform a lookup into the photon map rather than recursing any more. This will speed up the algorithm as well as reducing variance a little bit (since the photon map estimate tends to be fairly smooth).

While you're at it, you should consider adding the fresnel term to the refraction code so that you can get nice-looking glass spheres like on the project page.

Caustic map

I recommend as a first step just using rejection sampling to compute this map. Send out a whole bunch of rays into the scene just as you did for the global map, but don't store them in the caustic map unless they hit something. You still need to scale photon power using the total number of photons you sent out rather than just the number that hit (to have the correct amount of power in the map). This simple sampling method will get you the most bang for your buck; in the box scene, it should still compute in a reasonable amount of time since the spheres are reasonably close to the lights. Only after you have this working should you worry about computing the projection map to make it more efficient.


Questions? Contact Christopher Twigg.