Home / Primitives
Primitives
Primitives are the geometric surfaces that rays hit. Each type below corresponds to a
class implementing the IPrimitive interface and to a value of the "type"
key in the JSON scene file. Everything on this page reflects the actual code
(srcs/plugins/primitive/, PrimitiveBuilder, SceneParser).
What is a primitive?
A primitive is a geometric object that a ray can intersect. Every primitive
implements the common IPrimitive interface, which exposes three essential
behaviours:
- Intersection test — given a ray
P(t) = O + tD, the primitive computes the smallest valid parametert(within the range[t_min, t_max]) at which the ray meets its surface, or reports no hit. - Normal — at the hit point
H = O + t·D, the primitive returns the surface normalN, oriented against the ray (flipped whenD·N > 0). The normal drives shading, reflections and refractions. - Bounding box (AABB) — every primitive exposes an axis-aligned box that fully contains it. This box lets the BVH quickly reject rays that miss the object. See AABB & BVH.
Common properties
In the scene file, every primitive shares a base set of common keys; the remaining keys depend on the type (radius, height, vertices…).
| Key | Type | Role |
|---|---|---|
name | string | Object name (identifier shown in the editor). |
type | string | Primitive type (sphere, plane, …). |
position | [x, y, z] | World position; places the center of the primitive. |
rotation | [x, y, z] | Euler rotation in degrees (applied X, then Y, then Z). |
scale | [x, y, z] | Per-axis scale factor (non-uniform allowed). |
material | material ref | Name of a material declared in the materials list. See Materials. |
Not every primitive uses all of these keys: for instance the
plane is oriented by an axis rather than rotation, and the
triangle is defined by its three vertices rather than
position/scale. Each section lists the keys actually read by the parser.
Sphere
type: "sphere" — the set of points at a constant distance r from a center
C. It is the basic analytic primitive.
Intersection — substituting the ray into the implicit equation
‖P − C‖² − r² = 0 yields a quadratic equation
a·t² + b·t + c = 0 with a = D·D, b = 2·(oc·D),
c = oc·oc − r² and oc = O − C. The discriminant Δ = b² − 4ac
decides: Δ < 0 means no hit, otherwise the smallest valid root is kept. The normal
is N = normalize(H − C).
| Key | Type | Description |
|---|---|---|
position | [x, y, z] | Sphere center. |
radius | number | Radius r (> 0). |
scale | [x, y, z] | Scale applied to the sphere. |
material | material ref | Surface material. |
{
"name": "Ball",
"type": "sphere",
"position": [0.0, 1.0, 0.0],
"scale": [1.0, 1.0, 1.0],
"radius": 1.0,
"material": "Red"
}
Plane
type: "plane" — a flat surface, oriented along an axis aligned with the
world frame (x, y or z). The axis key is converted
into a normal vector via AxisUtils::toVector; the plane passes through
position.
Intersection — the plane solves (P − P₀)·N = 0 directly, giving
t = ((P₀ − O)·N) / (D·N). When the denominator D·N is near zero the ray is
parallel to the plane and there is no hit. The normal is constant everywhere, equal to the axis
(flipped to face the ray).
| Key | Type | Description |
|---|---|---|
axis | "x" / "y" / "z" | Orientation axis of the plane (its normal). |
position | [x, y, z] | Point the plane passes through. |
size | number | Extent of the plane. |
material | material ref | Surface material. |
{
"name": "Floor",
"type": "plane",
"axis": "y",
"position": [0.0, 0.0, 0.0],
"size": 100.0,
"material": "Checker"
}
Cube
type: "cube" — a finite convex polyhedron with 6 faces, built as an oriented cube
(position + rotation + scale) from 6 planar faces of edge
length size.
Intersection — the ray is tested against each of the 6 faces (each face is a bounded
plane); the smallest valid t is kept. A candidate hit point is accepted only if it
falls within the face bounds (0 ≤ u ≤ 1, 0 ≤ v ≤ 1 in the face's local
basis). The normal is the axis-aligned normal of the winning face (±X, ±Y,
±Z), corrected by the rotation and the inverse scale.
| Key | Type | Description |
|---|---|---|
position | [x, y, z] | Cube center. |
rotation | [x, y, z] | Euler rotation in degrees. |
scale | [x, y, z] | Non-uniform scale. |
size | number | Base edge length. |
material | material ref | Surface material. |
{
"name": "Box",
"type": "cube",
"position": [0.0, 1.0, 0.0],
"rotation": [0.0, 45.0, 0.0],
"scale": [1.0, 1.0, 1.0],
"size": 2.0,
"material": "Wood"
}
A cube can be converted exactly into a mesh of 8 vertices / 12 triangles for vertex editing (see Vertex editing).
Cylinder
type: "cylinder" — a right circular cylinder defined by an axis, a radius
radius and a finite height height.
Intersection — ray and origin are projected perpendicular to the axis
(Dperp, ocPerp), which reduces the lateral surface to a
quadratic equation a·t² + b·t + c = 0 with
a = Dperp·Dperp, b = 2·(ocPerp·Dperp),
c = ocPerp·ocPerp − r². A root is kept only if the point's projection onto the axis
stays within [0, h]. The normal at point H is
normalize(H − Q), where Q is the projection of H onto the
axis.
| Key | Type | Description |
|---|---|---|
position | [x, y, z] | Cylinder center. |
rotation | [x, y, z] | Orientation (Euler, degrees). |
scale | [x, y, z] | Scale. |
radius | number | Radius r. |
height | number | Finite height h. |
material | material ref | Surface material. |
{
"name": "Column",
"type": "cylinder",
"position": [0.0, 0.0, 0.0],
"rotation": [0.0, 0.0, 0.0],
"scale": [1.0, 1.0, 1.0],
"radius": 0.5,
"height": 3.0,
"material": "Marble"
}
Cone
type: "cone" — a right circular cone defined by an apex, an axis and an opening,
clamped to a finite height height.
Intersection — like the cylinder, the ray is split into components parallel and
perpendicular to the axis, but the implicit equation involves the tangent of the angle
(k): ‖v_perp‖² − k²·v_parallel² = 0. This produces a
quadratic with a = Dperp·Dperp − k²·dv²,
b = 2·(ocPerp·Dperp − k²·ov·dv), c = ocPerp·ocPerp − k²·ov². The roots are
clipped to [0, h] along the axis. The normal derives from the gradient of the implicit
function.
| Key | Type | Description |
|---|---|---|
position | [x, y, z] | Cone center. |
rotation | [x, y, z] | Orientation (Euler, degrees). |
scale | [x, y, z] | Scale. |
radius | number | Base radius. |
height | number | Cone height. |
material | material ref | Surface material. |
{
"name": "Cone",
"type": "cone",
"position": [0.0, 0.0, 0.0],
"rotation": [0.0, 0.0, 0.0],
"scale": [1.0, 1.0, 1.0],
"radius": 1.0,
"height": 2.0,
"material": "Orange"
}
Triangle
type: "triangle" — a finite planar primitive defined by its three vertices
vertex0, vertex1, vertex2. It is the building block of
triangulated geometry and an editable primitive (its 3 corners can be moved in the
viewport).
Intersection — the Möller–Trumbore algorithm. From the edges
E1 = V1 − V0 and E2 = V2 − V0, the barycentric system is solved to obtain
(u, v, t): the hit is rejected if u < 0, v < 0 or
u + v > 1. The flat normal is normalize(E1 × E2) (or interpolated from
vertex normals for smooth shading).
| Key | Type | Description |
|---|---|---|
vertex0 | [x, y, z] | First vertex V0. |
vertex1 | [x, y, z] | Second vertex V1. |
vertex2 | [x, y, z] | Third vertex V2. |
material | material ref | Surface material. |
{
"name": "Facet",
"type": "triangle",
"vertex0": [0.0, 0.0, 0.0],
"vertex1": [1.0, 0.0, 0.0],
"vertex2": [0.0, 1.0, 0.0],
"material": "Green"
}
Torus
type: "torus" — a donut-shaped surface, defined by a major radius radius
(R, distance from the center to the tube axis) and a minor radius height
(r, tube radius).
Intersection — after moving into the torus's local frame, the quartic implicit
surface (‖P‖² + R² − r²)² − 4R²(…) = 0 reduces to a degree-4 equation
c₄t⁴ + c₃t³ + c₂t² + c₁t + c₀ = 0. The quartic is solved and the smallest valid real
root is kept. The normal is the gradient of the implicit function, rotated back into world space.
Mind the convention: here radius is the major radius
R and height is the tube radius r.
| Key | Type | Description |
|---|---|---|
position | [x, y, z] | Torus center. |
rotation | [x, y, z] | Orientation (Euler, degrees). |
radius | number | Major radius R. |
height | number | Tube radius r. |
material | material ref | Surface material. |
{
"name": "Ring",
"type": "torus",
"position": [0.0, 1.0, 0.0],
"rotation": [90.0, 0.0, 0.0],
"radius": 1.0,
"height": 0.3,
"material": "Gold"
}
Tanglecube
type: "tanglecube" — a smooth implicit algebraic surface (isosurface), generated by a
quartic scalar field and a threshold:
f(p) = x⁴ − 5x² + y⁴ − 5y² + z⁴ − 5z² + threshold, the surface being the zero level set
f(p) = 0.
Intersection — rather than solving the quartic analytically, the implementation uses
marching: an entry/exit interval from a bounding sphere, adaptive-step progression
until a sign change g(t_prev)·g(t_cur) < 0 is detected, then root refinement (Newton
with a bisection fallback). The normal is normalize(∇f) with
∇f = (4x³ − 10x, 4y³ − 10y, 4z³ − 10z).
| Key | Type | Description |
|---|---|---|
position | [x, y, z] | Surface center. |
rotation | [x, y, z] | Rotation (stored on the object). |
threshold | number | Iso-value offset for f(p) = 0. |
size | number | Global scale factor. |
material | material ref | Surface material. |
{
"name": "Tanglecube",
"type": "tanglecube",
"position": [0.0, 2.0, 0.0],
"rotation": [0.0, 0.0, 0.0],
"threshold": 11.8,
"size": 1.0,
"material": "Blue"
}
Fractal
type: "fractal" — a 3D fractal object (Mandelbulb-style) rendered by distance
estimation (distance estimator / sphere tracing). The surface is defined implicitly by an
estimator DE(p) = 0 computed iteratively.
Intersection — ray marching: the ray is first bounded by the
primitive's AABB to get [tEnter, tExit], then advanced by steps equal to the estimated
distance d = DE(p)·size. When d < ε, the point is accepted; otherwise
t += d, until the ray exits the box or reaches the step limit. The normal is estimated
numerically by finite differences of DE around the point.
| Key | Type | Description |
|---|---|---|
position | [x, y, z] | Fractal center. |
size | number | World scaling of the fractal domain (default 100). |
power | number | Fractal exponent (default 8). |
iterations | integer | Iteration count in the estimator (default 8). |
material | material ref | Surface material. |
{
"name": "Mandelbulb",
"type": "fractal",
"position": [0.0, 0.0, 0.0],
"size": 100.0,
"power": 8.0,
"iterations": 8,
"material": "Purple"
}
Mesh
type: "mesh" — a triangulated surface loaded from a Wavefront .obj file
(via ObjParser), or defined inline through vertices / faces / normals
arrays. The mesh is a single primitive that internally owns its triangles and a
local BVH, so hundreds of thousands of triangles are intersected in logarithmic
time. It is vertex-editable.
Intersection — each triangle is tested with Möller–Trumbore, but
rays never traverse the triangles linearly: the local BVH prunes them. Normals are interpolated by
barycentric coordinates when the .obj provides vertex normals (vn),
otherwise a flat per-face normal is used as a fallback. The geometry is recentered on its own
center: position therefore places the mesh center.
When inline geometry is provided (vertices non-empty), it takes
precedence over the file path: this is how "baked" primitives
(converted to a mesh) are stored.
| Key | Type | Description |
|---|---|---|
file | string | Path to the .obj (resolved from the working directory). |
vertices | list of [x, y, z] | Inline geometry (takes precedence over file). |
faces | list of [i, j, k] | Triangular faces (vertex indices). |
normals | list of [x, y, z] | Vertex normals (optional). |
position | [x, y, z] | Position of the mesh center. |
rotation | [x, y, z] | Euler rotation in degrees. |
scale | [x, y, z] | Per-axis scale (non-uniform allowed). |
vertex_overrides | list | Vertex overrides (index + position) re-applied after loading the .obj. |
material | material ref | Material applied to every hit. |
{
"name": "Suzanne",
"type": "mesh",
"file": "tests/obj/suzanne.obj",
"position": [0.0, 0.0, 0.0],
"rotation": [0.0, 0.0, 0.0],
"scale": [1.0, 1.0, 1.0],
"material": "Blue"
}
Only triangular faces are accepted; the .obj's texture
coordinates (vt) and material libraries
(mtllib / usemtl) are ignored. Details and editing:
Features → .obj meshes.
AABB & BVH
An AABB (Axis-Aligned Bounding Box) is a box aligned with the coordinate axes,
defined by its minimum Bmin and maximum Bmax corners. It is not a
primitive you place in a scene: it is the bounding volume exposed by every
primitive and used by the BVH.
The ray/AABB test uses the slab method: for each axis the entry and exit parameters
t1 = (Bmin − O)/D, t2 = (Bmax − O)/D are computed, then combined into
tEnter = max(tNear) and tExit = min(tFar). A hit exists when
tEnter ≤ tExit. A ray that misses a node's box skips its whole subtree.
Before rendering, the scene builds a bounding volume hierarchy (BVH) via
scene.buildBvh(); every node carries an AABB. Meshes additionally hold a
local BVH over their triangles. The construction is detailed in
Features → BVH acceleration.
The repository also contains the Mobius_strip.* source files, but this
primitive is not wired into the parser or builder: it is not available in a scene.