Perlin改进算法中是否可能拥有大于255*255的网格?

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

Is that possible to have grids bigger than 255*255 in the Perlin's improved algorithm?

问题

I have implemented the improved Perlin算法:

#include "lodepng/lodepng.h"

#include <glm/glm.hpp>

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

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[] = {
        { -1.0f, -1.0f },
        { -1.0f, 1.0f },
        { 1.0f, -1.0f },
        { 1.0f, 1.0f }
    };

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

float perlinNoiseLayer(const std::vector<unsigned char>& 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;
}

float perlinNoise(const std::vector<unsigned char>& permutations, glm::vec2 point, float frequency, int octavesCount, float persistence, float lacunarity)
{
    float value = 0.0f;
    float maxValue = 0.0f;

    float amplitude = 1.0f;

    for (int i = 0; i < octavesCount; i++)
    {
        value += amplitude * perlinNoiseLayer(permutations, point * frequency);
        maxValue += amplitude;

        amplitude *= persistence;
        frequency *= lacunarity;
    }

    return value / maxValue;
}

bool validatePerlinNoiseParams(glm::ivec2 size, float frequency, int octavesCount, float lacunarity)
{
    glm::vec2 resultVec = glm::dvec2(size - 1) * (frequency * std::pow(lacunarity, octavesCount));
    return resultVec.x < 256 && resultVec.y < 256;
}

std::vector<unsigned char> generatePermutations(std::mt19937& randomEngine)
{
    std::vector<unsigned char> 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 = 1.0f;
    int octavesCount = 1;
    float persistence = 0.65f;
    float lacunarity = 2.0f;
    std::string resultFileName = "noise.png";

    if (!validatePerlinNoiseParams(size, frequency, octavesCount, lacunarity))
    {
        std::cout << "Error: Incorrect parameters\n";

        return -1;
    }

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

    std::mt19937 randomEngine(time(nullptr));
    std::vector<unsigned char> 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, y }, frequency, octavesCount, persistence, lacunarity)));
        }
    }

    lodepng::encode(resultFileName, noiseImage, size.x, size.y, LodePNGColorType::LCT_GREY);

    std::cout << "Operation has finished successfully\n";

    return 0;
}

但似乎由于数组的大小限制为512(最大为256 + 256),它存在一些限制。因此,我已经实现了以下参数验证:

bool validatePerlinNoiseParams(glm::ivec2 size, float frequency, int octavesCount, float lacunarity)
{
    glm::vec2 resultVec = glm::dvec2(size - 1) * (frequency * std::pow(lacunarity, octavesCount));
    return resultVec.x < 256 && resultVec.y < 256;
}

这段代码运行良好,但问题是:我可以增加permutations数组的大小以避免这种严格的限制吗?

EDIT:我已经实现了以下函数,用于生成正确大小的permutations数组。算法似乎工作正常。唯一的问题是:这个解决方案中是否存在任何隐藏的错误?

std::vector<unsigned char> generatePermutations(std::mt19937& randomEngine, glm::ivec2 size, float frequency, int octavesCount, float lacunarity)
{
    const int initialSize = 256;

    std::vector<unsigned char> permutations(initialSize);
    std::iota(permutations.begin(), permutations.end(), 0);
    std::shuffle(permutations.begin(), permutations.end(), randomEngine);

    glm::ivec2 resultVec = glm::dvec2(size - 1) * (frequency * std::pow(lacunarity, octavesCount));
    int result = std::max(resultVec.x / initialSize, resultVec.y / initialSize);

    permutations.resize((result + 2) * initialSize);
    for (int i = 0; i < result + 1; i++)
    {
        std::copy_n(permutations.begin(), (i + 1) * initialSize, permutations.begin() + initialSize);
    }

    return permutations;
}
英文:

I have implemented the improved Perlin's algorithm:

#include &quot;lodepng/lodepng.h&quot;
#include &lt;glm/glm.hpp&gt;
#include &lt;vector&gt;
#include &lt;numeric&gt;
#include &lt;iostream&gt;
#include &lt;random&gt;
#include &lt;ctime&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[] = {
{ -1.0f, -1.0f },
{ -1.0f, 1.0f },
{ 1.0f, -1.0f },
{ 1.0f, 1.0f }
};
return glm::dot(point, randomVectors[seed % 4]);
}
float perlinNoiseLayer(const std::vector&lt;unsigned char&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;
}
float perlinNoise(const std::vector&lt;unsigned char&gt;&amp; permutations, glm::vec2 point, float frequency, int octavesCount, float persistence, float lacunarity)
{
float value = 0.0f;
float maxValue = 0.0f;
float amplitude = 1.0f;
for (int i = 0; i &lt; octavesCount; i++)
{
value += amplitude * perlinNoiseLayer(permutations, point * frequency);
maxValue += amplitude;
amplitude *= persistence;
frequency *= lacunarity;
}
return value / maxValue;
}
bool validatePerlinNoiseParams(glm::ivec2 size, float frequency, int octavesCount, float lacunarity)
{
glm::vec2 resultVec = glm::dvec2(size - 1) * (frequency * std::pow(lacunarity, octavesCount));
return resultVec.x &lt; 256 &amp;&amp; resultVec.y &lt; 256;
}
std::vector&lt;unsigned char&gt; generatePermutations(std::mt19937&amp; randomEngine)
{
std::vector&lt;unsigned char&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 = 1.0f;
int octavesCount = 1;
float persistence = 0.65f;
float lacunarity = 2.0f;
std::string resultFileName = &quot;noise.png&quot;;
if (!validatePerlinNoiseParams(size, frequency, octavesCount, lacunarity))
{
std::cout &lt;&lt; &quot;Error: Incorrect parameters&quot; &lt;&lt; &#39;\n&#39;;
return -1;
}
std::vector&lt;unsigned char&gt; noiseImage{};
noiseImage.reserve(size.x * size.y);
std::mt19937 randomEngine(time(nullptr));
std::vector&lt;unsigned char&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, y }, frequency, octavesCount, persistence, lacunarity)));
}
}
lodepng::encode(resultFileName, noiseImage, size.x, size.y, LodePNGColorType::LCT_GREY);
std::cout &lt;&lt; &quot;Operation has finished successfully&quot; &lt;&lt; &#39;\n&#39;;
return 0;
}

But it looks like it has got limitation because of size of the array, which is 512 (256 + 256 maximum). So I have implemented validation of parameters in this way:

bool validatePerlinNoiseParams(glm::ivec2 size, float frequency, int octavesCount, float lacunarity)
{
glm::vec2 resultVec = glm::dvec2(size - 1) * (frequency * std::pow(lacunarity, octavesCount));
return resultVec.x &lt; 256 &amp;&amp; resultVec.y &lt; 256;
}

The code works well, but the question is: can I increase the size of permutations array to avoid such a strict limitations?

EDIT: I have implemented this function, which generates permutations array of proper size. The algorithm seems to work well. The only question is: are there any hidden bugs in this solution?


std::vector&lt;unsigned char&gt; generatePermutations(std::mt19937&amp; randomEngine, glm::ivec2 size, float frequency, int octavesCount, float lacunarity)
{
const int initialSize = 256;
std::vector&lt;unsigned char&gt; permutations(initialSize);
std::iota(permutations.begin(), permutations.end(), 0);
std::shuffle(permutations.begin(), permutations.end(), randomEngine);
glm::ivec2 resultVec = glm::dvec2(size - 1) * (frequency * std::pow(lacunarity, octavesCount));
int result = std::max(resultVec.x / initialSize, resultVec.y / initialSize);
permutations.resize((result + 2) * initialSize);
for (int i = 0; i &lt; result + 1; i++)
{
std::copy_n(permutations.begin(), (i + 1) * initialSize, permutations.begin() + initialSize);
}
return permutations;
}

答案1

得分: 0

以下是翻译好的代码部分:

// 可以使用大小为256的数组并在我的实现中使用`operator%`:
#include "lodepng/lodepng.h"
#include <glm/glm.hpp>
#include <vector>
#include <numeric>
#include <iostream>
#include <random>
#include <ctime>

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[] = {
        { -1.0f, -1.0f },
        { -1.0f, 1.0f },
        { 1.0f, -1.0f },
        { 1.0f, 1.0f }
    };

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

