Skip to content

Construire un monde ouvert dans le navigateur, partie 8 : intégrer sans perdre notre base de référence

Par Oleg Sidorkin, CTO 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 contient les liens vers toutes les parties.

C’est au moment de l’intégration que les projets deviennent chaotiques. Les différents éléments fonctionnent séparément. Vous les connectez et, soudain, chaque bug semble pouvoir venir de n’importe où.

Les spikes 13 et 14 ont été notre réponse à ce piège. Le spike 13 a établi une base de référence Three.js WebGPU propre. Seulement un moteur de rendu, une scène, une caméra et un maillage simple. Pas de terrain, pas de calcul GPU, pas d’effets. Nous avons vérifié que le backend WebGPU de Three.js s’initialisait correctement, que la boucle de rendu était stable et que les matériaux à nœuds TSL (Three.js Shading Language) fonctionnaient comme prévu. Nous n’avons commencé à ajouter des couches qu’une fois cette étape de validation franchie.

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

Le spike 14 était consacré au renforcement progressif. Nous avons ajouté une fonctionnalité à la fois : d’abord les contrôles de la caméra, puis l’éclairage, ensuite le maillage généré par calcul GPU issu du pipeline marching cubes, et enfin la gestion des buffers permettant d’injecter directement la sortie du GPU dans les attributs géométriques de Three.js. Après chaque ajout, nous vérifiions que la couche précédente continuait de fonctionner correctement.

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

Cela peut sembler lent. Ça l’a été pendant exactement une journée, puis cette méthode nous en a fait gagner plusieurs peu après, lorsque la logique des raccords et le changement de stratégie se sont complexifiés.

La catégorie précise de bugs qui justifiait cette rigueur était celle des artefacts extrêmement fins. De minces fragments qui ressemblaient à une corruption de la géométrie, mais qui provenaient en réalité de données obsolètes. Le shader de calcul écrivait N sommets dans un buffer, mais l’appel de rendu restait configuré pour afficher N+M sommets provenant de l’image précédente. Ces sommets supplémentaires contenaient les données résiduelles de l’ancienne exécution. Le résultat visuel était un scintillement de triangles extrêmement fins qui apparaissaient et disparaissaient de façon imprévisible.

Ce type de bug ne se résout pas à l’intuition. Il se résout par des modifications contrôlées, qui permettent de savoir exactement ce qui a changé entre le dernier état fonctionnel et l’état défaillant actuel.

L’intégration de WebGPU nous a également beaucoup appris sur le cycle de vie des buffers. Dans WebGPU, les buffers GPU deviennent immuables une fois mappés pour un usage donné. Si vous devez redimensionner un buffer de sommets parce que la sortie de marching cubes a grandi, vous devez créer un nouveau buffer et mettre à jour la liaison. Il n’existe pas de realloc. Gérer correctement ce cycle de vie et détruire les anciens buffers sans entrer en conflit avec des tâches GPU encore en cours a nécessité une gestion explicite des barrières de synchronisation, qui n’existe pas dans WebGL.

Dans la partie 9, nous passons au travail sur les raccords Transvoxel. Ce chapitre commence délibérément par une structure de base. À ce stade, nous avions pleinement assimilé la leçon : précipiter l’intégration crée des mystères, tandis qu’une mise en place contrôlée produit des problèmes que l’on peut déboguer.

Technologies mentionnées dans ce chapitre

WebGPU. Le successeur de WebGL, qui fournit dans le navigateur un accès bas niveau au GPU avec des shaders de calcul et du rendu indirect. Les deux fonctionnalités essentielles de WebGPU pour les mondes ouverts sont les suivantes : les shaders de calcul permettent de générer le terrain, de placer la végétation et d’effectuer le culling côté GPU ; le rendu indirect permet au GPU de décider quoi afficher à partir des résultats des calculs, éliminant ainsi les goulots d’étranglement côté CPU dans les scènes denses. Disponible sur ordinateur dans Chrome, Edge et Firefox. Consultez WebGPU comme levier de performances.

Three.js Shading Language (TSL). Le système de shaders à nœuds de Three.js, qui remplace le code GLSL/WGSL brut par des expressions JavaScript composables. Les nœuds TSL tels que texture(), positionWorld, smoothstep() et fog() construisent à l’exécution un graphe de shader compilé pour le backend approprié (GLSL pour WebGL ou WGSL pour WebGPU). TSL permet d’écrire une seule fois la logique d’un matériau et de cibler les deux moteurs de rendu. Le graphe de nœuds étant évalué à chaque image, les uniformes dynamiques et les branchements conditionnels fonctionnent naturellement.

Cycle de vie des buffers GPU dans WebGPU. Les buffers WebGPU sont créés avec des indicateurs d’usage spécifiques (VERTEX, STORAGE, COPY_DST, etc.) et ne peuvent pas être redimensionnés après leur création. Si une exécution de marching cubes produit plus de sommets que le buffer ne peut en contenir, vous devez créer un nouveau buffer, mettre à jour la liaison et détruire l’ancien. Détruire un buffer encore référencé par une commande GPU en cours d’exécution provoque des erreurs. Une gestion explicite des barrières de synchronisation (avec device.queue.onSubmittedWorkDone()) garantit que l’ancien buffer n’est pas détruit avant que le GPU ait fini de l’utiliser. Cette rigueur dans la gestion du cycle de vie n’existe pas dans WebGL, où le pilote gère implicitement la mémoire.

Renforcement progressif. Une méthode rigoureuse d’intégration : établir une base de référence dont le bon fonctionnement est confirmé, ajouter une fonctionnalité à la fois, puis vérifier après chaque ajout que la couche précédente fonctionne toujours. Cette approche fait perdre une journée, mais en fait gagner plusieurs lors des phases ultérieures de débogage, car chaque régression peut être rattachée à une modification précise et contrôlée. Ce modèle consistant à établir une base de référence avant de procéder par incréments est courant dans le développement de mondes ouverts AAA, où les systèmes sont intégrés dans un ordre précis afin de maîtriser les risques.


Partie 8 sur 12.
Précédent : Partie 7 — Marching cubes et les premières véritables grottes
Suivant : Partie 9 — Transvoxel a commencé par une structure de base
Guide de la série : /fr/blog/2026-02-25-open-world-browser-series-guide