Алгоритм Perlin Noise не производит градиентного шума

Я пытаюсь реализовать Perlin Noise на C ++.

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

Однако, насколько я понимаю, он должен выглядеть примерно так:  Ожидаемый выход шума Perlin

То есть шум, который я производю в настоящее время, больше похож на "стандартный" нерегулярный шум.

Это алгоритм шума Перлина, который я реализовал до сих пор:

float perlinNoise2D(float x, float y)
{
    // Find grid cell coordinates
    int x0 = (x > 0.0f ? static_cast<int>(x) : (static_cast<int>(x) - 1));
    int x1 = x0 + 1;
    int y0 = (y > 0.0f ? static_cast<int>(y) : (static_cast<int>(y) - 1));
    int y1 = y0 + 1;

    float s = calculateInfluence(x0, y0, x, y);
    float t = calculateInfluence(x1, y0, x, y);
    float u = calculateInfluence(x0, y1, x, y);
    float v = calculateInfluence(x1, y1, x, y);

    // Local position in the grid cell
    float localPosX = 3 * ((x - (float)x0) * (x - (float)x0)) - 2 * ((x - (float)x0) * (x - (float)x0) * (x - (float)x0));
    float localPosY = 3 * ((y - (float)y0) * (y - (float)y0)) - 2 * ((y - (float)y0) * (y - (float)y0) * (y - (float)y0));

    float a = s + localPosX * (t - s);
    float b = u + localPosX * (v - u);

    return lerp(a, b, localPosY);
}

Функция calculateInfluence генерирует случайный вектор градиента и вектор расстояния для одной из угловых точек текущей ячейки сетки и возвращает их скалярное произведение. Реализован он как:

float calculateInfluence(int xGrid, int yGrid, float x, float y)
{
    // Calculate gradient vector
    float gradientXComponent = dist(rdEngine);
    float gradientYComponent = dist(rdEngine);

    // Normalize gradient vector
    float magnitude = sqrt( pow(gradientXComponent, 2) + pow(gradientYComponent, 2) );
    gradientXComponent = gradientXComponent / magnitude;
    gradientYComponent = gradientYComponent / magnitude;
    magnitude = sqrt(pow(gradientXComponent, 2) + pow(gradientYComponent, 2));

    // Calculate distance vectors
    float dx = x - (float)xGrid;
    float dy = y - (float)yGrid;

    // Compute dot product
    return (dx * gradientXComponent + dy * gradientYComponent);
}

Здесь dist - генератор случайных чисел из C ++ 11:

std::mt19937 rdEngine(1);
std::normal_distribution<float> dist(0.0f, 1.0f);

А lerp просто реализован как:

float lerp(float v0, float v1, float t)
{
    return ( 1.0f - t ) * v0 + t * v1;
}

Для реализации алгоритма я в первую очередь использовал следующие два ресурса:

Часто задаваемые вопросы по шуму Perlin Псевдокод шума Перлина

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

Точно так же может случиться так, что я неправильно усредняю ​​значения влияния. Из статьи часто задаваемых вопросов о Perlin Noise было немного сложно понять, как именно это должно быть сделано.

Есть ли у кого-нибудь подсказки относительно того, что может быть не так с кодом? :)


person CodingBeagle    schedule 29.05.2016    source источник


Ответы (1)


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

Чтобы сгенерировать многооктавный шум, используйте что-то вроде этого:

float multiOctavePerlinNoise2D(float x, float y, int octaves)
{
    float v = 0.0f;
    float scale = 1.0f;
    float weight = 1.0f;
    float weightTotal = 0.0f;
    for(int i = 0; i < octaves; i++)
    {
        v += perlinNoise2D(x * scale, y * scale) * weight;
        weightTotal += weight;
        // "ever-increasing frequencies and ever-decreasing amplitudes"
        // (or conversely decreasing freqs and increasing amplitudes)
        scale *= 0.5f; 
        weight *= 2.0f;
    }
    return v / weightTotal;
}

Для дополнительной случайности вы можете использовать генератор случайных чисел с разными начальными значениями для каждой октавы. Кроме того, веса, присвоенные каждой октаве, могут варьироваться, чтобы регулировать эстетическое качество шума. Если весовая переменная не корректируется на каждой итерации, то приведенный выше пример является "розовым шумом" (каждый удвоение частоты имеет тот же вес).

Кроме того, вам необходимо использовать генератор случайных чисел, который каждый раз возвращает одно и то же значение для данной пары xGrid, yGrid.

person samgak    schedule 29.05.2016
comment
Ага! Кажется, проблема не в использовании октав :) Теперь я получаю гораздо лучшие результаты! - person CodingBeagle; 29.05.2016
comment
Невероятно, совсем недавно у меня тоже была эта проблема! - person Dave3of5; 24.06.2016