Ray Tracing Arbitrary Meshes
Introduction
This was originally a project I did for a computer graphics course at Northwestern. The extent of the project was to create a ray tracer capable of rendering simple objects like spheres, disks, and cubes. I found this to be... unsatisfying.
Instead, I decided to write a ray tracer capable of rendering arbitrary triangle meshes. You can find the code for this project here, and below is an edited account of my project report.
Project Report: Ray Trace The World
With this project I really wanted to try to implement as general and capable of a ray tracer as possible. I also wrote almost all of the code from scratch, not using any starter code except very rarely as a reference. The majority of my codebase is written in TypeScript, with a small portion written in C which gets compiled down to WebAssembly. There are also shaders obviously written in GLSL. Vertex array objects are stored as JSON files.
The shaders include traditional Phong lighting as well as mesh ray tracing with Phong lighting. It also is important to me that everything be as general as possible, so things like scenes, lights, and meshes are very easy to add (as opposed to something hardwired like in the original project starter code).
Users can move around using WASD and look around using arrow keys. The next most important key is T, which ray traces the scene. If you open the console you can see a kind of percentage remaining countdown for tracing, as some traces can take a while depending on hardware. M and N are used to increase and decrease the number of reflections respectively. P and O similarly increase and decrease supersampling. J iterates through jitter modes. There are radio buttons to select between scenes and checkboxes to toggle individual light sources on and off.
The mesh geometries are automatically created as bounding volume hierarchies (using spheres), which exhibit tree-like structure and amortize the number of intersection checks. Without this improvement, some of the renders presented here might have taken days or longer.
Here is a simple render:
Note that the left is traditional rendering and the right is ray traced.
You can see in the traditional render that some blue light appears on the inside of the handle, but in the ray traced image this does not happen as the body of the teapot is occluding that geometry and casts a shadow.
Here's another render:
This render demonstrates tracing, Phong lighting, Phong materials, and mirror reflectance on three different mesh geometries. You can clearly see the reflection of the bunny in both the bear and the teapot, and a reflection of the bear and ground grid in the teapot. You can even see a reflection of the ground on the teapot on the bear, in two different places (forehead and chest). This render was generated at 1024x1024 resolution with no jitter, 6x6 supersampling, and 3 levels of recursive reflection. At this high of a quality it took 48 minutes on my desktop CPU, clearly demonstrating why GPUs now have dedicated hardware components for ray tracing.
This image is the same as the previous one except with 8x8 supersampling and 4 reflections. This one took around 6 hours to render, but to me it was well worth the wait.