Comment charger des morceaux d'empilage à la volée?

8

Je travaille actuellement sur un monde infini, principalement inspiré de minecraft.
Un chunk est constitué de 16x16x16 blocs. Un bloc (cube) est 1x1x1.

Cela fonctionne très bien avec une ViewRange de 12 morceaux (12x16) sur mon ordinateur. Bien.
Lorsque je change la hauteur du morceau à 256, cela devient - évidemment - un décalage incroyable.

Donc, ce que je veux faire, c'est empiler des morceaux. Cela signifie que mon monde pourrait être [∞, 16, ∞] gros morceaux.

La question est maintenant de savoir comment générer des morceaux à la volée?
Pour le moment, je génère des morceaux non existants circulaires autour de ma position (de près à loin). Comme je n'empile pas encore de morceaux, ce n'est pas très complexe.

Comme remarque importante ici: Je veux aussi avoir biomes, avec différentes hauteurs min / max. Ainsi , dans Biome Flatlands la couche la plus élevée avec des blocs serait 8 (8x16) - dans Biome montagnes la plus haute couche avec des blocs serait 14 (14x16). Comme exemple.

Ce que je pourrais faire serait de charger 1 morceau au-dessus et en dessous de moi par exemple.
Mais ici, le problème serait que les transitions entre différents biomes pourraient être plus grandes qu'un morceau sur y.




Transitions entre biomes




Mon chargement de morceaux actuel en action

Exemple de chargement de morceaux



Pour l'exhaustivité ici, mon "algorithme" de chargement de blocs actuel

private IEnumerator UpdateChunks(){
    for (int i = 1; i < VIEW_RANGE; i += ChunkWidth) {
        float vr = i;
        for (float x = transform.position.x - vr; x < transform.position.x + vr; x += ChunkWidth) {
            for (float z = transform.position.z - vr; z < transform.position.z + vr; z += ChunkWidth) {

                _pos.Set(x, 0, z); // no y, yet
                _pos.x = Mathf.Floor(_pos.x/ChunkWidth)*ChunkWidth;
                _pos.z = Mathf.Floor(_pos.z/ChunkWidth)*ChunkWidth;

                Chunk chunk = Chunk.FindChunk(_pos);

                // If Chunk is already created, continue
                if (chunk != null)
                    continue;

                // Create a new Chunk..
                chunk = (Chunk) Instantiate(ChunkFab, _pos, Quaternion.identity);
            }
        }
        // Skip to next frame
        yield return 0;
    }
}
Brettetete
la source

Réponses:

1

Ce que vous devez considérer pour charger / créer un morceau au-dessus et en dessous de la surface dans une pile donnée lorsque le joueur est à la surface, donc votre algorithme de génération doit se soucier des piles au niveau supérieur plutôt que des morceaux ... lorsque le joueur est sous le sol un au-dessus et en dessous du niveau de bloc actuel est très bien. Pour clarifier, une pile est une colonne verticale de morceaux du substratum rocheux à la stratosphère :)

Une autre façon de voir les choses serait de dire si la surface est en dessous du niveau de bloc actuel du joueur - générer la surface et une en dessous, sinon générer le niveau actuel et une au-dessus et en dessous.

Disons donc que votre monde aura 256 morceaux de haut (* 16 = 4096 blocs de voxels), et à tout moment si une pile est à portée de vue, vous aurez de 1 à 3 morceaux dans cette pile réellement chargés et rendus.

Les biomes présentent un problème supplémentaire de mélange des hauteurs sur les bords, mais vous pouvez gérer cela dans le code spécifique au biome qui sera appelé pour générer les caractéristiques de surface et de sous-surface. Si vous utilisez du bruit perlin / simplex pour générer des hauteurs, si un morceau borde un morceau qui est un biome différent, vous pouvez obtenir les valeurs de bruit que les deux types de biomes généreraient, puis les faire la moyenne.

Ascendion
la source
0

Ce que vous pouvez faire, c'est de faire un bloc de 256 de haut dans la direction y et de le diviser en 16 sections, chacune ayant 16 blocs de haut. Vous générez ensuite les données pour le bloc et créez la géométrie à l'intérieur des sections.

