My Renderer

I spent a lot of time on my renderer this quarter for CSE 168 with Henrik Jensen. I've really enjoyed working on it. I have lots of future plans for it, but this page will discuss its current state.

Procedural Grass

When I decided to render a jack-o-lantern I knew I would have to give it a decent environment to sit in. Continuing with my hobby of implementing procedure models for flora I decided to add patches of grass as a primitive to my renderer. The grass can be completely customized in the scene file. I don't think it is the most realistic grass, but it's a decent approximation. Each blade of grass is one triangle. The blades curve randomly. Those are the only real rules to the system; everything else is specified in the scene file (color, density, length, etc.). I have plans to add in sporadic long blades of grass later on.

Grass

Diffuse grass looks pretty good. The addition of subtle specular hilights was a small improvement.

Area Lights

One of the first featurs I implemented for my final rendering project was area lights. So far I have rectangular and sphere light sources working. I use stratified sampling for shadow rays to acheive soft shadows. The lights are also capable of emitting photons, which enables photon tracing. In order to simulate the moon in my scene I also implemented directional lights.

Soft Shadows

Soft shadows are nice, but they make my renderer darn slow. Note the multiple shadows from two different colored lights.

Subsurface Scattering

In order to accurately represent the inside of a pumpkin I decided to implement a subsurface scattering approximation. Per Henrik's recommendation I opted against implementing his fast BSSRDF method. Instead, I used a method he apparently came up with off the top of his head while I was in his office which is based on a 3D Gaussian blur of radiance at mesh vertices.

The first pass of the algorithm is to calculate the radiance at each vertex in the mesh. I used my standard raytracer shaders to perform this calculation. I then stored these points of radiance in a kd-tree to be accessed later. The kd-tree is a good data structure for performing nearest neighbor searches, which will come in handy during the second pass of the algorithm.

The second pass is when I perfrom the Gaussian blur. All vertices are traversed once more. This time the kd-tree is queried for points of radiance in the area of each vertex. The area searched is a sphere with a radius of two standard deviations. This guaruntees that the radiance at any vertex will have less than 5% error, which is more than accurate enough considering this is already a rough approximation of subsurface light transport. The standard deviation for the Gaussian kernel can be specified as part of the material properties for the mesh object in the scene file.

Once all the contributing points of radiance are found, their radiance is weighted by a coefficient calculated using the normal Gaussian distribution function based on their distance from the center. The weighted radiances are summed up and the final radiance is scaled by the inverse of the sum of all Gaussian weights used in the sum. This final radiance is stored in the vertex object itself. During rendering the radiance at the three triangle vertices is interpolated barycentrically across the surface of the triangle.

This method produced satisfactory results for most objects, certainly for my pumpkin. I first tested it on simpler objects, however, such as the Stanford bunny.

A translucent bunny

The front of the bunny looks smooth and much more uniform than the diffuse version.

A translucent bunny

From the back you can see the scattering better. Note how the ears are illuminated all the way through.

Photon Mapping

My final scene would certainly show off nice effects such as global illumination and color bleeding. For this reason I decided to implement photon mapping. As I was already familiar with the kd-tree structure, this proved to be fairly straightforward. Since my scene is almost exclusively diffuse, the first thing I implemented was indirect lighting via diffuse reflections. At this point I rendered a cornell box with a direct visualization of the photon map.

Cornell Box

Note the color bleeding from the red and blue walls onto the white surfaces and in the shadow from the sphere.

For the final render I used the photon map only for indirect illumination.

Final Result

The result of all this work was a pretty cool render of a jack-o-lantern. Here it is.

Cornell Box

Click on the image for a larger version.

Acknowledgements

Most importantly, huge thanks to Mike Rossmassler for creating an amazing jack-o-lantern model for me. Mike was great to work with, as usual. Originally I was going to render a classic jack-o-lantern with a smiley face, but I like his interpretation much more. I'd also like to thank my roommate Ryan Brown for initially thinking that it would be cool to render a jack-o-lantern. Finally, thanks to Peter Schwer, the Master of Computer Science who accepted me as a padawan learner.