c++ Perlin噪声算法伪影

dy2hfwbg  于 2023-07-01  发布在  Perl
关注(0)|答案(1)|浏览(238)

我正在尝试实现Perlin 2D噪声算法。总的来说,它工作得很好,但它有一些人工制品。下面是我的代码:

#include "lodepng/lodepng.h"

#include <vector>
#include <numeric>
#include <iostream>
#include <algorithm>
#include <random>
#include <ctime>

#include  <glm/glm.hpp>

float lerp(float a, float b, float x)
{
    return a + x * (b - a);
}

float fade(float x)
{
    return x * x * x * (x * (6 * x - 15) + 10);
}

float gradient(glm::vec2 point, unsigned char seed)
{
    glm::vec2 randomVectors[] = {
        { 0.0f, 1.0f },
        { 0.0f, -1.0f },
        { 1.0f, 0.0f },
        { -1.0f, 0.0f }
    };

    return glm::dot(point, randomVectors[seed % 4]);
}

float perlinNoise(glm::vec2 point, int seed)
{
    std::vector<unsigned int> permutations(256);
    std::iota(permutations.begin(), permutations.end(), 0);

    std::mt19937 randomEngine(seed);
    std::shuffle(permutations.begin(), permutations.end(), randomEngine);

    for (int i = 0; i < 256; i++)
    {
        permutations.push_back(permutations[i]);
    }

    glm::vec2 gridCoords = glm::ivec2(point);
    glm::vec2 pointCoords = point - gridCoords;
    glm::vec2 distanceCoords = { fade(pointCoords.x), fade(pointCoords.y) };

    return (lerp(
        lerp(
            gradient(pointCoords, permutations[permutations[gridCoords.x] + gridCoords.y]),
            gradient({ pointCoords.x - 1, pointCoords.y }, permutations[permutations[gridCoords.x + 1] + gridCoords.y]),
            distanceCoords.x
        ),
        lerp(
            gradient({ pointCoords.x, pointCoords.y - 1 }, permutations[permutations[gridCoords.x] + gridCoords.y + 1]),
            gradient({ pointCoords.x - 1, pointCoords.y - 1 }, permutations[permutations[gridCoords.x + 1] + gridCoords.y + 1]),
            distanceCoords.x
        ),
        distanceCoords.y
    ) + 1) / 2;
}

int main()
{
    glm::ivec2 size = { 512, 512 };
    float frequency = 16.0f;

    std::vector<unsigned char> noiseImage{};
    noiseImage.reserve(size.x * size.y);

    for (int y = 0; y < size.y; y++)
    {
        for (int x = 0; x < size.x; x++)
        {
            noiseImage.push_back(std::round(255 * perlinNoise({ x / (size.x / frequency), y / (size.y / frequency) }, time(nullptr))));
        }
    }

    lodepng::encode("noise.png", noiseImage, size.x, size.y, LodePNGColorType::LCT_GREY);
}

Here is the result。它有2条水平线,这使得它不平滑,但我在我的实现中看不到任何错误。我该如何纠正这种行为?是我把结果错误地写入图像中,还是错误直接在算法中?

tcbh2hod

tcbh2hod1#

答案很简单:我应该只生成一次置换向量。下面是正确的代码:

#include "lodepng/lodepng.h"

#include <vector>
#include <numeric>
#include <iostream>
#include <algorithm>
#include <random>
#include <ctime>

#include  <glm/glm.hpp>

float lerp(float a, float b, float x)
{
    return a + x * (b - a);
}

float fade(float x)
{
    return x * x * x * (x * (6 * x - 15) + 10);
}

float gradient(glm::vec2 point, unsigned char seed)
{
    glm::vec2 randomVectors[] = {
        { 0.0f, 1.0f },
        { 0.0f, -1.0f },
        { 1.0f, 0.0f },
        { -1.0f, 0.0f }
    };

    return glm::dot(point, randomVectors[seed % 4]);
}

float perlinNoise(const std::vector<unsigned int>& permutations, glm::vec2 point)
{
    glm::vec2 gridCoords = glm::ivec2(point);
    glm::vec2 pointCoords = point - gridCoords;
    glm::vec2 distanceCoords = { fade(pointCoords.x), fade(pointCoords.y) };

    return (lerp(
        lerp(
            gradient(pointCoords, permutations[permutations[gridCoords.x] + gridCoords.y]),
            gradient({ pointCoords.x - 1, pointCoords.y }, permutations[permutations[gridCoords.x + 1] + gridCoords.y]),
            distanceCoords.x
        ),
        lerp(
            gradient({ pointCoords.x, pointCoords.y - 1 }, permutations[permutations[gridCoords.x] + gridCoords.y + 1]),
            gradient({ pointCoords.x - 1, pointCoords.y - 1 }, permutations[permutations[gridCoords.x + 1] + gridCoords.y + 1]),
            distanceCoords.x
        ),
        distanceCoords.y
    ) + 1) / 2;
}

std::vector<unsigned int> generatePermutations(std::mt19937& randomEngine)
{
    std::vector<unsigned int> permutations(256);
    std::iota(permutations.begin(), permutations.end(), 0);

    std::shuffle(permutations.begin(), permutations.end(), randomEngine);

    permutations.resize(512);
    std::copy_n(permutations.begin(), 256, permutations.begin() + 256);

    return permutations;
}

int main()
{
    glm::ivec2 size = { 512, 512 };
    float frequency = 16.0f;

    std::vector<unsigned char> noiseImage{};
    noiseImage.reserve(size.x * size.y);

    std::mt19937 randomEngine(time(nullptr));
    std::vector<unsigned int> permutations = generatePermutations(randomEngine);

    for (int y = 0; y < size.y; y++)
    {
        for (int x = 0; x < size.x; x++)
        {
            noiseImage.push_back(std::round(255 * perlinNoise(permutations, { x / (size.x / frequency), y / (size.y / frequency) })));
        }
    }

    lodepng::encode("noise.png", noiseImage, size.x, size.y, LodePNGColorType::LCT_GREY);
}

相关问题