Skip to content

skateddu/python-ray-tracing

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python Ray Tracing

Python License uv Ruff GitHub last commit

Educational ray tracer written in Python + NumPy + Numba, inspired by the NVIDIA article "Writing Ray Tracing Applications in Python Using Numba for PyOptix" and Peter Shirley's Ray Tracing in One Weekend.

Example render

Features

  • Shapes: sphere, infinite plane, triangle (Moller-Trumbore), axis-aligned cube (slab method), finite cylinder (quadratic + disk caps), triangle mesh (OBJ loader)
  • Textures: solid color, 3D checker pattern, image texture (UV-mapped)
  • Materials: Lambertian (diffuse), Metal (specular + fuzz), Dielectric (glass with Snell + Schlick), DiffuseLight (emissive) — accept Vec3 or Texture
  • Camera: configurable FOV, aspect ratio, look-at, depth of field (thin-lens model)
  • Rendering: recursive path tracing with emission, multi-sample antialiasing, sky gradient background
  • Acceleration: Bounding Volume Hierarchy (BVH) with longest-axis split
  • Numba JIT: @njit kernels for vec3 math, intersections, and color conversion (~3x speedup)
  • Output: PNG (Pillow) and PPM formats
  • CLI: render any scene from the command line, with JSON logging option

Project Structure

python-ray-tracing/
├── src/raytracer/
│   ├── vec3.py              # Vec3 operations on NumPy arrays
│   ├── ray.py               # Ray(origin, direction) dataclass
│   ├── camera.py            # Camera with FOV, DOF, look-at, aspect_ratio
│   ├── config.py            # Centralized settings (dataclasses)
│   ├── aabb.py              # AABB bounding box (slab test)
│   ├── base.py              # ABCs: Hittable, Material
│   ├── hit.py               # HitRecord (+ factory), HittableList
│   ├── materials.py         # Lambertian, Metal, Dielectric
│   ├── renderer.py          # Recursive ray_color + render loop
│   ├── bvh.py               # Bounding Volume Hierarchy
│   ├── color.py             # Gamma correction, sRGB conversion
│   ├── utils.py             # PNG/PPM output
│   ├── logger.py            # Centralized logging + JSONFormatter
│   ├── textures.py          # SolidColor, CheckerTexture, ImageTexture
│   ├── shapes/
│   │   ├── __init__.py      # Re-exports all shapes
│   │   ├── sphere.py        # Ray-sphere intersection
│   │   ├── plane.py         # Infinite plane
│   │   ├── triangle.py      # Moller-Trumbore (single-pass t+uv)
│   │   ├── cube.py          # Axis-aligned box (slab method)
│   │   ├── cylinder.py      # Finite cylinder (quadratic + disk caps)
│   │   └── mesh.py          # OBJ loader + TriangleMesh with BVH
│   └── jit/
│       ├── __init__.py      # NUMBA_ACTIVE flag + select() dispatcher
│       └── kernels.py       # @njit kernels
├── src/scenes/              # Demo scene builders
│   ├── shape_gallery.py     # All primitives: sphere, cylinder, cube, tetrahedron, plane
│   ├── three_spheres.py     # Diffuse, metal, glass on checker ground
│   ├── cornell_box.py       # Cornell box with two cubes + emissive ceiling
│   └── random_spheres.py    # 400+ spheres (RTIOW finale)
├── main.py                  # CLI entry point (argparse)
├── tests/                   # Unit tests (pytest)
├── notebooks/               # Interactive tutorial
├── assets/                  # Static resources (README images)
├── pyproject.toml
└── LICENSE                  # MIT

Dependencies

Package Description Link
NumPy N-dimensional arrays for Vec3 math and image buffers Docs
Numba JIT compiler — @njit kernels for hot-path acceleration Docs
Pillow PNG image output and texture loading Docs
Matplotlib Rendering visualization and image display Docs
tqdm Progress bar for the render loop GitHub

For notebook support add --extra notebook (installs ipykernel).

Installation

Requires Python >= 3.10 and uv.

git clone https://github.com/skateddu/python-ray-tracing.git
cd python-ray-tracing
uv sync

Quick Start

CLI

# List available scenes
uv run python -m raytracer list-scenes

# Render a scene
uv run python -m raytracer render --scene three_spheres

# Render with Numba acceleration (~3x faster)
uv run python -m raytracer --numba render --scene three_spheres

# JSON log output
uv run python -m raytracer --json-log render --scene shape_gallery

# Full options
uv run python -m raytracer render \
    --scene random_spheres \
    --width 800 \
    --spp 100 \
    --depth 50 \
    --output data/my_render.png

CLI Options

Option Default Description
--scene three_spheres Scene name (use list-scenes)
--width 480 Image width in pixels
--spp 25 Samples per pixel (antialiasing)
--depth 15 Max ray bounce depth
--output data/<scene>.png Output file path
--no-bvh BVH enabled Disable BVH acceleration
--numba disabled Enable Numba JIT (before render)
--json-log plain text Structured JSON log output

Python API

from raytracer.bvh import BVHNode
from raytracer.utils import save_png
from raytracer.renderer import render
from scenes import SCENE_REGISTRY

# Build a scene
camera, world = SCENE_REGISTRY["three_spheres"]()
world = BVHNode(objects=world.objects)

# Render
image = render(camera=camera, world=world, image_width=480, image_height=270, samples_per_pixel=25)
save_png(image_array=image, path="data/my_render.png")

Available Scenes

Scene Description
shape_gallery All primitives: sphere, cylinder, cube, tetrahedron, plane
three_spheres Three material types on checker ground: diffuse, metal, glass
cornell_box Cornell box with colored walls, emissive ceiling, and two cubes
random_spheres 400+ random spheres (RTIOW finale scene, uses DOF)

Architecture

The ray tracer maps to the standard GPU ray tracing pipeline, implemented on CPU:

graph LR
    A[Camera] -->|generate rays| B[Renderer]
    B -->|traverse| C[BVH]
    C -->|intersect| D[Shapes]
    D -->|closest hit| E[Materials]
    E -->|scatter| B
    B -->|miss| F[Sky Gradient]
    B -->|max depth| G[Black]
    B -->|final color| H[sRGB + PNG]
Loading
GPU Concept (OptiX) CPU Implementation Module
Ray Generation Program Camera + render loop renderer.py
Closest Hit Program Material scatter materials.py
Miss Program Sky gradient renderer.py
Acceleration Structure BVH with AABB bvh.py
Shader Binding Table Material-geometry via HitRecord hit.py
Numba JIT (CPU) @njit kernels + select() jit/

Numba Acceleration

The --numba flag (or RAYTRACER_USE_NUMBA=1 environment variable) replaces hot-path functions with @njit-compiled versions:

  • Vec3 math: dot, cross, normalize, reflect, refract, length, length_squared
  • Intersection kernels: sphere_intersect_t, triangle_intersect, aabb_hit_check
  • Color: linear_to_srgb_value, schlick_reflectance

The first run compiles the kernels (a few seconds of overhead). Subsequent runs use the disk cache (cache=True).

Testing

# Run all tests
uv run pytest

# Run a specific test file
uv run pytest tests/test_shapes.py -v

# Run tests matching a pattern
uv run pytest -k "sphere" -v

References

About

A Python ray tracer built from scratch using NumPy and Numba JIT acceleration. Features BVH traversal, multiple materials, and geometric primitives.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors