Perlin噪声算法似乎无法产生梯度噪声。

4
我正在尝试在c++中实现Perlin噪声。
首先,问题在于输出结果并不是我所期望的。目前,我只是将生成的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 函数的作用是为当前网格单元的一个角点生成随机梯度向量和距离向量,并返回这两个向量的点积。其实现方式如下:

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噪声 FAQPerlin噪声伪代码
很难准确指出我的问题在哪里。可能是我生成的梯度向量不正确,因为我不确定它们应该具有什么样的分布。我尝试使用均匀分布,但似乎会在纹理中生成重复的模式!同样,我对影响值进行平均可能是不正确的。从Perlin噪声FAQ文章中很难确定应该如何做。请问有人能给我关于代码哪里出了问题的提示吗? :)
1个回答

9

看起来你只生成了一组柏林噪声。要得到像展示的那样的结果,你需要生成多组柏林噪声并将它们相加。在一系列的柏林噪声中,每个柏林噪声的网格单元大小都应该是上一个的两倍。

为了生成多组柏林噪声,可以使用类似以下的方法:

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

为了增加随机性,你可以为每个八度使用不同的种子随机生成器。此外,可以变化每个八度的权重以调整噪声的美学质量。如果未调整权重变量每次迭代,则上面的示例是"粉色噪声"(每个频率加倍具有相同的权重)。同时,你需要使用一个随机数生成器,对于给定的xGrid和yGrid对返回相同的值。

啊哈!似乎不使用八度音阶是问题所在 :) 现在我的结果好多了! - CodingBeagle
难以置信,我最近也遇到了这个问题! - Dave3of5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接