我已经实现了改进的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
数组的大小来避免这样的严格限制吗?
编辑:我已经实现了这个函数,它生成了适当大小的permutations
数组。算法似乎运行良好。唯一的问题是这个解决方案中有没有隐藏的bug?
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;
}
1条答案
按热度按时间mgdq6dx11#
好的,我可以只使用256大小的数组,并在我的实现中使用
operator%
: