ShirleyTracer

ShirleyTracer is an archived C++ path tracer built while working through Peter Shirley, Trevor David Black, and Steve Hollasch’s Ray Tracing in One Weekend series.

It was my first substantial offline-rendering project after earlier work in real-time OpenGL rasterisation. The goal was not to design a production renderer, but to implement the sequence of ideas in the books directly enough that I could understand the mechanics of ray generation, scattering, recursive light transport, texture lookup, acceleration, volumetric media, and importance sampling from the ground up.

The main branch now contains the completed book-three implementation. Earlier branches are preserved as checkpoints through the series.

This repository records an early stage of my graphics-programming path. It is useful as evidence of the rendering concepts I implemented and studied, but the C++ structure is not representative of my current engineering standards.

My current renderer work continues in Indus, a multithreaded C++23 CPU path tracer with a more deliberate renderer architecture, BSDF system, SAH BVH acceleration, progressive preview, concurrent scheduling, and runtime instrumentation.

At a glance

Rendering model Recursive Monte Carlo path tracing following the Shirley book series.
Geometry Spheres, quads, translated/rotated objects, object lists, and BVH nodes.
Acceleration Axis-aligned bounding boxes and BVH traversal.
Materials Lambertian diffuse, metal, dielectric, diffuse emissive, and isotropic media scattering.
Textures Solid colour, checker, image-backed texture loading, and procedural Perlin noise.
Camera and image Positionable camera, defocus blur, motion blur, anti-aliasing, gamma-corrected PPM/PNG output.
Lighting Emissive geometry, explicit light lists, cosine PDFs, object PDFs, sphere PDFs, and mixed PDFs.
Volumes Constant-density participating media for smoke/fog-like effects.
Runtime Optional row-segmented multithreaded rendering using std::thread.

What I implemented

Ray tracing core

  • Ray, vector, point, colour, and interval primitives.
  • Recursive radiance evaluation with configurable maximum depth.
  • Per-pixel stochastic sampling for anti-aliasing.
  • Gamma-corrected colour output.
  • Scene traversal through a common hittable/world-object interface.
  • Hit records carrying intersection point, surface normal, material, texture coordinates, and front-face orientation.

Camera and image generation

  • Configurable image dimensions, samples per pixel, and maximum bounce depth.
  • Camera placement through look-from, look-at, up-vector, field-of-view, and focus-distance controls.
  • Thin-lens defocus blur through disk sampling.
  • Motion blur through time-sampled rays and moving objects.
  • PPM image output with ImageMagick conversion to PNG.
  • Optional multithreaded rendering by splitting image rows across hardware threads.

Materials and scattering

  • Lambertian diffuse scattering with cosine-weighted behaviour.
  • Fuzzy metallic reflection.
  • Dielectric reflection/refraction with Schlick-style Fresnel approximation.
  • Diffuse emissive materials for light-emitting geometry.
  • Isotropic scattering for constant-density participating media.
  • Scatter records that separate attenuation, emitted radiance, specular paths, and PDF-driven scattering.

Geometry and acceleration

  • Sphere geometry, including static and moving spheres.
  • Quad geometry used for Cornell-box style scenes and area lights.
  • Translation and Y-axis rotation wrappers for scene objects.
  • Axis-aligned bounding boxes for objects and object collections.
  • BVH construction over hittable objects to reduce intersection cost.

Textures and procedural detail

  • Constant-colour textures.
  • Checker textures.
  • Image-backed textures using stb_image.
  • Perlin noise with shuffled permutation tables.
  • Turbulence-style procedural noise used for marble-like surfaces.

Direct lighting and importance sampling

  • Explicit light-object lists passed into the camera render path.
  • Probability-density interfaces for direction sampling.
  • Cosine hemisphere PDFs for diffuse scattering.
  • Object PDFs for sampling directions towards hittable light geometry.
  • Sphere PDFs and mixed PDFs.
  • Mixture sampling between material scattering and light sampling to reduce noise in direct-light scenes.

Volumes

  • Constant-density medium wrapper around boundary geometry.
  • Exponential-distance sampling through media.
  • Isotropic scattering material for smoke/fog-like effects.
  • Cornell-box style experimentation with volumetric objects.

Rendering flow

At a high level, the completed implementation renders each frame like this:

 1Build scene and light list
 2        |
 3        v
 4Configure camera and sampling parameters
 5        |
 6        v
 7Split image into row ranges for worker threads
 8        |
 9        v
10For each pixel sample:
11  generate camera ray with lens/time samples
12        |
13        v
14Trace ray through world/BVH
15        |
16        +--> miss: return background radiance
17        |
18        +--> hit emissive surface: accumulate emitted radiance
19        |
20        +--> hit scattering surface/medium:
21               sample material and/or light PDF
22               update attenuation and recurse
23        |
24        v
25Average samples, gamma correct, write image
26        |
27        v
28Convert PPM output to PNG through ImageMagick

Source layout

The project is primarily header-driven and organised around small renderer components:

1src/
2  main.cpp                         Scene selection and executable entry point
3  dep/stb_image.h                  Third-party image loader
4  headers/base/                    Ray, vector, camera, material, texture, PDF, BVH, AABB, utility code
5  headers/materials/               Lambertian, metal, dielectric, emissive, and isotropic materials
6  headers/textures/                Solid colour, checker, image, and noise textures
7  headers/world_objects/           Spheres, quads, volumes, transforms, and object collections

Building

The project was developed on Windows with Visual Studio.

Requirements

  • Visual Studio with C++ desktop-development support.
  • C++20-capable MSVC toolchain for the current code style and standard library use.
  • ImageMagick available on PATH if you want automatic PPM-to-PNG conversion through the existing magick convert image.ppm render.png call.

The repository includes the Visual Studio solution and project files used during development.

Visual Studio

  1. Clone the repository.
  2. Open WeekendRT.sln.
  3. Select an x64 configuration.
  4. Build and run the executable.

The active scene is selected in src/main.cpp by choosing which render function is called.

Project context

This was the project that gave me my first working mental model of offline rendering. It made the basic path-tracing loop concrete: camera rays, intersections, material scattering, emitted light, random sampling, recursive radiance estimates, and image accumulation.

It also exposed the limits of simply following a tutorial architecture. By the end of the third book I had implemented many important rendering features, but I also understood that I needed stronger foundations in modern C++, numerical robustness, sampling theory, acceleration structures, and renderer design. That gap is what eventually led me to build Indus as a more independent renderer project.

Status

This repository is archived and not under active development.

It is retained as a record of my first complete path-tracing study project and as a milestone between my earlier OpenGL work and my current physically based rendering work in Indus.

References and third-party software

Primary reference:

Third-party software:

  • Sean Barrett’s stb_image for image texture loading.
  • ImageMagick for converting generated PPM images to PNG.