- C 97.9%
- Makefile 2.1%
| include | ||
| scene | ||
| src | ||
| .gitignore | ||
| Makefile | ||
| README.md | ||
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
.rtfile) - Colored and multi-spot lights — multiple
Lentries, 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)·(1−cosθ)
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
- Computer Graphics from Scratch
- Basic Raytracing
- Linear Algebra
- Ray-Sphere Intersection
- Ray-Plane and Ray-Disk Intersection
- Introduction to Shading
Mathematics
- Raytracing shapes
- Rodrigues' rotation formula — Wikipedia
- Rotation matrix from axis and angle
- Perlin noise — Wikipedia
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