Comment puis-je générer des masses terrestres flottantes pour un moteur de type Minecraft?

19

Je crée un moteur de type Minecraft dans XNA. Ce que je veux faire, c'est créer des îles flottantes similaires à celle montrée dans cette vidéo:

http://www.youtube.com/watch?v=gqHVOEPQK5g&feature=related

Comment pourrais-je reproduire cela en utilisant un générateur de monde? Dois-je utiliser un algorithme de bruit Perlin? Je ne sais pas comment cela pourrait m'aider à faire des masses terrestres comme ça.

Voici le code du générateur de bruit perlin que j'utilise:

    private double[,] noiseValues;
    private float amplitude = 1;    // Max amplitude of the function
    private int frequency = 1;      // Frequency of the function

    /// <summary>
    /// Constructor
    /// </summary>
    /// 
    public PerlinNoise(int freq, float _amp)
    {
        Random rand = new Random(System.Environment.TickCount);
        noiseValues = new double[freq, freq];
        amplitude = _amp;
        frequency = freq;

        // Generate our noise values
        for (int i = 0; i < freq; i++)
        {
            for (int k = 0; k < freq; k++)
            {
                noiseValues[i, k] = rand.NextDouble();
            }
        }
    }

    /// <summary>
    /// Get the interpolated point from the noise graph using cosine interpolation
    /// </summary>
    /// <returns></returns>
    public double getInterpolatedPoint(int _xa, int _xb, int _ya, int _yb, double x, double y)
    {
        double i1 = interpolate(
            noiseValues[_xa % Frequency, _ya % frequency],
            noiseValues[_xb % Frequency, _ya % frequency]
            , x);

        double i2 = interpolate(
            noiseValues[_xa % Frequency, _yb % frequency],
            noiseValues[_xb % Frequency, _yb % frequency]
            , x);

        return interpolate(i1, i2, y);
    }

    public static double[,] SumNoiseFunctions(int width, int height, List<PerlinNoise> noiseFunctions)
    {
        double[,] summedValues = new double[width, height];

        // Sum each of the noise functions
        for (int i = 0; i < noiseFunctions.Count; i++)
        {
            double x_step = (float)width / (float)noiseFunctions[i].Frequency;
            double y_step = (float)height / (float)noiseFunctions[i].Frequency;

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    int a = (int)(x / x_step);
                    int b = a + 1;
                    int c = (int)(y / y_step);
                    int d = c + 1;

                    double intpl_val = noiseFunctions[i].getInterpolatedPoint(a, b, c, d, (x / x_step) - a, (y / y_step) - c);
                    summedValues[x, y] += intpl_val * noiseFunctions[i].Amplitude;
                }
            }
        }
        return summedValues;
    }

    /// <summary>
    /// Get the interpolated point from the noise graph using cosine interpolation
    /// </summary>
    /// <returns></returns>
    private double interpolate(double a, double b, double x)
    {
        double ft = x * Math.PI;
        double f = (1 - Math.Cos(ft)) * .5;

        // Returns a Y value between 0 and 1
        return a * (1 - f) + b * f;
    }

    public float Amplitude { get { return amplitude; } }
    public int Frequency { get { return frequency; } }

Mais le fait est que l'auteur du code utilise ce qui suit pour générer du bruit, et je ne le comprends pas le moins du monde.

    private Block[, ,] GenerateLandmass()
    {
        Block[, ,] blocks = new Block[300, 400, 300];

        List<PerlinNoise> perlins = new List<PerlinNoise>();
        perlins.Add(new PerlinNoise(36, 29));
        perlins.Add(new PerlinNoise(4, 33));

        double[,] noisemap = PerlinNoise.SumNoiseFunctions(300, 300, perlins); 

        int centrey = 400 / 2;

        for (short x = 0; x < blocks.GetLength(0); x++)
        {
            for (short y = 0; y < blocks.GetLength(1); y++)
            {
                for (short z = 0; z < blocks.GetLength(2); z++)
                {
                    blocks[x, y, z] = new Block(BlockType.none);
                }
            }
        }

        for (short x = 0; x < blocks.GetLength(0); x++)
        {
            for (short z = 0; z < blocks.GetLength(2); z++)
            {
                blocks[x, centrey - (int)noisemap[x, z], z].BlockType = BlockType.stone; 
            }
        }

        //blocks = GrowLandmass(blocks);

        return blocks;
    }

Et voici le site que j'utilise: http://lotsacode.wordpress.com/2010/02/24/perlin-noise-in-c/ .

Et j'essaie d'implémenter le bruit perlin de la manière spécifiée par Martin Sojka.

Ok, voici ce que j'ai obtenu jusqu'à présent:

entrez la description de l'image ici

Darestium
la source

Réponses:

21

Pour le terrain de base, créez deux champs de bruit continus 2D (Perlin, Simplex, Wavelet, une combinaison de ceux-ci - ce qui fonctionne pour vous), l'un avec principalement des basses fréquences. parties à faible amplitude pour la limite supérieure du terrain, l'autre avec à la fois des parties à haute fréquence et à haute amplitude et une basse fréquence à haute amplitude pour la limite inférieure du terrain. Lorsque la limite inférieure est supérieure à la limite supérieure, n'incluez aucun voxel terrestre (ou tout ce que votre jeu utilisera pour représenter le terrain). Le résultat final ressemble à peu près à ceci ...