float perlinNoiseLayer(const std::vector<unsigned char>& p, glm::vec2 point)
{
    glm::ivec2 gc = point;
    glm::vec2 pc = point - glm::vec2(gc);
    glm::vec2 dc = { fade(pc.x), fade(pc.y) };
    gc %= 256;

    return (lerp(
        lerp(
            gradient(pc, p

+ gc.y]), gradient({ pc.x - 1, pc.y }, p

+ gc.y]), dc.x ), lerp( gradient({ pc.x, pc.y - 1 }, p

+ gc.y + 1]), gradient({ pc.x - 1, pc.y - 1 }, p

+ gc.y + 1]), dc.x ), dc.y ) + 1) / 2; } float perlinNoise(const std::vector<unsigned char>& permutations, glm::vec2 point, float frequency, int octavesCount, float persistence, float lacunarity) { float value = 0.0f; float maxValue = 0.0f; float amplitude = 1.0f; for (int i = 0; i < octavesCount; i++) { value += amplitude * perlinNoiseLayer(permutations, point * frequency); maxValue += amplitude; amplitude *= persistence; frequency *= lacunarity; } return value / maxValue; } std::vector<unsigned char> generatePermutations(std::mt19937& randomEngine, glm::ivec2 size, float frequency, int octavesCount, float lacunarity) { std::vector<unsigned char> permutations(256); std::iota(permutations.begin(), permutations.end(), 0); std::shuffle(permutations.begin(), permutations.end(), randomEngine); return permutations; } int main() { glm::ivec2 size = { 512, 512 }; float frequency = 0.01f; int octavesCount = 30; float persistence = 0.65f; float lacunarity = 2.0f; std::string resultFileName = "noise.png"; std::vector<unsigned char> noiseImage{}; noiseImage.reserve(size.x * size.y); std::mt19937 randomEngine(time(nullptr)); std::vector<unsigned char> permutations = generatePermutations(randomEngine, size, frequency, octavesCount, lacunarity); 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, y }, frequency, octavesCount, persistence, lacunarity))); } } lodepng::encode(resultFileName, noiseImage, size.x, size.y, LodePNGColorType::LCT_GREY); std::cout << "Operation has finished successfully" << '\n'; return 0; }

英文:

Okay, I can just have array of 256 size and use operator% in my implementation:

#include &quot;lodepng/lodepng.h&quot;
#include &lt;glm/glm.hpp&gt;
#include &lt;vector&gt;
#include &lt;numeric&gt;
#include &lt;iostream&gt;
#include &lt;random&gt;
#include &lt;ctime&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[] = {
{ -1.0f, -1.0f },
{ -1.0f, 1.0f },
{ 1.0f, -1.0f },
{ 1.0f, 1.0f }
};
return glm::dot(point, randomVectors[seed % 4]);
}
float perlinNoiseLayer(const std::vector&lt;unsigned char&gt;&amp; p, glm::vec2 point)
{
glm::ivec2 gc = point;
glm::vec2 pс = point - glm::vec2(gc);
glm::vec2 dс = { fade(pс.x), fade(pс.y) };
gc %= 256;
return (lerp(
lerp(
gradient(pс, p

+ gc.y]), gradient({ pс.x - 1, pс.y }, p

+ gc.y]), dс.x ), lerp( gradient({ pс.x, pс.y - 1 }, p

+ gc.y + 1]), gradient({ pс.x - 1, pс.y - 1 }, p

+ gc.y + 1]), dс.x ), dс.y ) + 1) / 2; } float perlinNoise(const std::vector&lt;unsigned char&gt;&amp; permutations, glm::vec2 point, float frequency, int octavesCount, float persistence, float lacunarity) { float value = 0.0f; float maxValue = 0.0f; float amplitude = 1.0f; for (int i = 0; i &lt; octavesCount; i++) { value += amplitude * perlinNoiseLayer(permutations, point * frequency); maxValue += amplitude; amplitude *= persistence; frequency *= lacunarity; } return value / maxValue; } std::vector&lt;unsigned char&gt; generatePermutations(std::mt19937&amp; randomEngine, glm::ivec2 size, float frequency, int octavesCount, float lacunarity) { std::vector&lt;unsigned char&gt; permutations(256); std::iota(permutations.begin(), permutations.end(), 0); std::shuffle(permutations.begin(), permutations.end(), randomEngine); return permutations; } int main() { glm::ivec2 size = { 512, 512 }; float frequency = 0.01f; int octavesCount = 30; float persistence = 0.65f; float lacunarity = 2.0f; std::string resultFileName = &quot;noise.png&quot;; std::vector&lt;unsigned char&gt; noiseImage{}; noiseImage.reserve(size.x * size.y); std::mt19937 randomEngine(time(nullptr)); std::vector&lt;unsigned char&gt; permutations = generatePermutations(randomEngine, size, frequency, octavesCount, lacunarity); 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, y }, frequency, octavesCount, persistence, lacunarity))); } } lodepng::encode(resultFileName, noiseImage, size.x, size.y, LodePNGColorType::LCT_GREY); std::cout &lt;&lt; &quot;Operation has finished successfully&quot; &lt;&lt; &#39;\n&#39;; return 0; }

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

发表评论

匿名网友

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

确定