c++ - 是什么导致我的 2D Perlin 噪声中出现这些伪影?
问题描述
我实现了改进的 Perlin 噪声算法。为 3D 噪声提供的代码可以正常工作。
我调整了算法,以看似显而易见的方式制作了 2D 版本。它几乎可以工作,但会产生如下图所示的伪影。
这是正确的 3D 版本:
unsigned inc (unsigned number)
{
return (number + 1) & 255;
}
double fade (double t)
{
// Fade function as defined by Ken Perlin.
// This eases coordinate values
// so that they will "ease" towards integral values.
// This ends up smoothing the final output.
// 6t^5 - 15t^4 + 10t^3
return t * t * t * (t * (t * 6 - 15) + 10);
}
double lerp (double a, double b, double x)
{
return a + x * (b - a);
}
double grad (unsigned hash, double x, double y, double z)
{
// Take the hashed value and take the first 4 bits of it
// (15 == 0b1111)
unsigned h = hash & 15;
// If the most significant bit (MSB) of the hash is 0
// then set u = x. Otherwise y.
double u = h < 8 /* 0b1000 */ ? x : y;
double v;
if (h < 4 /* 0b0100 */)
// If the first and second significant bits
// are 0, set v = y
v = y;
else if (h == 12 /* 0b1100 */ || h == 14 /* 0b1110*/)
// If the first and second significant bits
// are 1, set v = x
v = x;
else
// If the first and second significant bits are not
// equal (0/1, 1/0) set v = z
v = z;
// Use the last 2 bits to decide if u and v are positive
// or negative. Then return their addition.
return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
}
double
ImprovedNoise :: noise (double x, double y, double z)
{
// Calculate the "unit cube" that the point asked will be located in
// The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
// plus 1. Next we calculate the location (from 0.0 to 1.0) in that
// cube. We also fade the location to smooth the result.
int xi = (int)x & 255;
int yi = (int)y & 255;
int zi = (int)z & 255;
double xf = x - (int) x;
double yf = y - (int) y;
double zf = z - (int) z;
double u = fade (xf);
double v = fade (yf);
double w = fade (zf);
int aaa, aba, aab, abb, baa, bba, bab, bbb;
auto & p = permutation;
aaa = p[p[p[ xi ] + yi ] + zi ];
aba = p[p[p[ xi ] + inc(yi)] + zi ];
aab = p[p[p[ xi ] + yi ] + inc(zi)];
abb = p[p[p[ xi ] + inc(yi)] + inc(zi)];
baa = p[p[p[inc(xi)] + yi ] + zi ];
bba = p[p[p[inc(xi)] + inc(yi)] + zi ];
bab = p[p[p[inc(xi)] + yi ] + inc(zi)];
bbb = p[p[p[inc(xi)] + inc(yi)] + inc(zi)];
double x1, x2, y1, y2;
// The gradient function calculates the dot product between a
// pseudorandom gradient vector and the vector from the input
// coordinate to the 8 surrounding points in its unit cube.
// This is all then lerped together as a sort of weighted average
// based on the faded (u,v,w) values we made earlier.
x1 = lerp (
grad (aaa, xf , yf , zf),
grad (baa, xf-1, yf , zf),
u);
x2 = lerp (
grad (aba, xf , yf-1, zf),
grad (bba, xf-1, yf-1, zf),
u);
y1 = lerp (x1, x2, v);
x1 = lerp (
grad (aab, xf , yf , zf-1),
grad (bab, xf-1, yf , zf-1),
u);
x2 = lerp (
grad (abb, xf , yf-1, zf-1),
grad (bbb, xf-1, yf-1, zf-1),
u);
y2 = lerp (x1, x2, v);
auto result = (lerp (y1, y2, w) + 1) / 2;
assert (0 <= result);
assert (result <= 1);
assert (false == std :: isnan (result));
return result;
}
我通过修复生成 2D 图像z=0
。这是 10 的频率,所以 x,y 在 [0..10] 中:
我的 2D 版本:
double grad (unsigned hash, double x, double y)
{
double u = (hash & 1) ? x : y;
double v = (hash & 2) ? x : y;
return ((hash & 4) ? u : -u) + (hash & 8) ? v : -v;
}
double
ImprovedNoise :: noise (double x, double y)
{
int xi = (int)x & 255;
int yi = (int)y & 255;
double xf = x - (int) x;
double yf = y - (int) y;
double u = fade (xf);
double v = fade (yf);
int aaa, aba,baa, bba;
auto & p = permutation;
aaa = p[p[ xi ] + yi ];
aba = p[p[ xi ] + inc(yi)];
baa = p[p[inc(xi)] + yi ];
bba = p[p[inc(xi)] + inc(yi)];
double x1, x2;
// The gradient function calculates the dot product between a
// pseudorandom gradient vector and the vector from the input
// coordinate to the 8 surrounding points in its unit cube.
// This is all then lerped together as a sort of weighted average
// based on the faded (u,v,w) values we made earlier.
x1 = lerp (
grad (aaa, xf , yf),
grad (baa, xf-1, yf),
u);
x2 = lerp (
grad (aba, xf , yf-1),
grad (bba, xf-1, yf-1),
u);
double result = (lerp (x1, x2, v) + 1) / 2;
assert (0 <= result);
assert (result <= 1);
assert (false == std :: isnan (result));
return result;
}
这是它生成的图像。
它是使用这种方法生成的:
int size=400;
int freq=10;
create_widget (size, size, [&] (int x, int y)
{
return noise (x*freq / float (size), y*freq / float (size));
});
是什么导致了这些水平线和垂直线?我认为这可能是一个整数边界问题,但这会预测freq
整个图像中的伪影,所以我想这是另外一回事。
你能看出错误是什么吗?
解决方案
可能有一个错误grad
(的优先级+
高于),这会导致特定//值的(?:
无论如何都不正确)结果的突然变化。xf
yf
hash
return ((hash & 4) ? u : -u) + (hash & 8) ? v : -v;
( )
推荐阅读
- python - 即使使用相同的数据,Keras 训练和验证指标值也不同(逻辑回归)
- android - React-native scrollView - 为什么我的图像没有出现?
- jquery - 动态更新输入值并在动态输入更改后更新另一个元素
- opengl - Opengl一定要在windows上使用winapi来绘制图形吗?
- sql-server - CSV 导入 SQL Server 表
- groovy - Spock:方法未被识别为调用
- python - zipfile 标头语言编码位在 Python2 和 Python3 之间设置不同
- c++ - 错误 C2131:表达式在 VC 2017 中未计算为常量
- intellij-idea - 如何在“查找路径”功能中排除文件掩码中的某些文件?
- python - xonsh 中循环遍历文件行的最佳方法是什么?