Maths pour le rendu 3D — 10 — Placer une caméra : la fonction LookAt

Transformer un simple « la caméra est ici et regarde là » en une matrice 4×4. Construire les trois axes (forward, right, up) avec des produits vectoriels, remplir la matrice, et gérer le cas-limite du gimbal lock.

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


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 , 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 forward et up proche 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.

Réservez un audit gratuit de 30 minutes. Je vous montre concrètement ce qu'on peut automatiser.