Skip to content

Construire un monde ouvert dans le navigateur, partie 5 : budgétiser les éléments visuels

Par Oleg Sidorkin, directeur technique et cofondateur de Cinevva

Vous découvrez la série ? Consultez le guide de la série. Il explique ce qu’est un spike et répertorie toutes les parties.

C’est dans ce chapitre que l’ambition visuelle s’est heurtée à l’arithmétique.

Nous avons réparti le coût du rendu entre plusieurs spikes distincts, car les résultats groupés sont difficiles à diagnostiquer. Si vous activez tout en même temps, vous apprenez seulement que la frame est lente. Vous ne savez pas quelle fonctionnalité a englouti le budget.

Le spike 7 ciblait la densité de la végétation et le coût de son animation. L’approche reposait sur une répartition à l’exécution à partir de cartes de densité de 32x32 par chunk de terrain, alimentant de grands ensembles d’InstancedMesh. Chaque brin d’herbe et groupe d’arbustes recevait une animation de vent dans le vertex shader, pilotée par une texture de bruit défilante. La métrique essentielle que nous surveillions n’était pas le nombre de triangles, mais le surcoût des draw calls et le débit de sommets sur les GPU de milieu de gamme. Nous avons constaté que regrouper les instances dans un nombre réduit de meshes comptait davantage que de diminuer le nombre de polygones de chaque brin.

Ouvrir le spike 7 dans un nouvel onglet ↗ · Voir le code source

Le spike 8 poussait plus loin la complexité des matériaux du terrain. Mélange multicouche pondéré selon l’angle de la pente et l’altitude, projection triplanaire facultative pour les parois rocheuses et normal maps propres à chaque couche. Le shader effectuait un splatting basé sur la pente avec quatre couches de textures, chacune nécessitant un échantillon diffus et un échantillon de normale. Cela représente 4×2=8 lectures de texture par fragment avant même d’ajouter l’éclairage, et ce nombre augmente linéairement avec les couches : une cinquième couche implique donc 10 lectures pour chaque pixel. Nous avons effectué nos mesures spécifiquement sur des GPU Intel intégrés afin de déterminer la configuration minimale. Conclusion : la projection triplanaire sur les surfaces verticales valait son coût, mais pas l’ajout d’une cinquième couche de splatting.

Ouvrir le spike 8 dans un nouvel onglet ↗ · Voir le code source

Le spike 9 portait sur le coût des cascaded shadow maps avec une charge réaliste de terrain et d’objets. Une CSM à trois cascades constituait notre référence. Nous avons effectué les tests avec un soleil bas sur l’horizon, car c’est dans cette situation que la pression sur les cascades est la plus forte. La cascade lointaine couvre une immense tranche du frustum, et le niveau de détail des ombres dépend de la densité de texels, ρR2A (une shadow map de résolution R étirée sur une surface au sol A). Avec une seule map couvrant toute la distance d’affichage, A devient énorme et ρ s’effondre. Les cascades résolvent ce problème en divisant le frustum afin que chaque tranche dispose de sa propre map de R×R sur une petite surface A, ce qui maintient ρ à peu près constant du premier plan jusqu’à l’horizon. Nous avons mesuré la différence de temps GPU entre deux et quatre cascades, puis entre des shadow maps de résolution 1024 et 2048. Résultat : trois cascades en 1024 nous procuraient des ombres de contact acceptables près de la caméra sans dépasser 2 ms de temps GPU sur notre matériel cible.

Ouvrir le spike 9 dans un nouvel onglet ↗ · Voir le code source

La difficulté de cette phase résidait dans la discipline produit. Certains effets étaient superbes, mais devaient malgré tout être limités, car ils consommaient une part trop importante du budget d’une frame par rapport à leur impact visuel.

Notre règle est devenue simple. Une fonctionnalité ne passe à l’étape suivante que si son coût peut être justifié par des mesures du temps de frame.

Cela paraît évident. Pourtant, cette pratique est peu courante pendant les cycles de prototypage rapide, lorsque tout le monde s’enthousiasme pour la prochaine amélioration visuelle. L’application précoce de cette règle a ensuite beaucoup simplifié les décisions d’architecture concernant les clipmaps et les zones volumétriques, car nous connaissions déjà le coût individuel de chaque fonctionnalité en concurrence pour les mêmes 16 ms.

Dans la partie 6, nous abordons le premier grand changement d’architecture du terrain avec les geometry clipmaps.

Technologies abordées dans ce chapitre

InstancedMesh et végétation sur GPU. L’InstancedMesh de Three.js affiche N exemplaires d’une même géométrie avec un seul draw call. Pour la végétation, une carte de densité (32x32 par chunk) pilote à l’exécution la répartition de brins d’herbe et de groupes d’arbustes dans des buffers d’instances. L’animation du vent s’exécute dans le vertex shader à l’aide d’une texture de bruit défilante. À grande échelle, le ComputeInstanceCulling de WebGPU élimine les instances hors écran et lointaines avant la rastérisation, tandis qu’IndirectBatchedMesh rassemble plusieurs types de végétation dans un buffer unique affiché avec du multi-draw indirect. Consultez notre guide de création de paysages sur le culling GPU de la végétation.

Mapping triplanaire. Les textures utilisant un mapping UV classique s’étirent sur les pentes raides, car les coordonnées UV se compriment. Le mapping triplanaire projette les textures le long des trois axes (X, Y, Z) et les mélange en fonction de la normale de la surface. Les parois rocheuses reçoivent la projection X ou Z, sans étirement, tandis que les terrains plats reçoivent la projection Y. Le mélange est fluide et automatique, sans nécessiter de dépliage UV. Pour un terrain PBR, les mêmes pondérations de mélange s’appliquent aux canaux d’albédo, de normale, de rugosité et d’occlusion ambiante. Consultez les détails du mapping triplanaire.

Splatting des matériaux selon la pente et l’altitude. Au lieu d’utiliser des splat maps peintes à la main, les matériaux sont attribués de manière procédurale dans le fragment shader selon les propriétés du terrain. Les terrains plats à basse altitude reçoivent de l’herbe, les pentes raides de la roche, les hautes altitudes de la neige — uniquement sur les surfaces assez plates pour qu’elle puisse s’accumuler — et les zones proches du niveau de la mer du sable. Les transitions utilisent smoothstep afin d’obtenir un mélange progressif. Dans notre implémentation, chaque chunk de terrain évalue quatre couches de textures, avec un échantillon diffus et un échantillon de normale par couche, soit huit lectures de texture par fragment avant l’éclairage. Consultez l’attribution des matériaux selon la pente et l’altitude.

Cascaded Shadow Maps (CSM). La CSM divise le frustum de la caméra en 3 à 4 plages de distance, appelées cascades. Chaque cascade génère une shadow map depuis le point de vue du soleil, à une résolution adaptée à sa distance. Les cascades proches bénéficient d’ombres en haute résolution — notamment des ombres de contact détaillées sous les arbres et les bâtiments — tandis que les cascades lointaines utilisent une résolution inférieure pour les grandes ombres des montagnes. Le shader du terrain échantillonne toutes les cascades et sélectionne celle qui convient à chaque fragment. Coût en performances : 3 à 4 cascades en 1024x1024 ajoutent environ 0,5 à 1 ms pour le rendu des shadow maps, plus environ 0,2 à 0,3 ms pour leur échantillonnage. Consultez la section sur les ombres du terrain.


Partie 5 sur 12.
Précédent : Partie 4 — Le streaming avant les terrains sophistiqués
Suivant : Partie 6 — Les clipmaps ont changé la donne
Guide de la série : /fr/blog/2026-02-25-open-world-browser-series-guide