Terrain lisse Voxel

13

En tant que projet personnel, j'essaie de créer un générateur de terrain qui créera un terrain ressemblant à un terrain lisse Castle Story.

Si vous ne l'avez pas encore vu, voici: entrez la description de l'image ici

Comme vous pouvez le voir, c'est une combinaison de blocs et de blocs "lisses".

Ce que j'ai essayé de faire pour émuler ce look, c'est de donner à chaque bloc de surface une mini-carte de hauteur. Cela fonctionne généralement, mais il y a quelques problèmes, ce qui donne un terrain comme celui-ci:

entrez la description de l'image ici

Le problème est que chaque bloc est 1x1x1, mais parfois la hauteur à un endroit particulier est négative ou> 1. Dans ce cas, je le clipse et règle la hauteur sur 0 ou 1.

Pour mieux illustrer ce que je veux dire, voici un diagramme: entrez la description de l'image ici

Pour générer la hauteur, je fais essentiellement:

genColumn(int x, int z)
{
    int highestBlockY = (int)noise2d(x, z);

    bool is_surface = true;

    for(int y = max_height - 1; y >= 0; y--)
    {
        Block b;

        if(is_surface)
        {
            b = Block.Grass;
            b.HasHeightMap = true;

            // generate heightmap
            for(int ix = 0; ix < 5; ix++)
            {
                for(int iz = 0; iz < 5; iz++)
                {
                    float heightHere = noise2d(x + ix / 4, z + iz / 4) - y;

                    // clip heights
                    if(heightHere > 1)
                        heightHere = 1;

                    if(heightHere < 0)
                        heightHere = 0;

                    b.HeightMap[ix][iz] = heightHere;
                }
            }

            is_surface = false;
        }
        else
        {
            b = Block.Dirt;
        }

        setBlock(x, y, z, b);
    }
}

Peut-être que j'aborde mal cela en utilisant la "vraie" valeur de bruit perlin?

Toute aide serait grandement appréciée!

sans titre
la source

Réponses:

11

Castle Story ressemble à ceci en raison de contraintes techniques: s'il y avait une carte de hauteur pour chaque voxel dans le volume entier, plutôt que seulement une carte de hauteur pour chaque voxel de surface , le coût de stockage serait considérablement plus élevé, de l'ordre de O (n ^ 3 ) qui peut être prohibitif, par opposition à un O plus favorable (n ^ 2), où n est la longueur latérale d'un espace de voxel cubique représentant votre monde. Gardez à l'esprit que les informations de carte de hauteur pour les voxels souterrains sont implicites dans la structure de la grille, donc les informations de carte de hauteur ne doivent être stockées explicitement que pour les voxels qui se trouvent à la surface. Ainsi, les gars de Castle Story ont claqué des sommets sur les interstices de la grille pour économiser sur les coûts de stockage et la complexité de la construction du maillage ... lisez la suite.

Tout d'abord, examinons vos options:

entrez la description de l'image ici

