Bonjour ! J'espère que vous êtes prêt pour cette nouvelle leçon sur l'optimisation.
Dans la leçon précédente, nous nous sommes concentrés sur l'optimisation de la "logique" de nos applications React en prévenant les re-rendus inutiles avec React.memo et useMemo. Nous avons vu comment optimiser le travail du CPU. Aujourd'hui, nous allons nous attaquer à un autre type de goulot d'étranglement, tout aussi critique en 3D : la communication entre le CPU et le GPU.
Imaginez que vous deviez créer une forêt, un champ d'astéroïdes ou une pluie de particules. Votre premier réflexe de développeur serait probablement de faire une boucle pour créer des milliers d'objets. Malheureusement, cette approche mène directement à des performances catastrophiques.
L'objectif de cette leçon est de vous apprendre à optimiser le rendu de nombreux objets identiques avec la technique de l'instanciation (InstancedMesh). C'est l'une des techniques les plus puissantes et les plus importantes pour créer des scènes 3D riches et fluides sur le web.
1. Le Problème : Les "Draw Calls"
Pour que le GPU (la carte graphique) puisse dessiner un objet, le CPU (le processeur) doit lui envoyer une commande. Cette commande, c'est ce qu'on appelle un "draw call". Si votre scène contient 5000 arbres, une approche naïve générera 5000 draw calls à chaque image.
Le problème n'est pas que le GPU est incapable de dessiner ces 5000 arbres. Les GPU modernes sont extrêmement puissants. Le véritable goulot d'étranglement est la communication constante entre le CPU et le GPU. Envoyer des milliers de commandes à chaque rafraîchissement (60 fois par seconde) sature ce canal de communication et fait chuter drastiquement le nombre d'images par seconde (FPS).

