Nori: An educational ray tracer

As part of the Advanced Computer Graphics class, taught by Prof. Dr. Wenzel Jakob at EPFL, I worked on Nori: a minimalist ray tracer written in C++. The objective of the class was to lay the groundwork for an effective renderer by implementing its core features. We built on top of the Nori base code, which includes many pre-built components that would be laborious to create from scratch, such as a simple GUI for real-time image rendering, an XML-based scene file loader, and basic geometric primitives classes. Below are the various topics covered in the course and the corresponding features I implemented in the renderer.

TL;DR

I implemented the core features of a renderer written in C++, including:

  • Ray tracing acceleration data structures
  • Basic rendering algorithms: whitted-style, path tracer with multiple importance sampling
  • Homogeneous participating media
  • Light and materials modelisation
  • Textures and normal mapping

Ray tracing acceleration data structures

I implemented an octree, a hierarchical data structure designed to expedite ray intersection computations with large numbers of triangles. This step involved constructing the octree, optimising its data organisation for efficient memory usage, and sing it for ray tracing by performing an ordered traversal of the tree’s children from nearest to farthest.

I also focused on performance optimisation, employing the parallelisation abilities to enhance the efficiency and speed of the implementation.

Monte Carlo sampling

I implemented Monte Carlo sampling techniques to transform an initially uniform distribution into various target distributions. This involved computing and implementing both the distribution functions and the corresponding sample warping schemes.

This gave me a first hands-on experience in probabilistic modeling and numerical methods.

Basic rendering algorithms

I implemented several basic rendering algorithms including:

  • Ambient occlusion, a rendering technique that assumes that a surface receives uniform illumination from all directions and that visibility is the only effect that matters.
  • Whitted-style ray-tracer, which integrates the incident radiance by sampling points on a set of light sources and is able to account for specular reflections and refractions.
  • Path tracer with Multiple Importance Sampling, which enhances accuracy and efficiency by weighting the contributions from different sampling techniques based on their effectiveness in capturing various lighting scenarios.
Rendered using multiple importance sampling
Rendered using multiple importance sampling

Homogeneous participating media

I added to the renderer support for handling homogeneous participating media (media with constant density and scattering properties), mainly in order to render beams of light.

Light and materials modelisation

Throughout the project, I implemented various models of light sources and materials to be used to render interesting things and validate my other implementations.

Light sources

  • Point lights
  • Area lights
  • Specialised emitters (rectangular, spherical, and disk lights)
  • Image-based lighting

BRDFs

  • Dielectric
  • Microfacet
  • Thin dielectric
  • Mirror
Thin dielectric and dielectric spheres
Microfacet bowl

Textures and normal mapping

I ultimately added to the renderer a few “low-effort” features to be able to model more realistic looking scenes, notably the Ability to add textures and normal maps to the different meshes in the scene with different simple types of UV mapping.

Texture mapped on a sphere
Normal map and texture mapped on a sphere