Edd Biddulph

Twitter | CV


September 2010
Pathtracing on GPU

This is a CUDA module which renders an image using unidirectional pathtracing. If you are not familiar with CUDA, please follow the link at the bottom of this page. It is a platform for general-purpose computing on NVIDIA graphics cards.

Scenes can be described using a basic plaintext format. The viewing position and orientation are configurable. The available primitives are sphere, (axis-aligned) cuboid, and plane. Available material features are importance-sampled lambertian diffuse, refractive glass-like appearance and specular reflection with gloss effects (also importance-sampled). A few example scenes are provided, which may be tried out by passing the name of the scene file as a single argument to the executable on a commandline.

Depth-of-field, large distant light sources

The plaintext scene format provides a set of single-character commands:

f <focal distance> <circle of confusion radius>

Sets the lens attributes

v <camera position> <camera direction>

Sets the viewing configuration

m <material id> <diffuse colour> <emissive colour> <specular colour> <reflectivity> <refractive index> <glossiness>

Creates a material

s <material id> <position> <radius>

Creates a sphere

c <material id> <minimum point> <maximum point>

Creates a cuboid

p <material id> <normal vector> <distance>

Creates an infinite plane

Comments can be made by starting a line with "//" in the style of C++ and Java (for instance).

This format was not really designed to be used by a human author. It is more suitable for emission by another program. However, the simplicity of the format made the parser very easy to implement and I was quickly able to start producing scenes for the renderer to process.

The renderer anti-aliases the image by taking multiple samples within a pixel. However, this amounts to a box filter of the image which still leaves harsh edges where there is a high amount of energy arriving at the viewing plane. This can be rectified by the use of a filter with a multi-pixel support, such as a Gaussian, or Mitchell kernel.

More DoF! Bad choice of colours though...

Since this project utilises CUDA but was written to be compiled by the GNU C++ compiler, at the time of development I had to use the CUDA driver API. I was not able to use the higher-level API as this was only available to developers using Microsoft Visual C. The raytracer written into the device kernel does not use any acceleration structure, but instead iterates through each primitive and calculates the nearest intersection point before generating the reflected ray direction from an importance-sampled lambertian BRDF and reflection probability. Russian roulette was not employed, as I did not use a sophisticated random number generator for this renderer. I used a series of hashes which were tweaked to reduce correlation, and this resulted in me enforcing a maximum for the number of ray reflections per pixel. Each reflection has a different hashing function associated with it. I designed the hashes by multiplying large prime numbers with the component values and performing an xor reduction of these products before taking the final value modulo another large prime number. Looking back, it might not have been too difficult to take an existing general-purpose RNG and implement it on GPU and in fact this has already been done with the popular mersenne twister RNG algorithm. The reflection count maximum does well to maintain high warp saturation over multiple passes, but prevents the correct imaging of complex scenes, especially those with many interacting specular surfaces. Furthermore, because of this the rendering algorithm is biased.

Large environmental lighting converges rapidly.

This program uses some functions of the CUDA Driver API, which according to NVIDIA's documentation are now deprecated. I'd like to convert those calls into ones to non-deprecated functions at some point. Also, I have had some trouble getting this project to compile properly for 64-bit architectures, although I seem to have resolved the problem for now. NVCC seems to have some issues with Visual C++ installation (or vice versa). After the difficulties I have had with setting up the required environment for development with NVCC and GNU, I have decided that my future developments in GPGPU should either be with Visual C++ proper (i.e. using Microsoft's IDE and tools for the entire project), or with OpenCL. I'm pretty sure this would make those programs more portable, too.

Download - Includes source, Windows 32-bit executable, example scenes, and a couple of Perl scripts which I used to produce some of the example scene files. Source code is licensed under the zlib license.

http://www.cs.princeton.edu/courses/archive/fall02/cs526/papers/kajiya86.pdf - James T. Kajiya, The Rendering Equation
http://www.nvidia.com/object/what_is_cuda_new.html - NVIDIA CUDA