我实现了Scipy documentation。为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;
}
我通过固定z=0
来生成2D图像。这是10的频率,所以x,y在[0..10]中:
improved Perlin noise algorithm
我的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
个伪像,因此我想是另外一回事了。
您知道错误在哪里吗?
答案 0 :(得分:0)
grad
中可能有一个错误(+
的优先级高于?:
),这会导致特定xf
上(无论如何不正确)结果的突然变化。 / yf
/ hash
值。
return ((hash & 4) ? u : -u) + (hash & 8) ? v : -v;
( )