Генерация шума Perlin для ландшафта

Я пытаюсь реализовать исходный код, который нашел в Интернете, чтобы создать карта высот с использованием Perlin Noise. Мне успешно удалось получить карту высот с помощью функции noise3, причем третья координата является случайным «семенем», что позволяет создавать случайные карты высот.

Моя проблема в том, что созданный ландшафт довольно скучный - мне нужны горы, и я получаю холмистые луга. Я прочитал о Perlin Noise (в основном здесь) . Из-за исходного кода, который я обнаружил явно не написанным с учетом удобочитаемости, и моего слабого понимания концепции шума Перлина в целом, я не могу понять, что мне нужно настроить в коде (амплитуду и частоту?), Чтобы создать более крутой ландшафт.

Также будет приветствоваться дополнительная информация о создании карт высот с использованием шума Перлина, шума Перлина в целом или даже более дешифрируемого кода.

РЕДАКТИРОВАТЬ: Я понимаю (отчасти), как работает Perlin Noise, например, в отношении амплитуды и частоты, мне просто интересно, какие переменные изменить в приведенном выше коде, которые используются для этих два аспекта.


person Emma    schedule 20.01.2011    source источник
comment
Обычный шум Перлина (1 слой шума) утомителен. То, что вы ищете, обычно называют фрактальным броуновским шумом движения (fBm), когда вы складываете шум Перлина разных частот вместе. См. здесь для подробных определений лакунарности, частоты, октавы относительно фрактального шума Перлина.   -  person bobobobo    schedule 22.12.2012
comment
Для тех, кто придет позже: концепции, которые Эрик пытался реализовать на основе статьи Элиаса, на самом деле не шум Перлина, а шум значений, наложенный на фрактальный (розовый) шум.   -  person LarsH    schedule 10.01.2017


Ответы (4)


Шум Перлина полностью контролируется различными установленными вами переменными, то есть амплитудой, частотой и стойкостью. Количество октав немного изменилось, но не сильно. В коде, который я написал в прошлом, я просто поиграл с порядком величины частоты и стойкости, пока не получил то, что мне нужно. При необходимости я могу попытаться найти свой старый источник.

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);
}
person Nick Banks    schedule 20.01.2011
comment
Для записи, несмотря на название, показанный здесь код представляет собой реализацию шума value, наложенного на фрактальный шум. Это не шум Перлина, который является разновидностью градиентного шума (en.wikipedia .org / wiki / Gradient_noise). - person LarsH; 10.01.2017

Друг только что связал меня с этим вопросом, и я подумал, что попытаюсь прояснить пару вещей, которые не рассматриваются в принятом ответе.

В интересной и полезной статье Элиаса используется «шум значений», а не «шум Перлина». Ценностный шум включает подгонку кривой рандомизированных точек. Градиентный шум (основным примером которого является шум Перлина) создает решетку из точек с нулевым значением и дает каждой из них случайный градиент. Их часто путают друг с другом!

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

Во-вторых, использование третьего значения в качестве начального числа дорого. Если вам нужна случайная местность, подумайте о переводе вашего происхождения в случайную величину. Звонки в формате 3D будут дороже, чем звонки в формате 2D. И все, что вы делаете, это используете значение z для выбора определенного фрагмента 2D-шума.

В-третьих, прямой вызов функции будет возвращать значения, которые в целом довольно плавные и скользящие, а не такие скалистые, как реальный ландшафт, поскольку случайность ограничена одной частотой. Чтобы получить более сложную местность, хорошей техникой является суммирование нескольких вызовов, которые проходят через шумовое пространство с разными частотами, обычно устанавливая «фрактальные» значения.

Таким образом, например, сложите вместе _1 _...

Результирующая сумма, вероятно, будет часто выходить за пределы диапазона от -1 до 1, поэтому вам придется нормализовать результат, прежде чем значения станут полезными. Я хотел бы предложить настроить ряд факторов (1, 1/2, 1/4 и т. Д.), Чтобы вы гарантированно оставались в пределах [-1, 1], что можно сделать путем прогрессивного взвешивания в зависимости от того, сколько "октавы", которые вы используете. (Но я не знаю, действительно ли это самый эффективный способ сделать это.)

Пример с четырьмя октавами: (1/15)(noise(x, y) + (2/15)(noise(2x, 2y) + (4/15)(noise(4x, 4y) + (8/15)(noise(8x, 8y)

Затем используйте нормализацию "турбулентного шума", взяв сумму и сделав ее = |sum| (то есть, используя функцию abs). Это придаст местности четко выраженные угловатые гребни долины, а не плавно перекатывается.

Я работаю над визуализатором SimplexNoise, надеюсь, в конечном итоге открою его исходный код на GitHub как проект Java. Первый черновик визуализатора можно найти и запустить в этом сообщении на java-gaming.org: http://www.java-gaming.org/topics/simplex-noise-experiments-towards-procedural-generation/27163/view.html Основное внимание в первом черновике уделяется руководству с примерами сгенерированного кода (но они написаны на Java).

Отличная статья о том, как работает SimplexNoise (и Perlin vs Gradient background): http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf

Стефан Густавсон отлично с этим справился!

person Phil Freihofner    schedule 27.09.2012
comment
Визуализатор, о котором я упоминал выше, находится на github.com/philfrei/SiVi. Хотел бы, чтобы некоторые люди помогли добавить новые функции, если есть какой-либо интерес. - person Phil Freihofner; 02.03.2015

Амплитуда определяет, насколько высоко / низко находится местность, частота, насколько она течет, при более низкой частоте более плавной.

Так что, если вам нужен неровный горный пейзаж, вам нужно подняться и на то, и на другое.

person wich    schedule 20.01.2011
comment
Для начала ему могут потребоваться приблизительные цифры - если так: 2-6 для частоты, подходящей для небольших ландшафтов, октавы 1-16 подходят, amp - просто нормализованное поле. Я генерирую город и выбираю здания на его основе для сетки 32x32, и считаю, что эти диапазоны подходят ... freq 5+ дает мне лучшие результаты для того, что я делаю. - person SinisterRainbow; 02.12.2012

Вот пример генерации поверхностей, который я написал некоторое время назад на JavaScript с использованием 3D Perlin Noise. Поскольку на поверхности воксели либо присутствуют, либо нет, я просто применяю порог после вычисления куба шума Перлина. В примере вероятность шума одинакова для всех измерений. Вы можете получить более реалистичный пейзаж, если увеличите случайные значения по направлению к земле и уменьшите их по направлению к небу.

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

WebGL должен быть включен. На момент написания этой статьи я рекомендовал использовать Chrome для максимальной производительности.

person oyophant    schedule 27.08.2013