J'essaie d'écrire un petit moteur de voxels parce que c'est amusant, mais j'ai du mal à trouver la meilleure façon de stocker les voxels réels. Je suis conscient que j'aurai besoin de morceaux en quelque sorte, donc je n'ai pas besoin d'avoir le monde entier en mémoire, et je suis conscient que j'ai besoin de les rendre avec des performances raisonnables.
J'ai lu des octrees et d'après ce que je comprends, cela commence par 1 cube, et dans ce cube peut être 8 cubes de plus, et dans tous ces 8 cubes peut être encore 8 cubes etc. Mais je ne pense pas que cela correspond à mon moteur de voxel parce que mes cubes / articles de voxel auront tous exactement la même taille.
Donc, une autre option consiste à créer simplement un tableau de taille 16 * 16 * 16 et à avoir un seul bloc, et vous le remplissez d'éléments. Et les pièces où il n'y a pas d'articles auront 0 comme valeur (0 = air). Mais je crains que cela ne gaspille beaucoup de mémoire et ne soit pas très rapide.
Ensuite, une autre option est un vecteur pour chaque morceau et remplissez-le de cubes. Et le cube tient sa position dans le morceau. Cela économise de la mémoire (pas de blocs d'air), mais rend la recherche d'un cube à un emplacement spécifique beaucoup plus lente.
Je ne peux donc pas vraiment trouver de bonne solution et j'espère que quelqu'un pourra m'aider. Alors, que voudriez-vous utiliser et pourquoi?
Mais un autre problème est le rendu. Il suffit de lire chaque morceau et de l'envoyer au GPU à l'aide d'OpenGL, mais c'est très lent. Générer un maillage par morceau serait mieux, mais cela signifie que chaque fois que je casse un bloc, je dois reconstruire le morceau entier, ce qui pourrait prendre un peu de temps, provoquant un hoquet mineur mais perceptible, ce que je ne veux évidemment pas non plus. Ce serait donc plus difficile. Alors, comment pourrais-je rendre les cubes? Il suffit de créer tous les cubes dans un tampon de vertex par bloc et de le rendre et peut-être d'essayer de le mettre dans un autre thread, ou y a-t-il une autre façon?
Merci!
Réponses:
Le stockage des blocs en tant que positions et valeurs est en fait très inefficace. Même sans surcharge due à la structure ou à l'objet que vous utilisez, vous devez stocker 4 valeurs distinctes par bloc. Cela n'aurait de sens que de l'utiliser sur la méthode de "stockage de blocs dans des tableaux fixes" (celle que vous avez décrite plus tôt) lorsque seulement un quart des blocs sont solides, et de cette façon vous ne prenez même aucune autre méthode d'optimisation en Compte.
Les octrees sont en fait parfaits pour les jeux basés sur les voxels, car ils sont spécialisés dans le stockage de données avec des fonctionnalités plus grandes (par exemple, les correctifs du même bloc). Pour illustrer cela, j'ai utilisé un quadtree (essentiellement des octrees en 2d):
Ceci est mon jeu de départ contenant des tuiles 32x32, ce qui équivaudrait à 1024 valeurs:
Le stockage de 1024 valeurs distinctes ne semble pas inefficace, mais une fois que vous atteignez des tailles de carte similaires à des jeux, tels que Terraria , le chargement des écrans prend plusieurs secondes. Et si vous l'augmentez jusqu'à la troisième dimension, il commence à utiliser tout l'espace du système.
Les quadtre (ou octrees en 3D) peuvent aider la situation. Pour en créer un, vous pouvez soit partir des tuiles et les regrouper, soit partir d'une énorme cellule et la diviser jusqu'à atteindre les tuiles. J'utiliserai la première approche, car elle est plus facile à visualiser.
Donc, dans la première itération, vous regroupez tout en cellules 2x2, et si une cellule ne contient que des tuiles du même type, vous déposez les tuiles et stockez simplement le type. Après une itération, notre carte ressemblera à ceci:
Les lignes rouges marquent ce que nous stockons. Chaque carré n'a qu'une valeur. Cela a ramené la taille de 1024 à 439, soit une diminution de 57%.
Mais vous connaissez le mantra . Allons plus loin et regroupons-les en cellules:
Cela a réduit le nombre de valeurs stockées à 367. Cela ne représente que 36% de la taille d'origine.
Vous devez évidemment faire cette division jusqu'à ce que toutes les 4 cellules adjacentes (8 blocs adjacents en 3d) à l'intérieur d'un bloc soient stockées dans une cellule, convertissant essentiellement un bloc en une grande cellule.
Cela a également d'autres avantages, principalement lors d'une collision, mais vous pouvez créer un octree séparé pour cela, qui ne se soucie que de savoir si un seul bloc est solide ou non. De cette façon, au lieu de vérifier la collision pour chaque bloc à l'intérieur d'un bloc, vous pouvez simplement le faire contre les cellules.
la source
Des octrois existent pour résoudre exactement le problème que vous décrivez, permettant un stockage dense de données éparses sans temps de recherche importants.
Le fait que vos voxels soient de la même taille signifie simplement que votre octree a une profondeur fixe. par exemple. pour un bloc 16x16x16, vous avez besoin d'au plus 5 niveaux d'arbre:
Cela signifie que vous avez au plus 5 étapes pour savoir s'il y a un voxel à une position particulière dans le morceau:
Beaucoup plus court que la numérisation, même 1% du chemin à travers un tableau de jusqu'à 4096 voxels!
Notez que cela nous permet de compresser les données partout où il y a un octant complet de la même valeur - que cette valeur soit entièrement aérienne ou entièrement rocheuse ou autre. C'est seulement là où les octants contiennent des valeurs mixtes que nous devons subdiviser davantage, jusqu'à la limite des nœuds foliaires à voxel unique.
Pour s'adresser aux enfants d'un morceau, nous procédons généralement dans l' ordre de Morton , quelque chose comme ceci:
Ainsi, notre navigation dans les nœuds Octree pourrait ressembler à ceci:
la source