Construire un ray tracer — 02 — La boucle de rendu

Le squelette du moteur : la double boucle sur les pixels, la recherche de l'intersection la plus proche parmi tous les objets, et l'idée des surfaces implicites contre paramétriques pour le test d'intersection.

02 — La boucle de rendu

Ce que tu vas apprendre

  • Le squelette de la boucle de rendu, autour de la fonction trace
  • Comment garder la bonne intersection : la plus proche
  • Surfaces implicites contre paramétriques
  • Pourquoi une interface Object commune simplifie tout

Prérequis


On sait fabriquer un rayon par pixel. Il faut maintenant le lancer et trouver ce qu'il rencontre. C'est la boucle de rendu : le cœur battant du moteur, qui orchestre tout le reste.

Le squelette

cppfor (int j = 0; j < imageHeight; ++j) {
  for (int i = 0; i < imageWidth; ++i) {
    Ray ray = generateCameraRay(i, j);   // article 01
    pixels[i][j] = trace(ray, objects);  // ↓
  }
}

Toute l'intelligence est dans trace : lancer un rayon, trouver l'objet le plus proche, et renvoyer une couleur. C'est la fonction récursive de la série fondations (reflets, réfraction), mais on se concentre ici sur sa première mission — la visibilité.

Trouver l'intersection la plus proche

Un rayon peut croiser plusieurs objets. Celui qu'on voit est le plus proche de la caméra. La boucle d'intersection garde donc la plus petite distance positive :

cppObject* findClosest(const Ray& ray, const std::vector<Object*>& objects,
                    float& tNear, Vec2f& uv, Object*& hit)
{
  tNear = INFINITY;
  hit = nullptr;
  for (Object* obj : objects) {
    float t; Vec2f barycentric;
    if (obj->intersect(ray, t, barycentric) && t < tNear) {
      tNear = t;       // plus proche que tout ce qu'on a vu
      uv = barycentric;
      hit = obj;
    }
  }
  return hit;
}

Le t est le paramètre du rayon O + t·D : c'est la distance le long du rayon. Le uv est l'information « où sur l'objet » on a tapé (utile pour les textures et l'ombrage lisse, articles suivants). Tout objet expose la même méthode intersect : c'est ce contrat commun qui permet à la boucle d'ignorer le type réel de l'objet.

Implicite contre paramétrique

Comment un objet teste-t-il l'intersection ? Tout dépend de sa façon d'être décrit, et il y a deux grandes familles, comme l'explique le chapitre Parametric and Implicit Surfaces de Scratchapixel.

Surface implicite : définie par une équation f(P) = 0 que les points de la surface vérifient. La sphère en est l'exemple type : ‖P − C‖² − r² = 0. Pour intersecter, on injecte le rayon O + t·D dans l'équation et on résout en t (une équation du second degré pour la sphère, comme vu dans la série fondations). Les plans et les quadriques se traitent pareil.

Surface paramétrique : décrite par une fonction qui engendre les points à partir de paramètres, par exemple S(u, v) pour un patch de Bézier. Pratique pour générer la surface, mais l'intersection avec un rayon y est nettement plus pénible.

C'est l'une des grandes raisons pour lesquelles on triangule tout : un triangle a une intersection simple et exacte, là où une surface paramétrique demanderait une résolution numérique coûteuse. On y vient dès l'article suivant.

Une interface commune

L'astuce d'architecture, c'est l'interface Object partagée :

cppstruct Object {
  virtual bool intersect(const Ray&, float& t, Vec2f& uv) const = 0;
  virtual void getSurfaceProps(/* ... */) const = 0;  // normale, coords texture
  Vec3f color;
};

Sphère, plan, triangle, maillage : chacun implémente intersect à sa façon, mais la boucle de rendu les manipule tous de la même manière. Ajouter un nouveau type de géométrie ne touche pas une ligne de la boucle. C'est exactement le genre de découplage qu'on prêche en architecture logicielle, ici au service du rendu.

La boucle est en place ; il lui manque la primitive reine. L'article suivant explique pourquoi c'est le triangle, et comment on l'intersecte.


Sources

  • Scratchapixel. A Minimal Ray-Tracer: Rendering Simple Shapes — Parametric and Implicit Surfaces. scratchapixel.com
  • Scratchapixel. A Minimal Ray-Tracer: Rendering Spheres. scratchapixel.com
  • Shirley, P. Ray Tracing in One Weekend, sections « Adding a Sphere » et « An Abstraction for Hittable Objects ». raytracing.github.io

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