Un avantage serait que vous ayez accès aux données d'un morceau complet, ce qui facilite l'accès aux données au-dessus et en dessous d'une section.

Cela a également l'avantage de pouvoir éliminer facilement beaucoup de géométrie qui ne se trouve pas à l'intérieur du tronc de visualisation. Il y aura également de nombreuses sections qui n'auront aucune géométrie.

Si vous ne l'utilisez pas déjà, le chargement des morceaux dans un autre thread peut également vous donner de meilleures fréquences d'images, tout en générant les données pour chaque morceau.

user000user
la source
1
Eh bien, je me suis peut-être expliqué un peu vague. J'ai déjà prévu de séparer un morceau en 16 couches. Mais comment décider des couches à charger? Chaque biome a sa propre hauteur min / max. Imaginez le biome A [100,20] Biome B [160,200] - Je suis au bord des deux biomes. Ils ont une transition en douceur. Comment décider quelles couches charger? Mais en écrivant cela, je pense que je n'ai qu'à vérifier chaque calque (de haut en bas) et le créer, lorsque le calque ci-dessus est vide / transparent - et s'arrêter lorsque le premier calque est créé. J'espère que vous obtenez ceci;) Il est ennuyeux de ne pas pouvoir ajouter de lignes vides dans les commentaires
Brettetete
3
Je vois ce que tu veux dire. Mais si vous implémentez un algorithme comme le maillage gourmand, vous devriez pouvoir charger toutes les couches sans perte de performances énorme. Vous pouvez trouver un article sur le maillage gourmand ici . Je fais de même dans mon moteur voxel et ça fonctionne bien jusqu'à présent.
user000user
Cette technique est tout simplement incroyable. Je vais essayer de l'implémenter dans mon moteur. Le code donné est un peu bizarre, partageriez-vous votre implémentation? :)
Brettetete
2
J'ai collé mon implémentation C ++ ici et ajouté quelques commentaires. J'espère que ça aide :)
user000user
Génial, cela semble être très utile. Merci! Je vais essayer de traduire ça maintenant. Mais sur # 67 - # 68 il y a un double && - avez-vous manqué / doublé accidentellement quelque chose à copier? :) Et pourriez-vous brièvement expliquer / publier les méthodes SHIFT_ ? De plus, je ne vois aucune déclaration / utilisation de tmp - Eh bien, sory pour toutes ces questions
Brettetete
0

Je ne pense pas que vous puissiez le faire en chargeant simplement certaines couches à cause du problème des transitions.

Ma tendance serait de stocker des métadonnées avec chaque morceau:

1) Le bloc est-il entièrement aérien. Si c'est le cas, il n'est pas nécessaire de le restituer.

2) Pour chaque face du bloc est-elle opaque. Un visage opaque signifie que vous n'avez pas besoin de considérer le prochain morceau. (Notez, cependant, que selon l'endroit où se trouve le morceau, il peut y avoir jusqu'à trois faces impliquées - un morceau doit être rendu si l'un des trois n'est pas opaque. Je soupçonne que c'est mieux pré-calculé - rendu si long soit b1 est visible et a une face non opaque f1 soit b2 est visible a une face non opaque f2 ou b3 est visible a une face non opaque f3.)

Il y a malheureusement plus de 7000 morceaux dans votre plage de vision de 12 morceaux. Cependant, je m'attendrais à ce que peu d'emplacements aient plus de trois morceaux verticaux qui doivent être rendus en utilisant cette approche qui réduit le nombre de morceaux à probablement pas plus de 1500.

J'appliquerais le même type de logique au sein d'un morceau - lorsque vous chargez un morceau, calculez quelles jonctions sont transparentes et quelles jonctions sont opaques touchant opaques - il vous suffit de rendre les faces où quelqu'un peut les voir. (Notez que dans Minecraft, vous avez trois types de blocs - transparents, opaques et altérant la vision - verre, portes, clôtures, etc. Vous ne pouvez ignorer que transparent-transparent et opaque-opaque.)

Loren Pechtel
la source