(1) va vous rendre les choses difficiles, car dans une seule colonne de voxel, vous ne voulez que des informations de mini-hauteur pour le voxel le plus haut - reportez-vous au premier paragraphe pour les raisons. En regardant (1), la colonne de droite contient des informations de carte de hauteur pour le haut et le deuxième à partir du haut (et cela pourrait s'appliquer au troisième à partir du haut et ainsi de suite, si la pente était suffisamment extrême). Ce n'est pas bien.

(2) Est probablement alors une meilleure option; c'est-à-dire, en s'assurant que les sommets des coins s'accrochent aux interstices de la grille de voxels. Mais comment alors aborder le problème des pentes extrêmes? Eh bien, nous devons choisir un gradient où nous nous contentons de nous accrocher à une colonne verticale et ainsi nous assurer que nous n'avons pas de dégradés qui peuvent traverser n voxels supérieurs. Un gradient de 45 degrés est la coupure naturelle pour les raisons que j'explique ci-dessous. Donc au lieu de (3), nous aurions (4):

entrez la description de l'image ici

(4) La solution du point d'angle du voxel à l'interstice de la grille la plus proche est la solution. L'effet visuel - aliasing diagonal - peut être vu dans votre capture d'écran de Castle Story. La pente de coupure pour les voxels est un gradient de 1: 1, ou 45 degrés (comme vu orthogonalement). En reculant de la solution, examinons les raisons:

  • La seule façon dont nous pourrions avoir une pente extrême ininterrompue, c'est si un voxel était allongé verticalement ...
  • .... Mais aucun maillage de voxel ne peut dépasser sa zone de délimitation, quelle que soit sa forme lissée réelle; un voxel doit s'asseoir dans un espace défini dans une grille 3D qui tient compte, sinon de sa forme exacte, du moins de sa boîte englobante alignée sur l'axe.

Une autre raison de l'aborder de cette façon est que, comme vous l'avez déjà découvert, le fait de ne pas utiliser l'accrochage (discrétisation) conduit à une gamme de scénarios de lissage de surface géométriquement complexes, qu'il vaut mieux éviter complètement ... les jeux de ce type ne nécessitent généralement pas le degré de précision qu'un algorithme CSG approprié fournirait, c'est la raison pour laquelle nous utilisons les voxels en premier lieu: les voxels facilitent beaucoup plus le travail incrémentiel avec les volumes que les algorithmes d'intersection de polygones à virgule flottante (continu) / CSG .

Ingénieur
la source
J'ai juste essayé de l'implémenter et j'ai rencontré une certaine confusion - que voulez-vous dire par «interstices»? Voulez-vous dire les coordonnées de la grille intégrale?
sans titre
@ThomasBradworth. "Un espace qui intervient entre les choses." Pour la 2D, si vous avez une grille de cellules anxn, vous aurez (n + 1) x (n + 1) interstices. Cela s'étend directement à la 3D. Ce sont les "sommets" qui délimitent chaque cellule, qui sont principalement partagés entre plusieurs cellules / voxels. Si vous le réduisez à 1D (une ligne), alors si les cellules et les interstices sont représentés sous forme de tableau, la cellule 0 est limitée par l'interstice 0 et l'interstice 1, tandis que la cellule 1 est limitée par l'interstice 1 et l'interstice 2 ... etc.
Ingénieur
Merci pour la réponse! J'ai essayé de faire exactement cela, mais j'ai obtenu des résultats défavorables. Au début, j'ai simplement essayé d'arrondir les valeurs de hauteur sur les bords (lorsque x est 0 ou 4, ou z est 0 ou 4), mais cela m'a donné: i.imgur.com/eQW7Y.png ( pastebin.com/Lr8vyyHB ) Ensuite, j'ai essayé de lisser les points du milieu, j'ai donc ajouté une "fonction de hauteur fixe": pastebin.com/AazQ07Xm Cela, cependant, m'a aussi donné un résultat plus anguleux, mais aussi mauvais: i.imgur.com/q1JVP .png Peut-être que quelque chose ne va pas avec ma fonction de bruit?
sans titre
@ThomasBradsworth Malheureusement, je sens que vous n'avez qu'à demi-lu / compris la réponse que j'ai pris plus d'une heure pour écrire et éditer. Oubliez le bruit; de plus, si vous ne comprenez pas la fonction de bruit, supprimez-la de l'équation pour l'instant. Votre problème réside dans la façon dont les données de maillage de voxels individuelles sont construites, et le bruit n'a rien à voir avec cela, car vous pouvez créer une carte de hauteur en l'absence de bruit. Travaillez avec le cas de test le plus simple, c'est-à-dire codez en dur le tableau de hauteur. Affichez ensuite vos résultats et ajustez votre algorithme de génération de maillage. Répétez jusqu'à ce qu'il fasse ce qui est attendu. Remettez ensuite le bruit en place et passez en revue.
Ingénieur
Voulez-vous dire que je devrais simplement stocker les valeurs de hauteur pour les coins et non pas stocker les valeurs de hauteur des points "intermédiaires"?
sans titre