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

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors