- À mesure que les projets de scènes et de niveaux 3D exécutés dans le navigateur ont pris de l’ampleur, un ensemble d’outils réutilisables s’est constitué pour appliquer des techniques procédurales et génératives à certains éléments des niveaux, plutôt que de générer des mondes entiers
- Le travail sur les textures s’articule principalement autour du Triplanar Mapping et du Hex Tiling, utilisés pour appliquer des textures sans UV ou masquer les motifs répétitifs
- Les deux techniques appliquent
pow()aux poids d’interpolation afin d’augmenter l’importance de l’axe dominant ou du résultat d’échantillonnage, et réduisent la charge de performance en omettant certaines lectures de texture - Les fragment shaders coûteux peuvent être atténués par un Depth Pre-Pass ; dans les scènes avec beaucoup d’overdraw, les performances peuvent s’améliorer de plus de 30 %
- Côté maillages et géométrie, on observe une tendance à étendre les décors, arrière-plans et effets de dégâts via du terrain LoD, des pipelines de traitement de maillages à l’exécution et, à l’avenir, de la Constructive Solid Geometry
Des outils procéduraux accumulés dans les scènes 3D du navigateur
- Après plusieurs années à créer des scènes et niveaux 3D exécutés dans le navigateur, des démos indépendantes centrées sur des shaders personnalisés ont évolué vers une forme proche du jeu, avec des éléments interconnectés
- L’approche commune consiste à appliquer des techniques procédurales et génératives à certaines parties des niveaux, plutôt qu’à produire un monde entièrement généré procéduralement
- Plusieurs outils et effets procéduraux ou semi-procéduraux, réutilisés dans différents niveaux, se sont ainsi naturellement accumulés
Shaders et textures
- La plupart des textures sont des seamless textures qui se répètent sans rupture sur les deux axes ; lorsqu’elles sont étendues sur de grandes surfaces, leurs motifs répétitifs peuvent devenir visibles
- Plusieurs fonctionnalités ont été ajoutées à des shaders personnalisés étendant le
MeshPhysicalMaterialde Three.JS afin d’améliorer la prise en charge du seamless texturing -
Triplanar Mapping
- Le Triplanar Mapping est un outil central de texturing utilisé dans presque tous les niveaux
- Il permet de texturer un maillage avec une seamless texture sans UV map prédéfinie, ce qui est utile dans les cas où un modeleur n’a pas l’occasion de définir l’UV mapping, comme pour un terrain généré procéduralement
- Il fonctionne bien à la fois avec les maillages générés et ceux modélisés à la main
- L’implémentation est légère et simple ; une implémentation de référence se trouve dans triplanarMapping.ts
-
Améliorations du Triplanar Mapping
- Le Triplanar Mapping classique mélange linéairement les lectures de texture sur trois axes en fonction de la normale du fragment
- Dans les zones où la normale n’est pas proche d’un axe unique, la texture peut donner un aspect stratifié
- En appliquant un
pow()à exposant élevé aux poids, puis en les normalisant à nouveau, on augmente la part de l’axe dominant et on réduit les zones de transition - Avec cette transformation, sur la majeure partie du maillage, le poids d’un axe devient proche de 1, tandis que ceux des deux autres axes se rapprochent de 0
- En omettant les lectures de texture dont le poids est inférieur à un seuil, on peut ramener le coût du Triplanar Mapping à un niveau seulement légèrement supérieur à celui d’un texturing basé sur des UV classiques
- Le traitement des normal maps nécessite une prise en compte spécifique dans le code du shader, et utilise l’approche de GPU Gems
- Les détails d’implémentation sont disponibles dans Normal Mapping for a Triplanar Shader
-
Hex Tiling
- Le Hex Tiling est un algorithme qui masque le tiling et les répétitions visibles des seamless textures
- Le simple ajout d’une option de configuration au matériau peut faire passer une scène d’un aspect de mockup peu abouti à un rendu proche du semi-réaliste
- L’implémentation initiale s’appuie sur le Shadertoy de Fabrice Neyret ; elle a été adaptée au material system de Three.JS puis intégrée au shader de matériau principal du projet
- Elle a ensuite été portée, avec autorisation, dans une bibliothèque indépendante, three-hex-tiling, qui permet d’ajouter le Hex Tiling aux materials intégrés de projets Three.JS
- Contrairement au Triplanar Mapping, il nécessite un UV mapping prédéfini
- Utiliser les deux techniques ensemble peut faire monter le nombre maximal de texture fetches par fragment à 27 pour chaque map, ce qui n’est pas pratique
- Comme le Hex Tiling interpole lui aussi linéairement trois résultats d’échantillonnage pour chaque fragment, la technique de pondération par
pow()utilisée pour le Triplanar Mapping permet d’améliorer à la fois les performances et la qualité du résultat
-
Depth Pre-Pass
- Les techniques avancées de texturing peuvent produire des fragment shaders coûteux dans les grandes scènes
- Le Depth Pre-Pass consiste à rendre d’abord toute la scène avec un material très simple et peu coûteux afin d’enregistrer la profondeur de chaque pixel
- Cette approche ajoute le surcoût de rendre la scène deux fois, mais dans les scènes avec beaucoup d’overdraw, le gain dépasse généralement le coût
- En cas de fort overdraw, l’ajout d’un Depth Pre-Pass peut améliorer les performances de plus de 30 %
- En modifiant la configuration du pre-pass, on peut ne rendre que les fragments occultés, afin de visualiser ceux qui seraient ignorés lors de l’utilisation du pre-pass
- Les détails d’implémentation et de configuration dans Three.JS sont disponibles dans un article dédié
-
Synthèse de textures PBR basée sur l’IA
- Les textures générées par IA sont utilisées dans presque toutes les scènes
- Employées avec modération, elles peuvent produire un résultat plutôt convaincant ; toutes les textures de la scène d’exemple sont générées par IA
- Le processus de génération de textures, de création de PBR maps et d’assemblage en seamless textures 4K sans upscaling est traité dans un article séparé
- Le site web mentionné dans cet article pour générer les PBR maps n’est plus disponible
- Actuellement, DeepBump est utilisé pour créer les normal maps, et si nécessaire des outils non IA comme Materialize sont employés pour les autres maps
-
Brouillard et nuages volumétriques
- Le rendu volumétrique est un domaine qui suscite de l’intérêt, car il peut donner un effet distinctif aux scènes
- Un shader relativement générique a été créé pour ajouter des nuages ou du brouillard à n’importe quelle scène Three.JS
- Inspiré par le Shadertoy d’Inigo Quilez, un shader de base pour nuages volumétriques utilisant un LoD noise lookup similaire a été créé, puis étendu vers une forme plus générale et configurable
- Ce shader est utile pour remplir les espaces vides de niveaux clairsemés et ajouter une sensation de dynamisme à des niveaux statiques avec des nuages ou du brouillard en mouvement
- Une partie du code et de l’approche développés par n8programs dans le projet
three-good-godraysest également utilisée three-good-godraysest aussi souvent utilisé et ajoute une atmosphère très distinctive aux niveaux
Maillages et géométrie
- La génération de maillages à l’exécution est un domaine de plus en plus exploré
- L’idée de mondes qui poussent à partir d’une seed logicielle plaît, mais l’objectif est d’éviter le phénomène « infini mais vide » de certains jeux qui mettent en avant la génération procédurale
- L’accent est donc surtout mis sur l’ajout de décors, d’arrière-plans et de flourishes procéduraux aux niveaux, plutôt que sur la génération procédurale de toute l’expérience centrale
-
Terrain LoD
- La génération de terrain est un domaine emblématique du développement de jeux procédural, et son implémentation n’a rien de particulier
- Comme dans la plupart des approches, une heightmap de terrain est créée à l’aide d’une noise function, puis tessellée en triangles pour le rendu
- Le texturing utilise le Triplanar Mapping ou le Hex Tiling
- Le point central est le système LoD : le terrain est créé par tiles, et chaque tile est générée à plusieurs résolutions
- Différentes résolutions sont échangées dynamiquement selon la distance entre la tile et la caméra
- Ce système de génération de terrain est fréquemment réutilisé ; grâce à un système flexible et efficace, il peut être appliqué à plusieurs niveaux avec peu d’effort
-
Pipeline de traitement et de manipulation procédurale de maillages
- La partie la plus travaillée récemment est le pipeline de procedural mesh processing
- L’objectif initial était de subdiviser et déformer procéduralement des low-poly meshes, y compris des maillages générés dynamiquement
- Le but est de rendre plus réalistes ou plus intéressants des maillages simples placés dans les niveaux, comme des plateformes, des rochers ou de grandes structures
- Ce travail a abouti à un pipeline logiciel qui prend des raw geometry data à l’exécution dans le navigateur, les modifie arbitrairement, puis les réexporte dans un format rendu exploitable
- Ce processus exige une attention particulière aux détails, notamment pour le traitement des normales
- Les détails d’implémentation figurent dans l’article subdividing meshes for displacement
Prochaine piste d’expérimentation : Constructive Solid Geometry
- La plupart des outils listés ici ont commencé comme des implémentations ponctuelles pour des cas d’usage précis, mais ont été réutilisés à plusieurs reprises dans d’autres niveaux et contextes
- La prochaine grande idée à tester est la Constructive Solid Geometry
- La Constructive Solid Geometry est un système qui applique des opérateurs booléens dans l’espace 3D
- Il permet de fusionner deux maillages arbitraires
- Il permet de découper des chunks dans un maillage
- Il permet d’effectuer d’autres manipulations similaires
- csg.js implémente un toolkit CSG comprenant des mesh primitives, des opérateurs booléens et une API propre, le tout dans un seul fichier JavaScript commenté d’environ 500 LoC
- Il est prévu de porter un jour cette bibliothèque en Rust afin de mieux comprendre son fonctionnement
- Utiliser la CSG avec le pipeline de traitement de maillages existant pourrait produire des résultats intéressants
- L’auteur souhaite en particulier essayer de détériorer procéduralement des maillages
- Découper des chunks dans des bâtiments ou des ponts pour simuler la dégradation ou l’altération par les intempéries
- Générer des fissures dans des murs ou des routes
1 commentaires
Commentaires sur Hacker News
J’avais déjà un peu touché à la génération procédurale, en particulier pour créer de beaux arbres, et la pièce qui me manquait, c’était une façon simple de raccorder la géométrie
Créer deux cylindres est facile, mais les relier proprement de façon naturelle est très difficile
En théorie, le CSG peut combler ce manque, mais ce n’était pas une manière simple d’aborder le problème. On ne peut pas le voir comme une simple boucle qui ajoute des sommets, parce qu’il faut modéliser chaque élément comme une forme 3D
J’ai aussi essayé d’écrire une routine qui prend deux boucles de sommets et ajoute des faces pour les relier avec des heuristiques, mais choisir quels sommets connecter s’est révélé bien plus difficile que prévu, et on obtenait facilement des raccords peu esthétiques
Un jour, j’aimerais créer un jeu où différents systèmes modulaires de génération procédurale coopèrent tout en gardant une part d’improvisation. Par exemple, les systèmes pourraient « occuper » une partie du monde, déléguer cette partie à d’autres systèmes, ou se raccorder naturellement aux éléments voisins
Il y a eu récemment une vidéo sur la création d’arbres générés [0], et la solution consistait simplement à laisser les cylindres s’intersecter. Ce genre d’approche bricolée peut très bien fonctionner et donner un bon résultat
Comme tu l’as dit, le CSG est aussi possible, mais ça peut être inutilement complexe. Une autre méthode consiste à créer d’abord le squelette de l’arbre, puis à faire un lofting et, si nécessaire, à combiner cela avec du CSG pour produire le tronc et l’écorce
Une bonne bibliothèque ouvre beaucoup de possibilités. Si tu trouves une bibliothèque de géométrie 3D qui gère les opérations booléennes comme l’union de cylindres ou la soustraction de géométrie, tu peux tester énormément d’idées nouvelles. J’en ai essayé plusieurs, et la seule qui m’ait vraiment plu était JSCAD [1]
[0] https://youtu.be/8zMbJmuwEUc?si=KQclrVPeSrIRmsbA
[1] https://github.com/jscad/OpenJSCAD.org
C’est similaire à une manière fonctionnelle de décrire et transformer des formes 3D
Si tu veux voir ce que la demoscene a fait avec ça, je recommande vivement de chercher Mercury Delight sur YouTube. Il y a aussi beaucoup d’exemples sur Shadertoy, avec des choses vraiment incroyables
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Condition...
L’exemple ci-dessous vaut aussi le détour
https://github.com/MaxBondABE/batteries/blob/master/src/geom...
Une bonne génération de terrain n’a rien de trivial, que ce soit à l’échelle du monde ou à hauteur d’homme
L’approche simple, comme on le voit dans l’article, consiste à produire une heightmap bosselée, qui ressemble à peine à un relief réel et n’est pas très intéressante à explorer
Par exemple, Dwarf Fortress part d’un déplacement médian basique, mais applique ensuite beaucoup de post-traitement sur mesure
Rien de ce qui est montré dans cet article n’est vaguement trivial. Rien qu’en rendu par shader GPU, l’auteur se rapproche clairement d’un développeur x100 par rapport à presque tout le reste de la planète
« Approche simple » ? Vraiment simple ?
Sur 8 milliards de personnes, quel pourcentage serait capable d’implémenter ne serait-ce que l’étape « Hello World » nécessaire pour démarrer ce que montre cet article ? Et combien savent seulement ce qu’est un shader ? Il n’y a pas tant de postes OpenGL que ça, et on voit souvent des situations du genre « C’est quoi OpenGL ? Nous on utilise juste Unity »
Et chez les joueurs en ligne ? Au 28 juillet 2024 à 13 h 22 EST, parmi les 1,021,282 personnes connectées à Counter Strike 2 [1], quel pourcentage serait capable d’implémenter ne serait-ce que la première étape des shaders nécessaires au jeu auquel elles jouent ?
Quel pourcentage serait capable de compiler un simple programme C++ en ligne de commande, ou d’écrire un script JavaScript encore plus simple dans un navigateur ? En réalité, c’est presque une question piège, parce que la plupart des gens ont déjà du mal avec l’e-mail
[1] https://steamdb.info/app/730/charts/
Comme pour l’IA, même la meilleure génération procédurale de contenu nécessitera probablement un post-traitement sur mesure dans le résultat final
C’est un article vraiment utile, qui rassemble bien plusieurs techniques procédurales utilisées sur le web
Si vous vous intéressez à un éditeur de niveaux RPG procédural en cours de développement, vous pouvez aussi regarder https://github.com/gamedevgrunt/3D-Action-RPG-JavaScript
Mon expérience avec les depth pre-pass est mitigée
Les quelques fois où j’ai essayé, je n’ai pas constaté d’amélioration de performance notable sur des GPU desktop de milieu ou haut de gamme
Je ne connais pas la raison exacte, mais je pense que l’élimination précoce par Z réduisait déjà les appels au pixel shader. En général, on rend les meshes opaques de l’avant vers l’arrière
Cela dit, mes tests se situaient dans un contexte d’application CAD/CAM et non de jeu. Les scènes étaient assez différentes d’un environnement de jeu classique, avec peu de textures et une géométrie très riche en polygones
Le depth pre-pass est souvent aussi la première étape de l’occlusion culling, mais là encore, tout dépend du contexte. C’est probablement bien plus utile dans un paysage urbain complexe que sur des modèles CAD
Il est surtout utile quand la complexité en profondeur est élevée et que le fragment shader coûte cher. Pour être honnête, c’est le cas de la plupart des jeux
Dans les renderers différés, ce n’est généralement pas indispensable, mais en forward+, cela apporte en général un gain appréciable