10 — Placer une caméra : la fonction LookAt
Ce que tu vas apprendre
- Ce qu'est la matrice caméra-vers-monde
- Construire les trois axes de la caméra à partir de « d'où » et « vers où »
- Remplir la matrice 4×4 avec ces axes
- Le cas-limite qui casse tout : le gimbal lock
Prérequis
- Le produit vectoriel, les matrices de transformation et les repères locaux
Personne n'a envie de remplir à la main les seize cases d'une matrice de caméra. La fonction LookAt offre une interface humaine : tu dis « la caméra est ici, elle regarde là, et le haut est à peu près par là », et elle te construit la matrice. C'est l'application pratique qui réunit presque tout ce qu'on a vu dans la série — produits vectoriels, normalisation, repère local, matrices.
Ce qu'on cherche : la matrice caméra-vers-monde
L'objectif est de bâtir la matrice caméra-vers-monde : celle qui place et oriente le repère local de la caméra dans le monde. Pour ça, il faut les trois axes de la caméra exprimés dans le monde — son « droite » (x), son « haut » (y), son « avant » (z) — plus sa position. Trois vecteurs orthonormés et un point : exactement le contenu d'une matrice de transformation (article 04).
Les entrées de la fonction sont :
from: la position de la caméra ;to: le point visé ;arbitraryUp: un « haut » approximatif (souvent(0, 1, 0)), simple indication.
Construire les trois axes
Tout repose sur trois produits vectoriels et quelques normalisations.
Étape 1 — l'axe avant (forward). Il pointe de la caméra vers la cible. Attention à l'ordre (from − to, dans la convention droitière de Scratchapixel où le z caméra pointe vers l'arrière) :
forward = normalize(from − to)
Étape 2 — l'axe droit (right). Le « haut » fourni n'est probablement pas perpendiculaire à forward. On obtient une vraie perpendiculaire par produit vectoriel :
right = normalize(cross(arbitraryUp, forward))
L'ordre des arguments (up × forward) est dicté par l'anticommutativité du produit vectoriel et la main du repère — l'inverser retournerait la caméra.
Étape 3 — le vrai axe haut (up). Maintenant qu'on a deux axes perpendiculaires, le troisième tombe :
up = cross(forward, right)
Comme forward et right sont unitaires et perpendiculaires, ce up est déjà normalisé. On a redressé le « haut approximatif » en un vrai « haut » orthogonal aux deux autres.
up (vrai)
|
|
from ●———→ right
\
↘ forward (vers la scène)
Remplir la matrice
Les trois axes occupent les trois premières lignes, la position occupe la quatrième (convention vecteur-ligne de Scratchapixel) :
| right.x right.y right.z 0 |
| up.x up.y up.z 0 |
| forward.x forward.y forward.z 0 |
| from.x from.y from.z 1 |
cppvoid lookat(const Vec3f& from, const Vec3f& to,
const Vec3f& arbitraryUp, Matrix44& m)
{
Vec3f forward = normalize(from - to);
Vec3f right = normalize(cross(arbitraryUp, forward));
Vec3f up = cross(forward, right);
m[0][0]=right.x; m[0][1]=right.y; m[0][2]=right.z;
m[1][0]=up.x; m[1][1]=up.y; m[1][2]=up.z;
m[2][0]=forward.x; m[2][1]=forward.y; m[2][2]=forward.z;
m[3][0]=from.x; m[3][1]=from.y; m[3][2]=from.z;
}
De caméra-vers-monde à monde-vers-caméra
Les API de rendu veulent en général l'inverse : la matrice monde-vers-caméra (la view matrix), qui exprime la scène vue par la caméra. Bonne nouvelle vue à l'article 08 : la matrice caméra-vers-monde est orthogonale (rotations + translation, pas d'échelle), donc son inverse est simplement sa transposée — gratuit. On ajuste au passage le signe du z selon la convention de l'API (caméra qui regarde vers les z négatifs).
Le piège : le gimbal lock
La méthode a un point de rupture : quand forward devient parallèle à arbitraryUp — caméra qui regarde droit en haut ou droit en bas. Le produit vectoriel de deux vecteurs alignés est nul : impossible de fabriquer right, et le repère s'effondre. C'est une forme de gimbal lock.
Les parades :
- détecter ce cas (produit scalaire de
forwardetupproche de ±1) et choisir alors un « haut » de secours différent ; - pour des caméras qui doivent passer par ces orientations en douceur (jeux, survols), utiliser des quaternions plutôt qu'une matrice LookAt — un sujet pour plus tard dans le parcours.
En pratique, pour placer une caméra fixe sur une scène, le cas ne se présente quasiment jamais ; il faut juste savoir qu'il existe.
L'article suivant clôt la série par un glossaire de référence de tout le vocabulaire mathématique du rendu.
Sources
- Scratchapixel. Placing a Camera: the LookAt Function. scratchapixel.com
- Marschner, S., & Shirley, P. (2021). Fundamentals of Computer Graphics (5ᵉ éd.), chap. « Viewing » (matrices de caméra). CRC Press.
- Lengyel, E. (2011). Mathematics for 3D Game Programming and Computer Graphics (3ᵉ éd.). Cengage.