Accueil / Primitives
Primitives
Les primitives sont les surfaces géométriques que les rayons viennent frapper. Chaque
type ci-dessous correspond à une classe qui implémente l'interface IPrimitive et à une
valeur de la clé "type" dans le fichier de scène JSON. Le contenu de cette page reflète
le code réel (srcs/plugins/primitive/, PrimitiveBuilder, SceneParser).
Qu'est-ce qu'une primitive ?
Une primitive est un objet géométrique intersectable par un rayon. Toutes les
primitives implémentent l'interface commune IPrimitive, qui expose trois
comportements essentiels :
- Test d'intersection — étant donné un rayon
P(t) = O + tD, la primitive calcule le plus petit paramètretvalide (dans l'intervalle[t_min, t_max]) pour lequel le rayon rencontre sa surface, ou signale l'absence de contact. - Normale — au point d'impact
H = O + t·D, la primitive fournit la normale de surfaceN, orientée face au rayon (retournée siD·N > 0). La normale sert à l'ombrage, aux réflexions et aux réfractions. - Boîte englobante (AABB) — chaque primitive expose une boîte alignée sur les axes qui la contient entièrement. Cette boîte permet au BVH de rejeter très vite les rayons qui manquent l'objet. Voir AABB & BVH.
Propriétés communes
Dans le fichier de scène, chaque primitive partage un socle de clés communes ; les autres clés dépendent du type (rayon, hauteur, sommets…).
| Clé | Type | Rôle |
|---|---|---|
name | chaîne | Nom de l'objet (identifiant affiché dans l'éditeur). |
type | chaîne | Type de primitive (sphere, plane, …). |
position | [x, y, z] | Position dans le monde ; place le centre de la primitive. |
rotation | [x, y, z] | Rotation d'Euler en degrés (appliquée X, puis Y, puis Z). |
scale | [x, y, z] | Facteur d'échelle par axe (non uniforme autorisé). |
material | ref matériau | Nom d'un matériau déclaré dans la liste materials. Voir Matériaux. |
Toutes les primitives n'utilisent pas toutes ces clés : par exemple le
plan est orienté par un axe plutôt que par rotation, et le
triangle est défini par ses trois sommets plutôt que par
position/scale. Chaque section précise les clés réellement lues par le
parseur.
Sphère
type: "sphere" — l'ensemble des points situés à distance constante r d'un
centre C. C'est la primitive analytique de base.
Intersection — en substituant le rayon dans l'équation implicite
‖P − C‖² − r² = 0, on obtient une équation quadratique
a·t² + b·t + c = 0 avec a = D·D, b = 2·(oc·D),
c = oc·oc − r² et oc = O − C. Le discriminant Δ = b² − 4ac
décide : Δ < 0 aucun contact, sinon on garde la plus petite racine valide.
La normale est N = normalize(H − C).
| Clé | Type | Description |
|---|---|---|
position | [x, y, z] | Centre de la sphère. |
radius | nombre | Rayon r (> 0). |
scale | [x, y, z] | Échelle appliquée à la sphère. |
material | ref matériau | Matériau de surface. |
{
"name": "Boule",
"type": "sphere",
"position": [0.0, 1.0, 0.0],
"scale": [1.0, 1.0, 1.0],
"radius": 1.0,
"material": "Rouge"
}
Plan
type: "plane" — une surface plane, orientée selon un axe aligné sur le
repère (x, y ou z). La clé axis est convertie
en vecteur normal via AxisUtils::toVector ; le plan passe par position.
Intersection — le plan résout directement (P − P₀)·N = 0, ce qui donne
t = ((P₀ − O)·N) / (D·N). Si le dénominateur D·N est proche de zéro, le
rayon est parallèle au plan et il n'y a pas de contact. La normale est constante en tout point,
égale à l'axe (retournée face au rayon).
| Clé | Type | Description |
|---|---|---|
axis | "x" / "y" / "z" | Axe d'orientation du plan (sa normale). |
position | [x, y, z] | Point par lequel passe le plan. |
size | nombre | Étendue du plan. |
material | ref matériau | Matériau de surface. |
{
"name": "Sol",
"type": "plane",
"axis": "y",
"position": [0.0, 0.0, 0.0],
"size": 100.0,
"material": "Damier"
}
Cube
type: "cube" — un polyèdre convexe fini à 6 faces, construit comme un cube orienté
(position + rotation + scale) à partir de 6 faces planes de
côté size.
Intersection — le rayon est testé contre chacune des 6 faces (chaque face est
un plan borné) ; on retient le plus petit t valide. Un point d'impact candidat est
accepté seulement s'il tombe dans les bornes de la face (0 ≤ u ≤ 1,
0 ≤ v ≤ 1 dans la base locale de la face). La normale est la normale axiale de la face
gagnante (±X, ±Y, ±Z), corrigée par la rotation et l'échelle
inverse.
| Clé | Type | Description |
|---|---|---|
position | [x, y, z] | Centre du cube. |
rotation | [x, y, z] | Rotation d'Euler en degrés. |
scale | [x, y, z] | Échelle non uniforme. |
size | nombre | Longueur d'arête de base. |
material | ref matériau | Matériau de surface. |
{
"name": "Boite",
"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": "Bois"
}
Un cube peut être converti exactement en maillage de 8 sommets / 12 triangles pour l'édition de sommets (voir Édition de sommets).
Cylindre
type: "cylinder" — un cylindre circulaire droit défini par un axe, un rayon
radius et une hauteur finie height.
Intersection — on projette rayon et origine perpendiculairement à l'axe
(Dperp, ocPerp), ce qui ramène la surface latérale à une
équation quadratique a·t² + b·t + c = 0 avec
a = Dperp·Dperp, b = 2·(ocPerp·Dperp),
c = ocPerp·ocPerp − r². Une racine n'est retenue que si la projection du point sur
l'axe reste dans [0, h]. La normale au point H est
normalize(H − Q), où Q est la projection de H sur l'axe.
| Clé | Type | Description |
|---|---|---|
position | [x, y, z] | Centre du cylindre. |
rotation | [x, y, z] | Orientation (Euler, degrés). |
scale | [x, y, z] | Échelle. |
radius | nombre | Rayon r. |
height | nombre | Hauteur finie h. |
material | ref matériau | Matériau de surface. |
{
"name": "Colonne",
"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": "Marbre"
}
Cône
type: "cone" — un cône circulaire droit défini par un apex, un axe et une ouverture,
limité à une hauteur finie height.
Intersection — comme le cylindre, on décompose le rayon en composantes parallèle
et perpendiculaire à l'axe, mais l'équation implicite fait intervenir la tangente de l'angle
(k) : ‖v_perp‖² − k²·v_parallel² = 0. Cela produit une
quadratique avec a = Dperp·Dperp − k²·dv²,
b = 2·(ocPerp·Dperp − k²·ov·dv), c = ocPerp·ocPerp − k²·ov². Les racines
sont clippées à [0, h] le long de l'axe. La normale dérive du gradient de la fonction
implicite.
| Clé | Type | Description |
|---|---|---|
position | [x, y, z] | Centre du cône. |
rotation | [x, y, z] | Orientation (Euler, degrés). |
scale | [x, y, z] | Échelle. |
radius | nombre | Rayon de base. |
height | nombre | Hauteur du cône. |
material | ref matériau | Matériau de surface. |
{
"name": "Cornet",
"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" — une primitive planaire finie définie par ses trois sommets
vertex0, vertex1, vertex2. C'est la brique de base de la
géométrie triangulée et une primitive éditable (ses 3 coins peuvent être déplacés
dans le viewport).
Intersection — algorithme de Möller–Trumbore. À partir des arêtes
E1 = V1 − V0 et E2 = V2 − V0, on résout le système barycentrique et on
obtient (u, v, t) : on rejette si u < 0, v < 0 ou
u + v > 1. La normale plate est normalize(E1 × E2) (ou interpolée par
les normales de sommets pour un ombrage lisse).
| Clé | Type | Description |
|---|---|---|
vertex0 | [x, y, z] | Premier sommet V0. |
vertex1 | [x, y, z] | Deuxième sommet V1. |
vertex2 | [x, y, z] | Troisième sommet V2. |
material | ref matériau | Matériau de surface. |
{
"name": "Facette",
"type": "triangle",
"vertex0": [0.0, 0.0, 0.0],
"vertex1": [1.0, 0.0, 0.0],
"vertex2": [0.0, 1.0, 0.0],
"material": "Vert"
}
Tore
type: "torus" — une surface en forme d'anneau (donut), définie par un grand rayon
radius (R, distance du centre à l'axe du tube) et un petit rayon
height (r, rayon du tube).
Intersection — après passage dans le repère local du tore, la surface implicite
quartique (‖P‖² + R² − r²)² − 4R²(…) = 0 se ramène à une équation de degré
4 c₄t⁴ + c₃t³ + c₂t² + c₁t + c₀ = 0. On résout le quartique et on garde la
plus petite racine réelle valide. La normale est le gradient de la fonction implicite, ramené dans
le repère du monde.
Attention à la convention : ici radius est le grand
rayon R et height le rayon du tube r.
| Clé | Type | Description |
|---|---|---|
position | [x, y, z] | Centre du tore. |
rotation | [x, y, z] | Orientation (Euler, degrés). |
radius | nombre | Grand rayon R. |
height | nombre | Rayon du tube r. |
material | ref matériau | Matériau de surface. |
{
"name": "Anneau",
"type": "torus",
"position": [0.0, 1.0, 0.0],
"rotation": [90.0, 0.0, 0.0],
"radius": 1.0,
"height": 0.3,
"material": "Or"
}
Tanglecube
type: "tanglecube" — une surface algébrique implicite (isosurface) lisse, engendrée par
un champ scalaire quartique et un seuil threshold :
f(p) = x⁴ − 5x² + y⁴ − 5y² + z⁴ − 5z² + threshold, la surface étant l'ensemble
f(p) = 0.
Intersection — plutôt qu'une résolution analytique du quartique, l'implémentation
procède par marching : intervalle d'entrée/sortie via une sphère englobante,
progression à pas adaptatif jusqu'à détecter un changement de signe
g(t_prev)·g(t_cur) < 0, puis raffinement de la racine (Newton avec repli par
bissection). La normale est normalize(∇f) avec
∇f = (4x³ − 10x, 4y³ − 10y, 4z³ − 10z).
| Clé | Type | Description |
|---|---|---|
position | [x, y, z] | Centre de la surface. |
rotation | [x, y, z] | Rotation (stockée sur l'objet). |
threshold | nombre | Décalage d'iso-valeur pour f(p) = 0. |
size | nombre | Facteur d'échelle global. |
material | ref matériau | Matériau de surface. |
{
"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": "Bleu"
}
Fractale
type: "fractal" — un objet fractal 3D (type Mandelbulb) rendu par
estimation de distance (distance estimator / sphere tracing). La surface est
définie implicitement par un estimateur DE(p) = 0 calculé de façon itérative.
Intersection — ray marching : on borne d'abord le rayon par
l'AABB de la primitive pour obtenir [tEnter, tExit], puis on avance par pas égaux à la
distance estimée d = DE(p)·size. Quand d < ε, le point est accepté ;
sinon t += d, jusqu'à sortir de la boîte ou atteindre la limite d'itérations. La
normale est estimée numériquement par différences finies sur DE autour du point.
| Clé | Type | Description |
|---|---|---|
position | [x, y, z] | Centre de la fractale. |
size | nombre | Mise à l'échelle du domaine fractal (défaut 100). |
power | nombre | Exposant fractal (défaut 8). |
iterations | entier | Nombre d'itérations dans l'estimateur (défaut 8). |
material | ref matériau | Matériau de surface. |
{
"name": "Mandelbulb",
"type": "fractal",
"position": [0.0, 0.0, 0.0],
"size": 100.0,
"power": 8.0,
"iterations": 8,
"material": "Violet"
}
Maillage
type: "mesh" — une surface triangulée chargée depuis un fichier Wavefront
.obj (via ObjParser), ou définie en ligne par des tableaux
vertices / faces / normals. Le maillage est
une seule primitive qui possède en interne ses triangles et un BVH
local, ce qui permet d'intersecter des centaines de milliers de triangles en temps
logarithmique. Il est éditable au sommet.
Intersection — chaque triangle est testé par Möller–Trumbore, mais
les rayons ne parcourent jamais les triangles linéairement : le BVH local les élague. Les
normales sont interpolées par coordonnées barycentriques quand le .obj fournit des
normales de sommet (vn), sinon une normale de face plate sert de repli. La géométrie
est recentrée sur son propre centre : position place donc le centre du maillage.
Quand une géométrie en ligne est fournie (vertices non vide), elle a
la priorité sur le chemin file : c'est ainsi que sont stockées les
primitives « cuites » (converties en maillage).
| Clé | Type | Description |
|---|---|---|
file | chaîne | Chemin du .obj (résolu depuis le dossier de travail). |
vertices | liste [x, y, z] | Géométrie en ligne (prioritaire sur file). |
faces | liste [i, j, k] | Faces triangulaires (indices de sommets). |
normals | liste [x, y, z] | Normales de sommet (optionnelles). |
position | [x, y, z] | Position du centre du maillage. |
rotation | [x, y, z] | Rotation d'Euler en degrés. |
scale | [x, y, z] | Échelle par axe (non uniforme autorisée). |
vertex_overrides | liste | Surcharges de sommets (index + position) réappliquées après chargement du .obj. |
material | ref matériau | Matériau appliqué à tous les impacts. |
{
"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": "Bleu"
}
Seules les faces triangulaires sont acceptées ; les
coordonnées de texture (vt) et les bibliothèques de matériaux
(mtllib / usemtl) du .obj sont ignorées. Détails et
édition : Fonctionnalités → Maillages .obj.
AABB & BVH
Une AABB (Axis-Aligned Bounding Box) est une boîte alignée sur les axes, définie
par ses coins minimum Bmin et maximum Bmax. Ce n'est pas une primitive
que l'on place dans une scène : c'est le volume englobant exposé par chaque
primitive et exploité par le BVH.
Le test rayon/AABB se fait par la méthode des slabs : pour chaque axe on calcule
les paramètres d'entrée et de sortie t1 = (Bmin − O)/D, t2 = (Bmax − O)/D,
puis on combine tEnter = max(tNear) et tExit = min(tFar). Il y a
intersection lorsque tEnter ≤ tExit. Un rayon qui manque la boîte d'un nœud ignore
tout son sous-arbre.
Avant le rendu, la scène construit une hiérarchie de volumes englobants (BVH) via
scene.buildBvh() ; chaque nœud porte une AABB. Les maillages disposent en plus d'un
BVH local sur leurs triangles. Le détail de la construction est décrit dans
Fonctionnalités → Accélération BVH.
Le dépôt contient aussi les fichiers source Mobius_strip.*, mais
cette primitive n'est pas branchée au parseur ni au builder : elle n'est pas
disponible en scène.