Martin Sojka
la source
Mais c'est pour la 2D n'est-ce pas?
Darestium
Mais je l'aime bien :)
Darestium
4
2D / 3D - même chose
Gavin Williams
OK, mauvaise tentative pour l'implémenter demain ... Souhaitez-moi bonne chance;)
Darestium
@Darestium: C'est un exemple 2D pour une visualisation plus facile. La même méthode fonctionne pour n'importe quel nombre de dimensions (algébriques) supérieures à une.
Martin Sojka
15

Est-ce que quelque chose comme ça suffirait?

entrez la description de l'image ici

Si c'est le cas, consultez cet article . Citant les parties les plus pertinentes:

Afin d'obtenir un bruit plus intéressant, plusieurs octaves de bruit simplex peuvent être ajoutées ensemble. [...] Puisque je veux obtenir une sorte de roche flottante à peu près sphérique, je dois multiplier le bruit avec sa distance du centre. [...] Je veux aussi que la roche soit plus plate en haut qu'en bas, donc un deuxième facteur de multiplication est un gradient dans la direction y. En les combinant ensemble et en étirant y pour le bruit tout en compressant x et za bit, nous obtenons quelque chose comme une roche flottante. [...] L'excavation de grottes avec un autre exemple de décalage de bruit un peu le rend également plus intéressant.

  • Donc, fondamentalement, vous commencerez avec un ensemble de données générées à partir de bruit simplex ou perlin (ou plutôt plusieurs octaves de bruit additionnées ).
  • Ensuite, façonnez-le en quelque chose de plus proche d'une masse terrestre flottante en le rendant plus sphérique (en multipliant le bruit par sa distance par rapport au centre ).
  • Et créez du terrain en le rendant plus plat près du sommet (en le multipliant par un gradient vertical, c'est-à-dire en commençant par des valeurs faibles en haut et en augmentant vers le bas).
  • Combinez ces trois et ajustez la forme en mettant le bruit à l' échelle le long des axes X / Y / Z (l'article suggère d' étirer sur l'axe Y et de compresser sur les axes X et Z ).
  • Un bruit supplémentaire peut être utilisé pour creuser des grottes .
David Gouveia
la source
Oui, je pense que quelque chose comme ça est sans conteste ce que je veux. Le fait est que j'ai peu d'expérience avec le bruit perlin, donc la seule chose que je peux générer est vraiment des montagnes basiques et je n'aurais aucune idée sur la façon d'ajouter "plusieurs octaves de bruit ensemble). Pour la génération de bruit perlin j'utilise le code que je suis descendu de stackoverflow.com/questions/4753055/… et que je l'ai porté en C #. Je vais ajouter ma version dans le message d'origine ... Seriez-vous prêt à me donner un exemple de la façon dont j'atteindrais une telle masse terrestre avec cela code?
Darestium
2
C'est pourquoi j'ai lié l'article. Il a une explication de toutes les étapes et du code source à la fin. Vous devriez essayer d'étudier cela.
David Gouveia
4
  1. À l'aide de votre grille 3D existante, décidez de la hauteur à laquelle vous souhaitez que les sommets des îles soient. Créez un ensemble d'îles dans ce plan 2D (appelons-le le plan XY) en dispersant les points à travers le plan, puis en plaçant des cubes à ces points. Utilisez la cohésion pour les rapprocher les uns des autres en touffes. Remplissez tous les trous et vous avez un ensemble d'îlots.
  2. Utilisez une autorité de certification-méthode similaire pour faire pousser les îles vers le bas. (a) En partant du niveau Z où vous avez tracé vos points initiaux, pour chaque cellule de ce niveau Z actuel, déterminez la possibilité de s'étendre jusqu'au niveau inférieur suivant, compte tenu du nombre de voisins dans le plan XY, de 0 à 8 ( les voisins diagonaux sont inclus), par exemple, attribuez une chance de 10% à chaque voisin, jusqu'à 80% de chance au maximum. Calculez cela pour chaque cellule du plan de départ. (b) Ensuite, aléatoirement contre cette chance et étendez vers le bas si vous êtes dans la plage de pourcentage. Rincez, répétez l'étape 2 (passez au niveau suivant, déterminez les voisins pour chaque voxel, étendez-vous vers le bas pour ce voxel) jusqu'à ce qu'aucune extension ne se produise. Votre extension vers le bas devrait former un cône en raison de l'approche du nombre de voisins, car ces voxels vers le centre XY de l'île auront généralement plus de voisins.

Pseudocode pour l'étape 2:

int planeNeighbours[x][y]; //stores how many neighbours each voxel in this plane has

for each z level (starting at level where you plotted your points)
    for each x, y voxel in z level
        for each neighbour space bordering this voxel
            if neighbour exists
                ++planeNeighbours[x][y];
    for each x, y voxel in z level
        chance = random(0,8); //depends on your RNG implementation
        if chance < planeNeighbours[x][y]
            worldGrid[x][y][z+1] = new cube

Une fois vos îles générées, vous pouvez éventuellement les déplacer de haut en bas dans l'espace pour les avoir à différentes hauteurs.

Ingénieur
la source
OK, j'ai eu une fissure dans votre méthode et il semble que le terrain se développe vers l'extérieur plutôt que vers l'intérieur. Je
posterai