我正在研究一些相关的相干噪声实现(我知道有库,但这主要是为了自己的教育和好奇心),以及如何使用它,但我对原始的Perlin噪声算法有一个问题。
根据这个常常被引用的数学FAQ,输出范围将在-1
和1
之间,但是我不明白值是如何达到这个范围的。
据我所知,算法基本上是这样的:每个网格点都有一个关联的长度为1
的随机梯度向量。然后,对于每个点,对于所有四周的网格点,计算随机梯度和从该网格点出发的向量的点积。然后使用花式缓动曲线和线性插值将其降至一个值。
但是,这里有我的问题:这些点积偶尔会超出[-1, 1]
的范围,由于最终值最终在点积之间进行线性插值,那么这是否意味着最终值偶尔会超出[-1, 1]
的范围?
比如说,其中一个随机向量是(sqrt(2)/2, sqrt(2)/2)
(长度为1),(0.8, 0.8)
(在单位正方形内),你得到的结果大约是1.131
。如果该值用于线性插值,生成的值完全有可能大于1
。事实上,使用我的直接实现,这种情况经常发生。
我这里缺少了什么吗?
供参考,这是我的Java代码。 Vec
是一个简单的类,用于进行简单的2D矢量运算,fade()
是缓动曲线,lerp()
是线性插值,gradient(x, y)
给出了该网格点的梯度作为Vec
。 gridSize
变量以像素为单位给出了网格的大小(它具有double类型):
public double getPoint(int x, int y) {
Vec p = new Vec(x / gridSize, y / gridSize);
Vec d = new Vec(Math.floor(p.x), Math.floor(p.y));
int x0 = (int)d.x,
y0 = (int)d.x;
double d00 = gradient(x0 , y0 ).dot(p.sub(x0 , y0 )),
d01 = gradient(x0 , y0 + 1).dot(p.sub(x0 , y0 + 1)),
d10 = gradient(x0 + 1, y0 ).dot(p.sub(x0 + 1, y0 )),
d11 = gradient(x0 + 1, y0 + 1).dot(p.sub(x0 + 1, y0 + 1));
double fadeX = fade(p.x - d.x),
fadeY = fade(p.y - d.y);
double i1 = lerp(fadeX, d00, d10),
i2 = lerp(fadeX, d01, d11);
return lerp(fadeY, i1, i2);
}
编辑:以下是生成随机渐变的代码:
double theta = gen.nextDouble() * 2 * Math.PI;
gradients[i] = new Vec(Math.cos(theta), Math.sin(theta));
其中gen
是一个java.util.Random
对象。