c++ - Perlin Noise 算法似乎不会产生梯度噪声

标签 c++ algorithm random noise perlin-noise

我正在尝试用 C++ 实现 Perlin Noise。

首先,问题(我认为)是输出不是我所期望的。目前我只是在灰度图像中使用生成的 Perlin 噪声值,这是我得到的结果: Perlin Noise Algorithm Output

但是,根据我的理解,它应该看起来更像是: Expected Perlin Noise Output

也就是说,我目前产生的噪音似乎更符合“标准”不规则噪音。

这是我目前已经实现的 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 函数负责为当前网格单元的角点之一生成随机梯度 vector 和距离 vector ,并返回它们的点积。它被实现为:

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 Noise FAQ Perlin Noise Pseudo Code

我很难准确指出我似乎搞砸了的地方。可能是我错误地生成了梯度 vector ,因为我不太确定它们应该具有哪种类型的分布。我尝试过均匀分布,但这似乎会在纹理中产生重复的图案!

同样,可能是我错误地平均了影响值。从 Perlin Noise FAQ 文章中很难准确地辨别应该如何完成。

有人对代码可能有什么问题有任何提示吗? :)

最佳答案

您似乎只生成了一个 Octave 的柏林噪声。要获得如图所示的结果,您需要生成 multiple octaves并将它们加在一起。在一系列 Octave 音程中,每个 Octave 音程的网格单元大小应是最后一个 Octave 音程的两倍。

要生成多倍频程噪声,请使用与此类似的东西:

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;
}

为了获得额外的随机性,您可以为每个 Octave 音程使用不同种子的随机生成器。此外,可以改变赋予每个 Octave 音阶的权重,以调整噪声的美学质量。如果每次迭代都不调整权重变量,那么上面的例子就是"pink noise" (每次频率加倍都具有相同的权重)。

此外,您需要使用随机数生成器,每次为给定的 xGrid、yGrid 对返回相同的值。

关于c++ - Perlin Noise 算法似乎不会产生梯度噪声,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37508938/

相关文章:

python - 如何从列表中随机选择一个变量,然后在 python 中修改它?

algorithm - 在二叉树上实现随机过程

c++ - 如何将 boost::shared_ptr<T> 后面的完整对象深度复制到指向新位置的 shared_ptr?

C++:查找可分为两组的数组项的所有组合

c - C中的BFS算法

algorithm - 检查 A 是否是二叉树 B 的一部分

python - 从 [A-z] 生成飞行字符串

c++ - 你如何正确地将 "snap"设为一个值?

c++ - 在 C++ 中将父窗体的引用传递给子 UserControl

java - 查找数组中的多数数