J'ai déjà implémenté des cubes de marche / tétraèdres pour rendre une IsoSurface. Cela a fonctionné ( YouTube ), mais les performances ont été désastreuses car je n'ai jamais réussi à implémenter un niveau de détail variable en fonction de la distance de vue (ou même à supprimer d'anciens morceaux distants).
J'ai décidé de recommencer et de le faire correctement cette fois. J'ai commencé par créer un OctreeNode qui fonctionne comme suit quand Build()
est appelé.
- Si le bloc est trop petit pour être construit, revenez immédiatement.
- Déterminez si la surface traverse le volume de ce morceau.
- Si oui, alors décidez si nous voulons augmenter la LOD (car la caméra est proche)
- Si oui, générez 8 enfants et appelez le même processus sur eux
- Sinon, créez le maillage en utilisant les dimensions du nœud actuel
Quelques PseudoCode:
OctNode Build() {
if(this.ChunkSize < minChunkSize) {
return null;
}
densityRange = densitySource¹.GetDensityRange(this.bounds);
if(densityRange.min < surface < densityRange.max) {
if(loDProvider.DesiredLod(bounds)² > currentLoD) {
for(i 1 to 8) {
if(children[i] == null) {
children[i] = new OctNode(...)
}
children[i] = children[i].Build();
}
} else {
BuildMesh();
}
return this;
}
}
¹ En plus de renvoyer la densité en un point, la source de densité peut déterminer la plage de densité possible pour un volume donné.
² Le fournisseur LoD prend une boîte englobante et renvoie la LoD maximale souhaitée en fonction de la position / tronc de la caméra, des paramètres utilisateur, etc.
Alors ... Tout cela fonctionne assez bien. En utilisant une sphère simple comme source de densité et en montrant tous les nœuds:
Et juste les feuilles:
Cependant, il y a quelques problèmes:
- Je dois définir le volume englobant initial (et plus il est grand, plus je dois faire de traitement)
- À la racine de l'arbre, je n'ai aucune idée de la profondeur des feuilles, donc ma numérotation LoD commence à la qualité la plus basse (racine) et augmente à mesure que les morceaux deviennent plus petits. Parce que LoD est maintenant relatif au volume initial, ce n'est pas très utile lorsque je veux faire des choses à des tailles / qualités spécifiques.
J'ai pensé à quelques options, mais les deux semblent défectueuses:
- Maintenir une collection d'octrees et ajouter / supprimer en fonction de la distance. Je ne vois pas comment je maillerais bien¹, et j'aurais besoin d'une liste de nœuds vides connus, surtout si je veux des surfaces 3D arbitraires (pour éviter de recalculer les volumes vides à plusieurs reprises)
- Ajoutez un nœud parent à la racine actuelle, puis ajoutez sept frères et sœurs pour le nœud d'origine. Cela fonctionnerait et serait à la demande, mais il semble complexe de reculer sensiblement lorsque le joueur se déplace dans le paysage. Cela rendrait également les numéros LoD encore moins significatifs.
¹ [Pour clarifier Q ci-dessous] À l'heure actuelle, si 2 nœuds physiquement adjacents dans l'arbre sont à des niveaux de détail différents, j'ai du code pour forcer les verts de sorte qu'il n'y ait pas de couture lorsque les mailles sont générées. Je peux le faire en connaissant la densité de plusieurs nœuds environnants. Dans un scénario où j'ai deux octets indépendants côte à côte, je n'aurais aucun moyen facile de récupérer ces informations, ce qui entraînerait des coutures.
Quelle est la meilleure façon d'aborder cela?