Generowanie hałasu Perlina dla terenu

Próbuję zaimplementować jakiś kod źródłowy, który znalazłem online do wygenerowania mapy wysokości za pomocą szumu Perlina. Udało mi się uzyskać mapę wysokości za pomocą funkcji noise3, a trzecia współrzędna jest losowym "ziarnem", aby umożliwić losowe mapy wysokości.

Mój problem polega na tym, że generowany teren jest raczej nudny - chcę gór i robię toczące się łąki. Poczytałem trochę o szumach Perlina (głównie tutaj ). Ze względu na kod źródłowy znalazłem oczywiście nie napisany z myślą o czytelności i moim słabym zrozumieniu pojęcia szumu Perlina w ogóle, nie mogę zrozumieć, co muszę poprawić w kodzie (amplituda i częstotliwość?) w celu stworzenia bardziej drastycznego terenu.

Trochę więcej informacji na temat generowania map wysokości przy użyciu szumu Perlina, szumu Perlina w ogóle, a nawet jakiegoś bardziej odszyfrowanego kodu byłoby mile widziane.

EDIT: Rozumiem (trochę) jak działa szum Perlina, np. w odniesieniu do amplitudy i częstotliwości, Zastanawiam się tylko, jakie zmienne zmienić w kodzie, który podlinkowałem powyżej, które są używane dla tych dwóch aspektów.

Author: LAD, 2011-01-21

4 answers

Szum Perlina jest całkowicie kontrolowany przez różne zmienne, które ustawiasz, tj. amplitudę, częstotliwość i trwałość. Ilość oktaw trochę się zmienia, ale niewiele. W kodzie, który napisałem w przeszłości, po prostu bawiłem się rzędem wielkości częstotliwości i wytrwałości, dopóki nie dostałem tego, czego potrzebowałem. Mogę spróbować znaleźć moje stare źródło w razie potrzeby.

PerlinNoise.h
#pragma once

class PerlinNoise
{
public:

  // Constructor
    PerlinNoise();
    PerlinNoise(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed);

  // Get Height
    double GetHeight(double x, double y) const;

  // Get
  double Persistence() const { return persistence; }
  double Frequency()   const { return frequency;   }
  double Amplitude()   const { return amplitude;   }
  int    Octaves()     const { return octaves;     }
  int    RandomSeed()  const { return randomseed;  }

  // Set
  void Set(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed);

  void SetPersistence(double _persistence) { persistence = _persistence; }
  void SetFrequency(  double _frequency)   { frequency = _frequency;     }
  void SetAmplitude(  double _amplitude)   { amplitude = _amplitude;     }
  void SetOctaves(    int    _octaves)     { octaves = _octaves;         }
  void SetRandomSeed( int    _randomseed)  { randomseed = _randomseed;   }

private:

    double Total(double i, double j) const;
    double GetValue(double x, double y) const;
    double Interpolate(double x, double y, double a) const;
    double Noise(int x, int y) const;

    double persistence, frequency, amplitude;
    int octaves, randomseed;
};
PerlinNoise.cpp
#include "PerlinNoise.h"

PerlinNoise::PerlinNoise()
{
  persistence = 0;
  frequency = 0;
  amplitude  = 0;
  octaves = 0;
  randomseed = 0;
}

PerlinNoise::PerlinNoise(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed)
{
  persistence = _persistence;
  frequency = _frequency;
  amplitude  = _amplitude;
  octaves = _octaves;
  randomseed = 2 + _randomseed * _randomseed;
}

void PerlinNoise::Set(double _persistence, double _frequency, double _amplitude, int _octaves, int _randomseed)
{
  persistence = _persistence;
  frequency = _frequency;
  amplitude  = _amplitude;
  octaves = _octaves;
  randomseed = 2 + _randomseed * _randomseed;
}

double PerlinNoise::GetHeight(double x, double y) const
{
  return amplitude * Total(x, y);
}

double PerlinNoise::Total(double i, double j) const
{
    //properties of one octave (changing each loop)
    double t = 0.0f;
    double _amplitude = 1;
    double freq = frequency;

    for(int k = 0; k < octaves; k++) 
    {
        t += GetValue(j * freq + randomseed, i * freq + randomseed) * _amplitude;
        _amplitude *= persistence;
        freq *= 2;
    }

    return t;
}

double PerlinNoise::GetValue(double x, double y) const
{
    int Xint = (int)x;
    int Yint = (int)y;
    double Xfrac = x - Xint;
    double Yfrac = y - Yint;

  //noise values
  double n01 = Noise(Xint-1, Yint-1);
  double n02 = Noise(Xint+1, Yint-1);
  double n03 = Noise(Xint-1, Yint+1);
  double n04 = Noise(Xint+1, Yint+1);
  double n05 = Noise(Xint-1, Yint);
  double n06 = Noise(Xint+1, Yint);
  double n07 = Noise(Xint, Yint-1);
  double n08 = Noise(Xint, Yint+1);
  double n09 = Noise(Xint, Yint);

  double n12 = Noise(Xint+2, Yint-1);
  double n14 = Noise(Xint+2, Yint+1);
  double n16 = Noise(Xint+2, Yint);

  double n23 = Noise(Xint-1, Yint+2);
  double n24 = Noise(Xint+1, Yint+2);
  double n28 = Noise(Xint, Yint+2);

  double n34 = Noise(Xint+2, Yint+2);

    //find the noise values of the four corners
    double x0y0 = 0.0625*(n01+n02+n03+n04) + 0.125*(n05+n06+n07+n08) + 0.25*(n09);  
    double x1y0 = 0.0625*(n07+n12+n08+n14) + 0.125*(n09+n16+n02+n04) + 0.25*(n06);  
    double x0y1 = 0.0625*(n05+n06+n23+n24) + 0.125*(n03+n04+n09+n28) + 0.25*(n08);  
    double x1y1 = 0.0625*(n09+n16+n28+n34) + 0.125*(n08+n14+n06+n24) + 0.25*(n04);  

    //interpolate between those values according to the x and y fractions
    double v1 = Interpolate(x0y0, x1y0, Xfrac); //interpolate in x direction (y)
    double v2 = Interpolate(x0y1, x1y1, Xfrac); //interpolate in x direction (y+1)
    double fin = Interpolate(v1, v2, Yfrac);  //interpolate in y direction

    return fin;
}

