Подтвердить что ты не робот

Perlin Noise Generation для местности

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

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

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

EDIT: я понимаю (вид), как работает Perlin Noise, например: по амплитуде и частоте, мне просто интересно, какие переменные для изменения в коде, который я связал выше, используются для этих двух аспектов.

4b9b3361

Ответ 1

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

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

Ответ 2

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

Интересная и полезная статья Elias использует "шум ценности", а не "шум Perlin". Шум значения включает в себя скручивание рандомизированных точек. Шум градиента (из которого перлиновый шум является первичным) создает решетку 0-значных точек и дает каждому случайный градиент. Их часто путают друг с другом!

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

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

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

Таким образом, например, суммируйте вместе noise(x, y) + (1/2)(noise(x*2, y*2) + (1/4)(noise(x*4, y*4)...

Полученная сумма, вероятно, часто будет находиться вне диапазона от -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). Это даст определенному рельефу местности angular долины, в отличие от плавной прокатки.

Я работаю над визуализатором 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

Стефан Густавсон сделал действительно хорошую работу!

Ответ 3

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

Итак, если вам нужен зубчатый горный ландшафт, вам нужно обойти оба.

Ответ 4

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

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

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