Le ray tracing arrive en temps réel, avec une facture en images par seconde

Le ray tracing en temps réel commence enfin à sortir des démonstrations théoriques. NVIDIA a présenté les premières GeForce RTX le 20 août et on voit maintenant ce que les jeux sont capables d’en faire.

L’idée est facile à aimer. Depuis longtemps, les moteurs de jeu utilisent toutes sortes d’astuces pour l’éclairage : cartes d’ombre, reflets approximatifs et données préparées à l’avance. Le ray tracing pose plutôt une question géométrique : si un rayon part de ce point dans cette direction, quel triangle touche-t-il en premier?

Rasteriser d’abord, lancer quelques rayons ensuite

La rasterisation commence par les triangles de la scène. Le GPU transforme leurs sommets, projette les triangles sur l’écran et exécute des shaders pour les pixels couverts. Un depth buffer garde la surface la plus proche. Cette approche est très efficace pour savoir ce que la caméra voit, mais moins naturelle pour trouver ce qu’une surface réfléchissante voit ailleurs.

Dans un rendu hybride, le moteur conserve cette première passe, puis choisit certains pixels pour lancer un rayon secondaire. Pour un reflet, la direction dépend de la normale de la surface et de la direction de vue. Le rayon traverse une structure d’accélération, souvent une hiérarchie de boîtes englobantes, afin d’éviter de tester chaque triangle de la scène.

Le trajet simplifié ressemble à ceci :

géométrie → rasterisation → profondeur, normales et matériaux
                                  ↓
pixel réfléchissant → rayon → BVH → triangle touché
                                  ↓
                      couleur + éclairage du point
                                  ↓
                 échantillons bruités → débruitage → image finale

Le BVH regroupe les objets dans de grandes boîtes, puis subdivise les groupes. Si le rayon ne touche pas une boîte, tous les triangles à l’intérieur sont ignorés. La construction de cette structure coûte elle aussi du temps. Une scène statique peut la préparer une fois; des personnages et objets mobiles obligent à la mettre à jour.

Le budget est en millisecondes

À 60 images par seconde, toute la frame doit être terminée en environ :

1 seconde / 60 = 16,67 ms par image

Dans ce budget entrent la logique du jeu, l’animation, la préparation des commandes, la rasterisation, les rayons, le débruitage et la composition finale. Si le ray tracing prend 8 ms de plus sans qu’on enlève autre chose, la cadence peut tomber sous 40 images par seconde. À 30 images, le budget monte à 33,3 ms, mais les contrôles et l’animation répondent moins rapidement.

Lancer un grand nombre de rayons par pixel réduit le bruit, mais le coût grimpe vite. Une image 1920×1080 contient un peu plus de deux millions de pixels. Un seul rayon par pixel représente déjà deux millions de requêtes d’intersection, avant les rayons d’ombre ou les rebonds supplémentaires.

Les premiers jeux font donc des choix :

  • tracer seulement les reflets ou les ombres;
  • lancer moins d’un rayon par pixel et réutiliser l’information entre les frames;
  • limiter la distance et la complexité des objets visibles dans le reflet;
  • rendre l’effet à une résolution plus basse;
  • appliquer un débruiteur guidé par la profondeur, les normales et le mouvement.

Le débruitage reconstruit une image stable à partir de peu d’échantillons. Si le vecteur de mouvement est mauvais ou si un objet apparaît soudainement, l’historique ne correspond plus et produit des traînées, du scintillement ou un reflet qui tarde à arriver. L’optimisation doit donc regarder les artefacts autant que la moyenne d’images par seconde.

Mesurer la bonne étape

Une baisse de performance peut venir du CPU, de la rasterisation, des intersections de rayons, de la mémoire ou du débruiteur. Je capturerais une frame et chronométrerais chaque passe GPU plutôt que de désactiver des options au hasard :

ombres rasterisées       1,8 ms
géométrie principale     4,2 ms
rayons de réflexion      5,6 ms
débruitage               2,1 ms
post-traitement          1,4 ms
autres                   2,7 ms
total                   17,8 ms  → sous 60 FPS

Ici, gagner 1,2 ms suffit théoriquement à repasser sous 16,7 ms. Réduire la distance des reflets ou leur résolution peut être plus utile que simplifier toute la géométrie. Il faut aussi tester les scènes difficiles : beaucoup de surfaces réfléchissantes, objets animés, particules et changements rapides de caméra.

Comme j’aime le jeu et le hardware, cette première génération m’intéresse beaucoup. En novembre 2018, les cartes coûtent cher, peu de jeux sont compatibles et les compromis sont faciles à voir. RTX me paraît être une vraie direction, mais l’ingénierie la plus intéressante est justement ce mélange : garder la rasterisation là où elle est efficace, dépenser quelques rayons là où ils changent l’image, puis mesurer chaque milliseconde avant de demander au joueur de sacrifier la fluidité.