Pour mieux comprendre comment les draw calls sont comptabilisés et pourquoi ils sont si coûteux, la vidéo suivante offre une excellente explication.
Three.js Optimization - Best Practices and Techniques
Cette vidéo de Valentin's coding book explique ce que sont les draw calls et comment des éléments comme les ombres ou la transparence peuvent augmenter leur nombre de manière significative.
Regardez la section de 00:03:00 à 00:04:52. Concentrez-vous sur la définition d'un draw call et sur l'exemple qui montre comment leur nombre peut exploser à partir de seulement deux boîtes.
La solution pour contourner ce problème ? Envoyer une seule commande pour dessiner tous les objets identiques. C'est le principe de l'instanciation.
2. La Solution : L'Instanciation (Instancing)
L'instanciation est une technique qui permet de dessiner de multiples copies ("instances") d'un mĂŞme objet en un seul draw call.
L'idée est simple :
- On envoie une seule fois les données communes à toutes les instances : la géométrie (la forme) et le matériau (l'apparence).
- Puis, on envoie une seule fois une grande liste de données spécifiques à chaque instance : principalement sa matrice de transformation (qui définit sa position, sa rotation et son échelle).
Le GPU reçoit alors l'ordre : "Prends cette géométrie et ce matériau, et dessine-les des milliers de fois en utilisant les transformations de cette liste". Cela réduit drastiquement la communication CPU-GPU et libère les performances.
Create 100,000+ Objects With Instancing - Three.js Instanced Rendering Tutorial
La vidéo suivante, de Wael Yasmina, offre la définition la plus claire et concise de l'instanciation et de son utilité.
Regardez l'introduction de 00:00:02 à 00:02:47. L'auteur explique parfaitement pourquoi l'instanciation est la solution au problème des draw calls.
Maintenant que nous avons compris le "pourquoi" et le "quoi", passons au "comment".
3. Implémenter l'Instanciation avec Three.js
Avant de voir les abstractions de React Three Fiber, il est essentiel de comprendre comment l'instanciation fonctionne en "pur" Three.js. Cela vous donnera des bases solides. La classe clé est THREE.InstancedMesh.
Le processus est le suivant :
- On crée un
InstancedMeshen lui passant une géométrie, un matériau et le nombre total d'instances que l'on souhaite afficher. - On parcourt une boucle, de 0 jusqu'au nombre d'instances.
- À chaque itération, on définit la position, la rotation et l'échelle de l'instance courante. Ces informations sont stockées dans un objet
THREE.Matrix4. - On assigne cette matrice à l'instance correspondante avec la méthode
mesh.setMatrixAt(index, matrix).
La vidéo que nous avons commencée est un tutoriel parfait pour cela.
Create 100,000+ Objects With Instancing - Three.js Instanced Rendering Tutorial
Continuons avec la vidéo de Wael Yasmina pour voir une démonstration pratique de l'implémentation de InstancedMesh.
Regardez attentivement les sections suivantes : De 00:02:47 à 00:03:30 : Création de l'InstancedMesh de base. De 00:03:30 à 00:06:12 : La partie la plus importante. Observez comment une boucle est utilisée avec un Object3D temporaire et setMatrixAt pour positionner chaque instance. De 00:06:12 à 00:06:42 : Bonus : voyez comment setColorAt permet même de donner une couleur unique à chaque instance.
Ce processus, bien que très puissant, est assez "impératif". Il ne correspond pas tout à fait à la philosophie déclarative de React. Voyons comment le transposer dans React Three Fiber.
4. L'Instanciation dans l'Écosystème React Three Fiber
Avec R3F, il existe deux manières principales de faire de l'instanciation, allant du plus bas niveau au plus haut niveau d'abstraction.
A. L'Approche Bas Niveau : <instancedMesh>
React Three Fiber expose directement la classe InstancedMesh de Three.js sous la forme d'un composant JSX : <instancedMesh>.
Cette approche est un mélange du monde impératif et déclaratif.
- On déclare l'
<instancedMesh>dans notre JSX. - On utilise un
useRefpour obtenir une référence à l'objet Three.js sous-jacent. - On utilise un
useEffect(qui ne s'exécute qu'une seule fois au montage) pour faire notre boucle et appelersetMatrixAt, exactement comme dans l'exemple "vanilla" Three.js.
Scaling performance - React Three Fiber
La documentation de React Three Fiber contient un exemple de code concis pour cette approche. C'est un excellent pont entre ce que vous venez de voir en Three.js pur et le monde de React.
Lisez la section "Instancing" et étudiez l'exemple de code du composant Instances. Repérez les éléments familiers : <instancedMesh>, useRef, et useEffect qui contient la boucle et l'appel à setMatrixAt.
Cette méthode vous donne un contrôle total, mais elle reste assez verbeuse. Heureusement, la librairie drei nous offre une solution bien plus élégante.
B. L'Approche Haut Niveau : <Instances> et <Instance> de drei
La librairie @react-three/drei contient des composants qui encapsulent cette logique pour nous. Pour l'instanciation, elle fournit les composants <Instances> et <Instance>.
<Instances>: Le composant conteneur. Il prend en charge la création de l'InstancedMeshen coulisses. On lui passe la géométrie et le matériau partagés.<Instance>: Un composant virtuel que l'on place à l'intérieur de<Instances>. On peut en créer autant qu'on veut, et chaque<Instance>peut recevoir ses propres props de transformation (position,rotation,scale,color).
Cette approche est entièrement déclarative et bien plus "React-friendly". Vous n'avez plus besoin de useRef, de useEffect ou de manipuler des matrices manuellement.
threejs-scene-builder | LobeHub
Cet extrait de code montre à quel point l'approche avec drei est simple et intuitive, surtout pour un développeur React.
Trouvez la section "Drei Library - Essential Helpers" et analysez l'exemple de code "Performance-optimized instances" pour le composant Trees. Remarquez la simplicité : on mappe sur un tableau et on retourne un composant <Instance> pour chaque arbre, en passant simplement les transformations en tant que props.
Cette méthode est celle que vous utiliserez dans 90% des cas. Elle est plus lisible, moins sujette aux erreurs et s'intègre parfaitement à la gestion d'état de React.
5. Quand Utiliser l'Instanciation ? (Et quand ne pas le faire ?)
L'instanciation est une solution miracle pour les objets identiques (même géométrie, même matériau). Cependant, il y a des compromis à connaître.
- Frustum Culling : Normalement, Three.js ne dessine pas les objets qui sont en dehors du champ de vision de la caméra (c'est le frustum culling). Un
InstancedMeshest traité comme un seul grand objet. Par conséquent, si une seule instance est visible, le GPU devra quand même traiter le groupe entier. - Flexibilité : Vous ne pouvez pas appliquer un matériau radicalement différent à une seule instance (même si vous pouvez changer sa couleur avec
setColorAt).
Dans le monde réel, le choix d'optimisation est souvent un équilibre.
Three.js Optimization - Best Practices and Techniques
Pour finir, cette section de la vidéo sur l'optimisation présente une étude de cas réelle (le projet 'Colts Club') qui a dû peser le pour et le contre de l'instanciation par rapport à d'autres techniques comme le regroupement de maillages (merging).
Regardez la section de 00:13:13 à 00:14:59. Cela vous donnera un aperçu des décisions complexes d'optimisation dans un grand projet et montrera que l'instanciation, bien que puissante, fait partie d'une boîte à outils plus large.
Conclusion
Félicitations ! Vous venez d'acquérir une des compétences les plus importantes pour la performance en 3D sur le web.
Points clés à retenir :
- Le rendu de nombreux objets via une boucle crée un grand nombre de draw calls, ce qui sature la communication CPU-GPU et détruit les performances.
- L'instanciation résout ce problème en dessinant des milliers d'objets identiques (même géométrie, même matériau) en un seul draw call.
- En "pur" Three.js, on utilise
InstancedMeshet on configure chaque instance de manière impérative avecsetMatrixAt. - En R3F, l'approche la plus idiomatique et la plus simple est d'utiliser les composants
<Instances>et<Instance>de la librairie@react-three/drei. - L'instanciation est idéale pour des éléments répétitifs comme des arbres, des particules, de l'herbe, des projectiles, etc.
Aperçu de la prochaine leçon :
Maintenant que nous savons créer des scènes complexes et performantes, un autre aspect de l'expérience utilisateur devient primordial : le temps de chargement. Dans la prochaine leçon, nous apprendrons à gérer le chargement de nos modèles 3D, textures et autres ressources de manière élégante avec Suspense et le hook useProgress, pour que l'utilisateur ne se retrouve jamais face à un écran vide.