Come posso generare masse di terra fluttuanti per un motore simile a Minecraft?


19

Sto creando un motore simile a Minecraft in XNA. Quello che voglio fare è creare isole galleggianti simili a quella mostrata in questo video:

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

Come potrei replicarlo usando un generatore mondiale? Dovrei usare qualche algoritmo di rumore Perlin? Non so come ciò mi aiuterebbe a creare masse terrestri così.

Ecco il codice per il generatore di rumore perlin che sto usando:

    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; } }

Ma il fatto è che l'autore del codice usa quanto segue per generare rumore, e non lo capisco affatto.

    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;
    }

Ed ecco il sito che sto usando: http://lotsacode.wordpress.com/2010/02/24/perlin-noise-in-c/ .

E sto cercando di implementare il rumore del perlin nel modo specificato da Martin Sojka.

Ok, questo è quello che ho ottenuto finora:

inserisci qui la descrizione dell'immagine

Risposte:


21

Per la terra di base, crea due campi di rumore continuo 2D (Perlin, Simplex, Wavelet, una loro combinazione - qualunque cosa funzioni per te), uno con frequenza prevalentemente bassa. parti a bassa ampiezza per il limite superiore del terreno, l'altra ad alta frequenza, parti ad alta ampiezza e bassa frequenza, ampiezza elevata per il limite inferiore del terreno. Laddove il limite inferiore sia superiore al limite superiore, non includere alcun voxel di terra (o qualunque cosa il gioco userà per rappresentare il terreno). Il risultato finale è approssimativamente simile a questo ...


Ma questo è per il 2D, no?
Darestium,

Ma mi piace abbastanza :)
Darestium,

4
2D / 3D - stessa cosa
Gavin Williams il

OK, mal tentativo di implementarlo domani ... Augurami buona fortuna;)
Darestium,

@Darestium: è un esempio 2D per una visualizzazione più semplice. Lo stesso metodo funziona per qualsiasi numero di dimensioni (algebriche) superiore a una.
Martin Sojka,

15

Qualcosa del genere sarebbe abbastanza?

inserisci qui la descrizione dell'immagine

In tal caso, consulta questo articolo . Citando le parti più rilevanti:

Per ottenere un rumore più interessante è possibile aggiungere più ottave di rumore simplex. [...] Dato che voglio ottenere una sorta di roccia galleggiante sferica, ho bisogno di moltiplicare il rumore per la sua distanza dal centro. [...] Voglio anche che la roccia sia più piatta in alto che in basso, quindi un secondo fattore di moltiplicazione è un gradiente in direzione y. Combinando questi insieme e allungando y per il rumore mentre comprimiamo x e za bit, otteniamo qualcosa come una roccia galleggiante. [...] Scavare le grotte con un'altra istanza di compensazione del rumore un po 'lo rende anche più interessante.

  • Quindi fondamentalmente inizierai con un set di dati generato dal rumore simplex o perlin (o piuttosto più ottave di rumore sommate insieme ).
  • Quindi modellalo in qualcosa di più vicino a una massa di terra galleggiante rendendolo più sferico (moltiplicando il rumore per la sua distanza dal centro ).
  • E crea terreno rendendolo più piatto vicino alla cima (moltiplicandolo per un gradiente verticale, cioè iniziando con valori bassi nella parte superiore e aumentando verso il basso).
  • Combina questi tre e regola la forma ridimensionando il rumore lungo gli assi X / Y / Z (l'articolo suggerisce l' allungamento sull'asse Y e la compressione sugli assi X e Z ).
  • Un ulteriore passaggio di rumore può essere utilizzato per scavare grotte .

Sì, penso che qualcosa del genere sia provocatoriamente ciò che voglio. Il fatto è che ho poca esperienza con il rumore del perlin, quindi l'unica cosa che posso generare sono montagne di base e non avrei alcuna idea su come aggiungere "più ottave di rumore insieme). Per la generazione del rumore di perlin sto usando il codice che sono uscito da stackoverflow.com/questions/4753055/… e l' ho portato su C #. Aggiungere la mia versione nel post originale ... Saresti disposto a darmi un esempio di come raggiungerei una tale massa di terra con quella codice?
Darestium,

2
Ecco perché ho collegato l'articolo. Ha una spiegazione di tutti i passaggi e il codice sorgente alla fine. Dovresti provare a studiarlo.
David Gouveia, il

4
  1. Usando la griglia 3D esistente, decidi l'altezza alla quale vuoi che si trovino le cime delle isole. Crea un insieme di isole in quel piano 2D (chiamiamolo piano XY) spargendo punti attraverso il piano, quindi posizionando i cubi in quei punti. Usa la coesione per avvicinarli insieme in gruppi. Riempi tutti i buchi e avrai un set di cime dell'isola.
  2. Usa una CA-modo simile per far crescere le isole verso il basso. (a) A partire dal livello Z in cui sono stati tracciati i punti iniziali, per ogni cella in quel livello Z corrente, determinare la possibilità di estendersi fino al livello inferiore successivo dato il numero di vicini nel piano XY, da 0 a 8 ( i vicini diagonali sono inclusi), ad esempio assegnare una probabilità del 10% per ciascun vicino, fino a una probabilità massima dell'80%. Calcola questo per ogni cella nel piano iniziale. (b) Quindi randomizza contro questa possibilità ed estendi verso il basso se ti trovi all'interno dell'intervallo percentuale. Risciacqua, ripeti il ​​passaggio 2 (vai al livello successivo, determina i vicini per ogni voxel, estendi verso il basso per quel voxel) fino a quando non si verificano più estensioni. L'estensione verso il basso dovrebbe formare un cono a causa dell'approccio del numero di vicini, perché quei voxel verso il centro XY dell'isola avranno in genere più vicini.

Pseudocodice per il passaggio 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

Una volta terminata la generazione delle tue isole, puoi opzionalmente spostarle su e giù nello spazio per averle ad altezze diverse.


OK, ho avuto una crepa nel tuo metodo e sembra che stia crescendo il terreno verso l'esterno anziché verso l'interno.
Pubblicherò
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.