Tachyonic

Introduction

Tachyonic is a global illumination ray tracer implemented in C#. Here are some highlights of the ray tracer:

  • It implements several shaders including photon mapping with caustics, path tracing, and direct illumination.
  • Supports a generic kd-tree as the acceleration structure.
  • Multithreaded.
  • Monte Carlo sampling with stratification. Also supports QMC methods such as Sobol sequence.
  • Support for 3D Studio Max ASE objects.

Tachyonic is a global illumination ray tracer that I have been working on for several years. It does not receive the same amount of love and attention as it used to. But it is still a project that is near and dear to me.

I first implemented Tachyonic as a CS 490 (Supervised Independent Research) project under the supervision of Dr. Kavita Bala when I was a junior at Cornell. I presented the results in Bits On Our Minds 2003. The first version of Tachyonic was written entirely in C++ and STL.

Since then I have expanded Tachyonic as a rendering platform where I experiment with different rendering techniques.

Implementation

The photon mapping implementation of Tachyonic is based on the algorithm described in Henrik Jensen’s book, Realistic Image Synthesis with Photon Mapping. It also implements final gathering and irradiance caching as described in Per Christensen’s paper “Faster Photon Map Global Illumination.” Irradiance is precomputed and stored in a kd-tree. Whenever a final gather ray his a surface, the irradnace at that point is approximated using the precomputed values and returned. Therefore, in a depareture from traditional path tracing, it is possible to sample multiple rays over the hemiphere using importance and stratified sampling. I have found that using Photon Mapping with Final Gathering results in a much faster convergence than with path tracing or bidirectional light tracing. Photon Mapping does introduce bias, so I have a path tracer to compare results.

There are a few things that I have changed from the vanilla photon mapping algorithm. I do not use a projection map to concentrate photons toward specular objects. Instead, I use a rejection test to fill the casutic photon map. I first cast a predetermined amount of photons and then store the results in both the global and caustic photon maps. Then I lock the global photon map and then continue to cast more photons until the desired number of photons are stored in the caustic map. I take care to ensure that the flux that each photon carries is accurate.

The kd-tree implements the OSAH (Ordinary Surface Area Heuristic) cost function model and recursive ray traversal algorithm outlined in the paper “Heuristic Ray Shooting Algorithms.” I find this spatial data structure to be flexible and fast. It seems to be able to accomodate the widest range of scenes.

Currently I support two object primitives: triangles and spheres. The ray-triangle intersection code uses the Moller-Trumbore method without precomputed normals. However, I do precompute and store the E1 and E2 edges. I referred to the implementation given here.

The random number generator I used is based on M. Matsumoto and T. Nishimura’s article. Please refer to http://www.agner.org/random.

Rendered Images

Diffuse Cornell Box

Cornell Box with Caustics

Cornell Box with Ward Spheres

Ward Spheres

Knot

M-16 Model