miniRT - ray tracer in C
  • C 97.9%
  • Makefile 2.1%
Find a file
2026-08-05 12:59:41 +02:00
include miniRT initial commit 2026-08-03 15:29:47 +02:00
scene modified .gitignore, adding scene/** .rt files 2026-08-05 12:59:41 +02:00
src miniRT initial commit 2026-08-03 15:29:47 +02:00
.gitignore modified .gitignore, adding scene/** .rt files 2026-08-05 12:59:41 +02:00
Makefile miniRT initial commit 2026-08-03 15:29:47 +02:00
README.md miniRT initial commit 2026-08-03 15:29:47 +02:00

This project has been created as part of the 42 curriculum by clao, vicli.

miniRT

A ray tracer written in C, built from scratch as part of the 42 curriculum. miniRT renders 3D scenes described in .rt files, with an interactive window for real-time camera and object manipulation.


Description

miniRT implements the ray tracing rendering technique: for each pixel on screen, a ray is cast from the camera into the scene, and the color is computed by solving intersections with geometric objects and evaluating the Phong lighting model at each hit point.

Supported geometry

  • Sphere — quadratic intersection, UV mapping for texture and checkerboard
  • Plane — single dot-product intersection
  • Cylinder — quadratic intersection with capped ends, full UV mapping
  • Cone — quadratic intersection with base cap, full UV mapping

Lighting

The renderer implements the full Phong reflection model:

  • Ambient light — constant base illumination, never fully dark
  • Diffuse lighting — Lambertian shading based on surface normal and light direction
  • Specular reflection — highlights controlled per-object by a specular coefficient
  • Hard shadows — shadow rays cast from each surface point to each light source
  • Multiple colored lights — additive, each with independent brightness and color

Bonus features

  • Checkerboard pattern — UV-based procedural texture on any object
  • Bump mapping — procedural Perlin-noise bumpmap perturbs surface normals before lighting, applied per-object
  • Specular reflection — full Phong model (specular coefficient in .rt file)
  • Colored and multi-spot lights — multiple L entries, each with RGB color
  • Cone — second-degree object with cap, full lighting and UV support

Instructions

Requirements

This project uses MiniLibX (cloned automatically by the Makefile) and requires:

  • libXext, libX11 (X11 development headers)
  • zlib

On Fedora / 42 school machines these dependencies are already present.

Building

make

The Makefile automatically clones and builds MiniLibX if not already present.

Running

./miniRT <scene.rt>

The .rt extension is required. If the file is missing or malformed, the program prints Error\n followed by an explicit message and exits cleanly.

Scene file format

Each line starts with an identifier. Fields are space-separated; position and color values use commas.

# Ambient light: identifier  brightness[0,1]  R,G,B
A 0.2 255,255,255

# Camera: identifier  x,y,z  orientation_x,y,z[-1,1]  fov[0,180]
C 0,2,0 0,0,1 70

# Light: identifier  x,y,z  brightness[0,1]  R,G,B
L -40,50,0 0.6 255,255,255

# Sphere: identifier  x,y,z  diameter  R,G,B  [specular>=0]  [checkerboard{0,1}]  [bump{0,1}]
sp 0,0,20 10 255,0,0 50 1 0

# Plane: identifier  x,y,z  normal_x,y,z  R,G,B  [specular>=0]  [checkerboard{0,1}]  [bump{0,1}]
pl 0,0,0 0,1,0 0,0,225 0 0 0

# Cylinder: identifier  x,y,z  axis_x,y,z  diameter  height  R,G,B  [specular>=0]  [checkerboard{0,1}]  [bump{0,1}]
cy 50,0,20 0,0,1 14.2 21.42 10,0,255 30 0 1

# Cone: identifier  x,y,z  axis_x,y,z  diameter  height  R,G,B  [specular>=0]  [checkerboard{0,1}]  [bump{0,1}]
co 0,5,16 0,1,0 4 6 255,255,0 20 0 0

Bonus fields (specular, checkerboard, bump) are optional and can be omitted entirely for mandatory-only scenes. specular is a positive float (Phong exponent). checkerboard and bump are 0 or 1.

Elements can appear in any order. Blank lines are ignored. Exactly one A and one C are required.

Interactive controls

The window supports real-time manipulation with a blocky preview during interaction, followed by a full-quality render after a short delay.

Camera:
  Alt + left_drag         → pan & tilt (Rodrigues' rotation)
  Space + left_drag       → truck & pedestal (along camera axes)
  Space + right_drag      → dolly (along camera forward axis)
  scroll                  → FOV zoom

Object (click to select):
  T + left_drag           → truck & pedestal
  T + right_drag          → dolly
  R + left_drag           → pan & tilt
  R + right_drag          → roll
  S + left_drag           → resize diameter (x) and height (y)

Light:
  L + left_drag           → truck & pedestal
  L + right_drag          → dolly
  L + scroll              → adjust brightness
  < or ,                  → select previous light
  > or .                  → select next light

ESC                       → close window and quit

Approach

Ray tracing pipeline

For each pixel, canvas_to_viewport constructs a ray from the camera position through the viewport using the camera's local basis (front, right, up vectors derived from the orientation). The ray is tested against all objects; the closest positive hit is shaded.

Camera system

The camera uses a 3-vector basis computed each frame from orientation:

basis[FRONT] = normalize(orientation)
basis[RIGHT] = cross(safe_world_ref(FRONT), FRONT)
basis[UP]    = cross(FRONT, RIGHT)

safe_world_ref returns world up (0,1,0) normally, switching to world back (0,0,-1) or world front (0,0,1) when the camera looks straight up or down, respecting the left-hand rule. All camera and object manipulation gestures operate in this local frame.

Rotation

Camera and object rotations use Rodrigues' rotation formula:

rot = v·cosθ + (k×v)·sinθ + k·(k·v)·(1cosθ)

Rotation is handled by rodrigues_rotation(v, k, delta, rot_speed). Pole singularities are handled with a two-layer defense: a pole guard blocks rotation further into the pole and zeroes out pan at the pole, and orientation_snap catches any result that reaches NEAR_PARALLEL and snaps cleanly to the pole axis. The snap axis is world up (0,1,0) for camera tilt, basis[UP] for object tilt, and basis[FRONT] for object roll.

Cylinder and cone intersections

Both reduce to a quadratic in the ray parameter t, solved with the discriminant method. A projection test (proj = dot(D,V)*t + dot(X,V)) restricts hits to the valid height range. Caps are plane intersections with a radius check.

Bump mapping

A 2000×2000 Perlin noise height map is generated once at startup using a gradient grid and bicubic interpolation. At each surface hit, UV coordinates are computed and the map is sampled at the hit point and at two small offsets to estimate dh/du and dh/dv. A tangent/bitangent frame is built from the geometric normal and the gradient is used to perturb the normal before Phong shading.

Render modes

Two modes are used for interactivity:

  • FULL — every pixel is individually ray-traced
  • MINI — 8×8 block pixels give a fast preview during manipulation; a timer fires a full render 600ms after the last gesture input

Parser

The parser reads the .rt file line by line with a custom GNL implementation, validates each field in a first pass (check_* functions), counts object types, allocates exact-sized custom arrays (with a header storing len and capacity), then fills the structs in a second pass.


Resources

References

Ray tracing theory

Mathematics

MiniLibX

How AI was used

Claude (claude.ai) was used throughout this project as a Socratic learning partner — not to generate solutions, but to guide understanding through targeted questions and hints. Specifically:

clao's usage (ray tracing geometry, camera system, hooks):

  • Explaining the mathematics of ray-object intersection (sphere, plane, cylinder, cone quadratics)
  • Stepping through Rodrigues' rotation formula and its edge cases (parallel axis collapse)
  • Analyzing camera rotation singularity at the pole: vacillation, blocked tilt near straight up/down, and pole guard design
  • Clarifying bump mapping theory: UV computation, tangent/bitangent frame construction, normal perturbation

vicli's usage (parsing, lighting, bump mapping):

  • Explaining how bump mapping works