Perlin Noise 算法似乎不会产生梯度噪声
Perlin Noise algorithm does not seem to produce gradient noise
我正在尝试用 C++ 实现 Perlin Noise。
首先,问题(我认为)是输出不是我所期望的。目前我只是在灰度图像中使用生成的 Perlin 噪声值,这是我得到的结果:
但是,根据我的理解,它应该看起来更像是:
也就是说,我目前产生的噪音似乎更像是"standard"不规则噪音。
这是我目前实现的柏林噪声算法:
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 Noise FAQ
Perlin Noise Pseudo Code
我很难准确指出我似乎搞砸了的地方。可能是我错误地生成了梯度向量,因为我不太确定它们应该具有哪种类型的分布。我尝试过均匀分布,但这似乎会在纹理中产生重复的图案!
同样,可能是我对影响值的平均计算不正确。从 Perlin Noise FAQ 文章中很难准确判断应该如何完成。
有人对代码可能有什么问题有任何提示吗? :)
您似乎只生成了一个八度音阶的柏林噪声。要获得如图所示的结果,您需要生成 multiple octaves 并将它们相加。在一系列的八度音阶中,每个八度音阶的网格单元尺寸应该是最后一个的两倍。
要生成多倍频程噪声,请使用类似以下内容:
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;
}
为了获得额外的随机性,您可以为每个八度音程使用不同种子的随机生成器。此外,可以改变赋予每个八度音阶的权重,以调整噪声的美学质量。如果每次迭代都不调整权重变量,那么上面的例子就是"pink noise"(每次频率加倍都带有相同的权重)。
此外,您需要使用随机数生成器,对于给定的 xGrid、yGrid 对,每次 returns 都具有相同的值。
我正在尝试用 C++ 实现 Perlin Noise。
首先,问题(我认为)是输出不是我所期望的。目前我只是在灰度图像中使用生成的 Perlin 噪声值,这是我得到的结果:
但是,根据我的理解,它应该看起来更像是:
也就是说,我目前产生的噪音似乎更像是"standard"不规则噪音。
这是我目前实现的柏林噪声算法:
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 Noise FAQ Perlin Noise Pseudo Code
我很难准确指出我似乎搞砸了的地方。可能是我错误地生成了梯度向量,因为我不太确定它们应该具有哪种类型的分布。我尝试过均匀分布,但这似乎会在纹理中产生重复的图案!
同样,可能是我对影响值的平均计算不正确。从 Perlin Noise FAQ 文章中很难准确判断应该如何完成。
有人对代码可能有什么问题有任何提示吗? :)
您似乎只生成了一个八度音阶的柏林噪声。要获得如图所示的结果,您需要生成 multiple octaves 并将它们相加。在一系列的八度音阶中,每个八度音阶的网格单元尺寸应该是最后一个的两倍。
要生成多倍频程噪声,请使用类似以下内容:
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;
}
为了获得额外的随机性,您可以为每个八度音程使用不同种子的随机生成器。此外,可以改变赋予每个八度音阶的权重,以调整噪声的美学质量。如果每次迭代都不调整权重变量,那么上面的例子就是"pink noise"(每次频率加倍都带有相同的权重)。
此外,您需要使用随机数生成器,对于给定的 xGrid、yGrid 对,每次 returns 都具有相同的值。