Perlin噪声算法的伪影

huangapple go评论64阅读模式
英文:

Perlin noise algorithm artefacts

问题

我正在尝试实现 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);
}

这是结果。它有两条水平线,使其不平滑,但我在我的实现中找不到任何错误。我该如何修复这种行为?是我错误地将结果写入图像,还是算法本身存在问题?

英文:

I am tryng to imlpement Perlin 2D noise algorithm. Generally it works well, but it have got some artefacts in it. Here is my code:

#include &quot;lodepng/lodepng.h&quot;
#include &lt;vector&gt;
#include &lt;numeric&gt;
#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;random&gt;
#include &lt;ctime&gt;
#include  &lt;glm/glm.hpp&gt;
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&lt;unsigned int&gt; 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 &lt; 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&lt;unsigned char&gt; noiseImage{};
noiseImage.reserve(size.x * size.y);
for (int y = 0; y &lt; size.y; y++)
{
for (int x = 0; x &lt; size.x; x++)
{
noiseImage.push_back(std::round(255 * perlinNoise({ x / (size.x / frequency), y / (size.y / frequency) }, time(nullptr))));
}
}
lodepng::encode(&quot;noise.png&quot;, noiseImage, size.x, size.y, LodePNGColorType::LCT_GREY);
}

Here is the result. It has got 2 horisontal lines, which are making it not smooth, but I can not see any bugs in my implementation. How can I fix this behaviour? Am I writing the result into image incorrectly, or the mistake is directly in algorithm?

答案1

得分: 1

以下是您要翻译的代码部分:

The answer was quite simple: I should generate permutation vector only once. Here is the correct code:
#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);
}

希望这有助于您的需求。

英文:

The answer was quite simple: I should generate permutation vector only once. Here is the correct code:

#include &quot;lodepng/lodepng.h&quot;
#include &lt;vector&gt;
#include &lt;numeric&gt;
#include &lt;iostream&gt;
#include &lt;algorithm&gt;
#include &lt;random&gt;
#include &lt;ctime&gt;
#include  &lt;glm/glm.hpp&gt;
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&lt;unsigned int&gt;&amp; 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&lt;unsigned int&gt; generatePermutations(std::mt19937&amp; randomEngine)
{
std::vector&lt;unsigned int&gt; 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&lt;unsigned char&gt; noiseImage{};
noiseImage.reserve(size.x * size.y);
std::mt19937 randomEngine(time(nullptr));
std::vector&lt;unsigned int&gt; permutations = generatePermutations(randomEngine);
for (int y = 0; y &lt; size.y; y++)
{
for (int x = 0; x &lt; size.x; x++)
{
noiseImage.push_back(std::round(255 * perlinNoise(permutations, { x / (size.x / frequency), y / (size.y / frequency) })));
}
}
lodepng::encode(&quot;noise.png&quot;, noiseImage, size.x, size.y, LodePNGColorType::LCT_GREY);
}

huangapple
  • 本文由 发表于 2023年6月29日 21:19:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76581462.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定