CS 294-13 Lab 1: Path Tracing

Introduction

The purpose of this assigment was to create a Monte Carlo path tracer to numerically solve the rendering equation [Kajiya, 86]. Path tracing essentially by calculating the radiance along various random walks in the scene and combining the results, apropriately weighted by the probability of the walks.

The path tracer was written in Common Lisp, as an extension of t-ray, my pre-existing raytracer. In this document, I'll refer to the path tracer itself as simply "t-ray". All of the development and testing was done using SBCL 1.0.30.* on my Ubunutu machine, but as far as I am aware there is no implementation or platform-specific code.

You can download the code here. (You can also download some example models for use with t-ray here.) The code depends on the alexandria, iterate, metabang-bind, cl-mesh, and pcall. cl-mesh is a library I wrote to load .OBJ files and is included in with t-ray. pcall is available on its homepage. The other libraries are ASDF-installable. Readers of the code will probably want to start at path-tracer.lisp, which contains the core algorithm and lighting calculations.

Implementation

Algorithm

At each bounce, I perform sampling for direct and indirect lighting. To not overestimate the direct lighting contribution, then, emitted light added to the contribution when the ray is from a camera or a specular bounce from a BRDF with a delta distribution. (I actually don't have any pure specular BRDFs implemented yet, but for future reference this will be the case.) This method of partitioning contributions is used in pbrt, from which many of my design decisions were based [Pharr 04].

Instead of storing a the location of each bounce, I just keep an accumulated total of the radiance along the path. This would have to be changed if I decided to implement certain extensions to path tracing, such as bi-directional path tracing [Lafortune, 93].

t-ray is capable of keeping accounts of direct and indirect illumination separately. This is useful for illustrative purposes, but mainly it is a necessity to perform noise filtering.

Architecture

t-ray breaks down the image rendering in tasks by column, and uses a thread pool to render tasks in parallel. (Like ray tracing, path tracing is one of the easiest tasks to parallelize.) On my two-core machine, I typically get a 170%-190% CPU usage. This method could allow, at some point, for the renderer to display columns of the solution live as the rendering progressed. (One could also separate the execution into chunks of full-image size, with 1 sample per pixel each. This way, the user could see full image progressively decrease in variance.)

Features and Results

Unless otherwise specified, all images shown were rendered with t-ray's path tracer with no external post-processing. Russian Roulette was used to terminate paths with a 10% likelihood, starting after the third bounce. A maximum depth of 10 was also imposed. Also, only 1 sample per light is used.

Materials

t-ray has a material system which allows for linear combinations of BRDFs. Right now, only spatially-independent BRDFs are supported (which means no texture maps, etc.), but I plan to support them in the future. BRDFs can be added by implementing an evaluation function and a sampling method. For instance, Lambertian BRDFs sample from a cosine-weighted hemisphere. So far, I have implemented Lambertian, Phong, and Oren-Nayar BRDFs.

The Oren-Nayar BRDF is a generalization of the Lambertian BRDF, which treats a surface as collection of v-shaped cavities with Gaussian-distributed slopes [Oren, 94]. Each facet is assumed to be Lambertian. The resulting approximation function gives more realistic results, physically speaking, than the Lambertian model.

These scenes of a gargoyle is a illuminated by one disc-shaped area light. The left gargoyle had a Lambertiam BRDF, and the right had an Oren-Nayar BRDF, with a sigma of 20 degrees. Notice that the Oren-Nayar gargoyle has a less drastic light falloff around some of its edges.
The scenes were traced at 512x512 resolution, with 100 samples per pixel. Rendering time was 1h52m for the Oren-Nayar gargoyle, and a comparable time for the Lambertian image.

Every BRDF I've implemented has a tailored importance function, but I don't have an architecure yet for importance sampling arbitrarily-specified BRDFs. All of the materials used and their underlying BRDFs are implemented in brdf.lisp

Lightweight Bounding Volume Hierarchies

t-ray uses lightweight BVHs, as specified in [Cline, 06], to accelerate the rendering of triangle meshes. (At some point, LBVHs will be implemented for the entire scene). LBVHs are internally implemented as heaps, so they are guaranteed to be perfectly balanced and use significantly less memory than normal BVHs.

In my implementation, a branch factor of 4 is used, and one triangle is stored per leaf node. One can store more primitives per leaf node to save memory at the cost of increased rendering time.

Most of the implementation is in shapes/lbvh.lisp and shapes/trimesh.lisp

The gargoyle has 200k triangles and is lit with a large triangle area light.
~25 samples per pixel

Lighting

t-ray supports point lights, area lights, and (in a somewhat ad-hoc fashion) infinite area lights. Area lights can be created from any shape that can be sampled. (This currently includes spheres, discs, and individual triangles.) Infinite area lights can support arbitrary light distributions, but they are not importance samples, so they are only really useful for constant color sources.

The sphere above is lit with a gray infinite-area light source and a nearby yellow point light. You can see the shadow cast by the point light, but that area is not completely dark due to the infinite-area light.
This image was rendered at 400x300 resolution, with 1000 samples per pixel. Rendering time was ~1h20m.

Robust Integration

Samplers

t-ray allows you to choose the type of sampler you want to use for sampling camera rays or (non-delta) light sources. Currently, stratified sampling is implemented in two dimensions, and Latin Hypercube sampling is implemented in one, two, and four dimensions. Latin Hypercube sampling partitions the d-dimensional unit cube into an n by n by .. by n grid, and distributes jittered samples such that each axis-aligned hyperplane has exactly one sample. In simpler terms, it places the n samples in a d-dimensional Latin Hypercube, jittering each sample within its sub-unit.

LHS is convenient since there is no restriction on (n, d). Stratified sampling, by contrast, places a restriction that n = xd, which becomes an issue for d = 4. It also avoids the worst-case issue with stratified sampling, where n*d samples are projected into a small area. On the other hand, Veach's thesis shows that stratified sampling has a better variance convergence rate in two dimensions [Veach, 97], and the effectiveness of LHS only decreases as d increases (compared to stratified).

Anti-aliasing

By sampling the pixel area for individual pixels with computing camera rays, one can achieve anti-aliasing.

(Top left) Three spheres rendered on a plane with three lights. All camera rays are through the center of the pixels. (Top Right) The same image, with camera rays distributed through out the pixel. (Bottom Left) The same image as the top left, zoomed in. Significant aliasing artifacts are present on the edge of the sphere and its shadows. (Bottom Right) In the AA version, these artifacts are significantly reduced. The image was rendered at 400x300 resolution, with 400 samples per pixel. Rendering times were 55m for the anti-aliased image, ~43m for the non-AA image.
Soft Shadows

By sampling an area light source, we can render soft shadows in the image, rather than the hard shadows characteristic of basic Whitted ray-tracing.

The soft shadows are cast by an infinite area light. The image was rendered at 400x300, with 64 samples per pixel and 9 samples per light.
Depth of Field

By sampling the camera's aperture, t-ray can also simulate depth-of-field.

(Top Row) The same scene is rendered with an aperture width of 0.0, 1.0, and 2.0, all focused on the yellow sphere. (Bottom Left) Larger rendering of the scene, with an aperture of 2.0 and focused on the yellow sphere. (Bottom right) Same, but focused on the far gray sphere instead.
Top row images were rendered at 400x300 resolution, with 36 samples per pixel. Bottom row images were rendered at 640x480, with 81 samplers per pixel.

Noise Filtering

Path tracing images are subject to noise due to variance in the integration estimates. One way to reduce the variance is by simply applying a smoothing filter on the resulting image. However, this causes noticeable blurring in the image, particularly on edges. One alternative, proposed by Jensen in [Jensen, 95], is to apply a noise-reduction filter only to the contribution from at least two diffuse bounces. The paper gives empirical evidence that using a 3x3 low-pass on this portion gives an image with a lower RMS error than the original image (as compared to a reference image). t-ray implements this method for noise reduction.

(Left) The same image as the one referenced above in Lighting, but with only 50 samples per pixel. (Right) The left image with noise reduction filters applied as described. The difference is not prevalent, because indirect lighting contributes little to the scene, relative to direct lighting.

Other Results

(Top left) The Cornell box with a matte yellow sphere and a glossy gray sphere. (Top Right) Same image, with noise-reduction filter applied as described in Noise Filtering. (Middle Row) The indirect illumination-only components of the top row images. Note that the reflection of the room in the gray sphere is more apparent. (Bottom) The (very smooth) direct illumination component of the image. I think these images are too noisy because of the material I used for the light source, which had too strong of a diffuse component.
The images were rendered at 512x512 resolution with 100 samples per pixel. Since t-ray keeps track of the separate contributions, the image was only rendered once, and the components were reassembled as described. Rendering time was 5h56m.
(Left) The same image as described in Anti-aliasing. (Right) The scene rendered with a max ray depth of zero. Notice how the sphere bottoms are completely in shadow on the right image, whereas they are somewhat illuminated in the left image. Also, one can see the diffuse-diffuse reflection on the plane in front of the green sphere and behind the red sphere on the left, but not on the right.
Images were rendered at 400x300 reslution, with 400 samples per pixel.
The bunny in the foreground has both Lambertian and Phong BRDFs. Notice the specular highlights due to a nearby point light. The bunny is ~66k triangles, and the t-rex is ~100k triangles.
The image was rendered at 400x300 resolution with 64 samples per pixel and 4 samples per light. Rendering time was 7h20m.

Limitations and Future Directions

Currently, t-ray is much slower than I would like it to be. Implementing LBVH for the entire scene will improve this somewhat. The running times were also from a non-release build, so adding optimization flags to the compiler might have large impact on rendering times.

I would like to add a number of BRDFS to be able to simulate a wider variety of surfaces. Perfect specular BRDFs and BTDFs would be first, followed by more realistic microfacet models and Fresnel-weighted reflections. Combined with appropriate image-based and procedural texture maps, this would allow for a more diverse set of scenes to be rendered. More fundamentally, I would like to implement photon mapping or bi-directional path tracing. Due to the current setup, photon mapping might be easier to implement initially. (I actually already implemented a kd-tree for photon storage, though that code is not included here). Irradiacne caching would also greatly improve the image results.

Conclusions

t-ray was extended to implement path tracing. In addition to the required parts of the assignment, I implemented multiple forms of robust integration, multiple sampler types, noise reduction filtering, and lightweight bounding volume hierarchies for accelerated triangle mesh rendering.


References

Models were found from Hughes Hoppe, the Caltech Multi-Res Modeling Group, the National Research Council of Canada, and the Stanford 3D Scanning Repository via the Mesh Compendium.