A raytracer built in Haskell.
Run the raytracer with:
cabal run raytracer <config-file.json> +RTS -N -qg -A32M -RTS
The config file lets you customize:
- Resolution & sampling
- Camera & scene setup
- Lighting
- Raytracer settings (e.g. BVH)
This project is documented as a progressive journey. The steps are grouped in own files:
- Creating the Image - Basic PPM output & gradients
- Progress Bar - Rendering feedback & performance tracking
- Vec3 Module - First vector operations and math foundations
- Drawing a Circle - First shape before proper ray-sphere logic
- The Ray Module - Definition of rays and ray evaluation
- Adding a Camera - Camera and background gradient
- Sphere Intersection - Algebra and rendering a sphere
- Surface Normals & Perspective Projection - Visualization of normals and perspective
- Ray-Sphere Simplification & Profiling - Simplyfing the ray-sphere intersection and profiling the Raytracer
- Hittable Abstraction - Polymorphism for objects
- Multiple Objects - Scene with many spheres
- Fixing Perspective - Distortion & focal length
- Front vs Back Faces - Handling ray-side normal direction
- Anti-Aliasing - Reducing jagged edges
- Diffuse Materials - (WIP) Lambertian surfaces
- Buffered Writing - Optimizing memory usage
- Config Files - Switching from CLI args to JSON configs
- Optimizations - Russian Roulette, .OBJ import
- Monkey Render - Rendering Blender monkey with triangles
- BVH (Bounding Volume Hierarchy) - Huge speedup via spatial acceleration
You can check out my complete gallery on raytracing.omeldar.com
Some of my favorites are:
Spheres with lights and shadows. High res and high Anti-Aliasing. |
Shadows in a 988 triangles scene. 1920x1080, AA-50 |
Shadows in a 63,432 triangles scene. 1920x1080, AA-100 |
-
Soft Shadows via Multi-Light Sampling
Implement soft shadows by spawning multiple sample lights per light source. The number of samples will be configurable through the config file. -
Light Intensity Control
Clamp or scale light intensity per light source to avoid overexposed surfaces (e.g., fully white patches). -
Distance-based lighting
Light should take less effect when its further away.
- Smooth Shading for Triangles
Add support for smooth shading using vertex normals (3 normals per triangle), enabling smooth transitions between adjacent triangle surfaces.
-
Function Profiling and Optimization
Use GHC’s profiler (+RTS -p
) to identify hotspots. Refactor small bottleneck functions (e.g.,dot
,normalize
,hit
checks). -
Memoization of Expensive Calculations
Cache repeated computations like material scattering, vector math, or normalized directions. Anti-Aliasing can be improved as well by caching pixels in active computation areas (careful because of RAM). -
Adaptive Sampling Improvements
Adjust per-pixel sample count based on variance in early samples to save render time.
-
Caustics and Colored Shadows
Allow materials to bend light causting colored / caustic shadows. -
Configurable absorption for materials
Implement configurable absorption per object for exponential falloff in dielectric objects. -
Material Assignment and Parsing
Allow materials to be assigned per object, including support for .mtl file parsing from .obj models. Optionally, override all materials via the config for batch control.
-
UV Parsing from OBJ Files
Extendobj
parser to supportvt
texture coordinates andf
face triples(v/vt/vn)
. -
Textured Triangles
Interpolate UVs using barycentric coordinates per hit. Sample from image texture. -
Image Texture Loader
UseJuicyPixels
to load.jpg/.png
into aTexture
data structure. -
Skydome from Blender
Import a textured inverted sphere from Blender as a background. Ensure correct UVs are used.
-
Lighting system & shadows
Objects cast shadows onto other objects by checking if light source is blocked. -
Material system
Objects can have material which cause them to react differently to light. -
Reflections (Metallic Surfaces)
Implement reflective behavior for metallic surfaces using the material system. -
Refractions (Glass-like Materials)
Add support for transparent, refractive materials with configurable IOR (index of refraction). -
Support for Multithreading
Split scanlines or pixel blocks across threads using Haskell’s parallel strategies orasync
/parMap
. Improve core utilization.