A basic CPU ray tracer built with modern C++17, SDL3, and GLM.
This project is a basic ray tracing system written in modern C++17, using:
- SDL3 for window creation and displaying pixels
- GLM for vector and matrix math
- A custom CPU path tracer with:
- Multiple materials (Lambertian, Metal, Dielectric, Emissive)
- Recursive reflections and refractions
- Multi-sample anti-aliasing
- Sky gradient background
- Infinite ground plane and many randomized spheres
The code is structured around a small set of clear classes:
Cameraโ generates rays for each pixelSceneโ stores objects and performs ray tracingSphere/Planeโ geometric primitivesMaterialand derived types โ control surface appearanceRenderer/Framebufferโ manage SDL rendering and pixel buffers
RayTracer/
โโโ Build/ # Runtime DLL(s) (e.g., SDL3.dll)
โโโ Source/ # All C++ source files
โ โโโ Camera.{h,cpp}
โ โโโ Color.h
โ โโโ Framebuffer.{h,cpp}
โ โโโ Material.{h,cpp}
โ โโโ Math.h
โ โโโ Object.h
โ โโโ Plane.{h,cpp}
โ โโโ Random.h
โ โโโ Ray.h
โ โโโ Renderer.{h,cpp}
โ โโโ Scene.{h,cpp}
โ โโโ Sphere.{h,cpp}
โ โโโ Transform.h
โ โโโ Main.cpp
โโโ ThirdParty/
โ โโโ SDL3/ # SDL3 headers and libraries
โ โโโ glm/ # GLM math library
โโโ RayTracer.sln # Visual Studio solution
โโโ RayTracer.vcxproj # Project file
โโโ RayTracer.vcxproj.filters
โโโ README.md # This file
-
CPU-based ray tracer / path tracer
- Recursive ray tracing with configurable max bounce depth (default: 7)
- Randomized hemisphere sampling for diffuse reflection
- Glass / dielectric refraction with Schlick-style reflectance
-
Multi-sample anti-aliasing
- Default: 250 samples per pixel (spp)
- Jittered sampling within each pixel (stochastic AA)
-
Gradient sky background
- Blends between
skyBottomandskyTopbased on ray direction - Configurable via
Scene::SetSky
- Blends between
-
Primitives
SpherePlane(used as infinite ground plane)
-
Transform system (
Transform)- Position / rotation (quaternions) / scale
- Helper functions for forward/up/right vectors
- Matrix generation for transforms (even if not heavily used in this minimal ray tracer)
Implemented in Material.h / Material.cpp:
Lambertianโ diffuse materialMetalโ reflective material with adjustable fuzzinessDielectricโ glass/transparent material with IOR and Schlick reflectanceEmissiveโ light-emitting material
Each material provides:
bool Scatter(
const ray_t& incident,
const raycastHit_t& hit,
color3_t& attenuation,
ray_t& scattered
) const;and optionally:
color3_t GetEmissive() const;In Main.cpp, the scene is created as:
-
Infinite ground plane (Lambertian)
-
A dense grid of small spheres with randomized positions and materials:
- Diffuse spheres with random HSV colors
- Metallic spheres with random albedo and fuzz
- Glass spheres with tinted dielectric material
-
Three larger โfeatureโ spheres:
- Center: glass (
Dielectric) - Left: diffuse (Lambertian)
- Right: metal (
Metal)
- Center: glass (
The final render (shown below) is produced at 1920ร1080, 250 spp, and a maximum recursion depth of 7 bounces.
-
Operating System: Windows 10/11
-
Compiler/IDE: Visual Studio 2019 or 2022 (x64)
-
No external installs required if using the provided
ThirdPartyfolder:SDL3(inThirdParty/SDL3)glm(inThirdParty/glm)
-
Open
RayTracer.slnin Visual Studio. -
Select:
- Configuration:
DebugorRelease - Platform:
x64
- Configuration:
-
Build with Build โ Build Solution or
Ctrl + Shift + B.
โ ๏ธ If the generated executable cannot findSDL3.dll, ensure a copy ofSDL3.dllfrom theBuild/folder is placed next to your built.exe(or addBuild/to your PATH or working directory setup).
- Set the RayTracer project as the Startup Project.
- In Project Properties โ Debugging, set Working Directory to the folder where your executable and
SDL3.dlllive (commonly$(SolutionDir)x64\Debugor$(SolutionDir)x64\Release). - Press F5 (run with debugger) or Ctrl+F5 (run without debugger).
- Locate the built executable (e.g.
x64/Release/RayTracer.exe). - Make sure
SDL3.dllis in the same folder or in your PATH (you can copy it fromBuild/). - Run the executable:
RayTracer.exeThe program:
- Opens an SDL window (default 1920ร1080)
- Performs the ray tracing render once
- Displays the final image and keeps the window open until you close it
The ray tracer renders a single still frame and does not currently support interactive camera movement.
- Close the window using the OS window close button
- Press Escape (
Esc) to exit
Camera position and target are set in Main.cpp:
float aspectRatio = framebuffer.width / (float)framebuffer.height;
Camera camera(70.0f, aspectRatio);
camera.SetView({ 0, 2, 5 }, { 0, 0, 0 }); // eye, targetAdjust these values to change the view.
Rendererโ wraps SDL initialization, window creation, andSDL_Renderer, and presents the framebufferFramebufferโ CPU pixel buffer + streaming texture update to SDLCameraโ computes view rays using a pinhole camera modelSceneโ stores objects and implements the recursiveTracefunctionSphere/Planeโ geometric primitives implementing intersection logicMaterialโ base class for shading behavior
Inside Scene::Render:
for (int y = 0; y < framebuffer.height; y++) {
for (int x = 0; x < framebuffer.width; x++) {
color3_t color{ 0 };
for (int i = 0; i < numSamples; i++) {
glm::vec2 pixel{ x, y };
pixel += glm::vec2{ random::getReal(), random::getReal() }; // jitter
glm::vec2 point = pixel / glm::vec2{ framebuffer.width, framebuffer.height };
point.y = 1 - point.y; // flip Y
ray_t ray = camera.GetRay(point);
color += Trace(ray, 1e-3f, 100.0f, 7); // 7 bounces
}
color = GetAverageColor(color, numSamples);
framebuffer.DrawPoint(x, y, ColorConvert(color));
}
}Key aspects:
- Anti-aliasing: 250 jittered samples per pixel
- Max recursion depth: 7 bounces per ray
- Small epsilon offset to prevent self-intersection artifacts
-
Find nearest intersection among all objects
-
If hit:
- Ask material to scatter ray (or emit light)
- Recursively trace scattered ray and apply attenuation
-
If no hit:
- Return gradient sky color:
glm::vec3 direction = glm::normalize(ray.direction);
float t = (direction.y + 1) * 0.5f;
color3_t color = glm::mix(skyBottom, skyTop, t);Save the screenshot you shared into your repository (for example at
docs/images/raytracer_250spp_7bounces.png) and ensure the path below matches.
Ray-traced image rendered at 1920ร1080, 250 samples per pixel, maximum depth 7 bounces.
This project uses:
- SDL3 โ windowing, input, and rendering
- GLM โ math library for vectors, matrices, and color conversions
Both are included under the ThirdParty/ directory. Refer to each libraryโs own license files or official repositories for detailed terms.
This ray tracer is a compact, self-contained example of:
- CPU-based ray tracing / path tracing in modern C++
- Multi-sample anti-aliasing (250 spp)
- Recursive reflections and refractions (up to 7 bounces)
- A lightweight material and scene system
Itโs a solid starting point for experimenting with:
- New primitives (boxes, meshes)
- More advanced BRDFs and texturing
- Area lights and global illumination tricks
- Multi-threading / tiling for faster renders
Have fun bending rays. ๐ฆโจ
ยฉ 2025 Cody Owens All rights reserved.
README authored with assistance from ChatGPT (GPT-5.1 Thinking) by OpenAI.
Third-party libraries and assets are the property of their respective owners and are used under their own licenses.