double PerlinNoise::Interpolate(double x, double y, double a) const
{
    double negA = 1.0 - a;
  double negASqr = negA * negA;
    double fac1 = 3.0 * (negASqr) - 2.0 * (negASqr * negA);
  double aSqr = a * a;
    double fac2 = 3.0 * aSqr - 2.0 * (aSqr * a);

    return x * fac1 + y * fac2; //add the weighted factors
}

double PerlinNoise::Noise(int x, int y) const
{
    int n = x + y * 57;
    n = (n << 13) ^ n;
  int t = (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff;
    return 1.0 - double(t) * 0.931322574615478515625e-9;/// 1073741824.0);
}
 55
Author: Nick Banks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-20 22:05:01

Znajomy połączył mnie z tym pytaniem i pomyślałem, że spróbuję wyjaśnić kilka rzeczy, które nie są uwzględnione w zaakceptowanej odpowiedzi.

Ciekawy i pomocny artykuł Eliasa używa "szumu wartości", a nie "szumu Perlina". Szum wartości polega na dopasowaniu krzywej punktów losowych. Szum gradientowy (którego podstawowym przykładem jest szum Perlina) tworzy siatkę punktów o wartości 0 i nadaje każdemu z nich losowy gradient. Często mylone są z jednym kolejny!

Http://en.wikipedia.org/wiki/Gradient_noise

Po drugie, używanie trzeciej wartości jako ziarna jest drogie. Jeśli chcesz losowego terenu, rozważ tłumaczenie swojego pochodzenia jako losową kwotę. Połączenia 3D będą droższe niż połączenia 2D. A wszystko, co robisz, to użycie wartości z, aby wybrać konkretny kawałek szumu 2D.

Po Trzecie, proste wywołanie funkcji zwróci wartości, które są dość gładkie i toczące się ogólnie, a nie tak skaliste jak prawdziwy teren, ponieważ jego losowość jest ograniczona do pojedynczej częstotliwości. Aby uzyskać trudniejszy teren, dobrą techniką jest zsumowanie wielu wywołań, które przechodzą przez przestrzeń szumów o różnych częstotliwościach, Zwykle ustawiając wartości "fraktalne".

Zatem np. sumujemy razem noise(x, y) + (1/2)(noise(x*2, y*2) + (1/4)(noise(x*4, y*4)...

Suma wynikowa prawdopodobnie często będzie poza zakresem -1 do 1, więc będziesz musiał znormalizować wynik, zanim wartości będą użyteczne. Chciałbym zaproponować utworzenie serii factor (1, 1/2, 1/4 itd.), dzięki czemu masz gwarancję, że pozostaniesz w granicach [-1, 1], co można zrobić poprzez stopniowe ważenie w zależności od liczby "oktaw", których używasz. (Ale nie wiem, czy jest to naprawdę najskuteczniejszy sposób, aby to zrobić.)

Przykład z czterema oktawami: (1/15)(noise(x, y) + (2/15)(noise(2x, 2y) + (4/15)(noise(4x, 4y) + (8/15)(noise(8x, 8y)

Następnie użyj" burzliwego szumu " normalizacji pobierania sumy i jej tworzenia = |sum| (np. za pomocą funkcji abs). Pozwoli to ukształtować ukształtowanie terenu w postaci kanciastych grzbietów dolin, a nie płynnie kręcimy.

Pracuję nad wizualizerem SimplexNoise, mam nadzieję otworzyć go na Githubie w końcu, jako projekt Java. Pierwszy szkic wizualizera można znaleźć i uruchomić w tym poście na stronie java-gaming.org: http://www.java-gaming.org/topics/simplex-noise-experiments-towards-procedural-generation/27163/view.html Nacisk na pierwszy szkic jest bardziej tutorial, z wygenerowanymi przykładami kodu (ale są one w Javie).

Świetny artykuł o tym, jak działa SimplexNoise (i Perlin vs Tło gradientowe): http://staffwww.itn.liu.se/ ~ stegu / simplexnoise / simplexnoise. pdf

Stefan Gustavson wykonał bardzo dobrą robotę!
 19
Author: Phil Freihofner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-10 11:25:29

Amplituda kontroluje jak wysoki / niski jest teren, częstotliwość jak płynie, przy czym niższa częstotliwość jest bardziej płynna.

Więc jeśli chcesz postrzępiony górzysty krajobraz, musisz podnieść oba.

 5
Author: wich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-20 21:56:00

Oto przykład generowania powierzchni, o którym pisałem jakiś czas temu w JavaScript, używając szumu Perlina 3D. Ponieważ na powierzchni występują albo voxele, albo nie, po prostu zastosuję próg po obliczeniu sześcianu szumu Perlina. W przykładzie prawdopodobieństwo szumu jest równe dla wszystkich wymiarów. Możesz uzyskać bardziej realistyczny krajobraz, gdy zwiększysz losowe wartości w kierunku ziemi i zmniejszysz je w kierunku nieba.

Http://kirox.de/test/Surface.html

WebGL musi być włączone. W momencie pisania tego polecam użycie Chrome dla najlepszej wydajności.

 2
Author: oyophant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-27 16:32:18