I'm investigating a few of the various implementations for coherent noise (I know there are libraries, but this is mostly for my own edification and curiosity) and how you can use it, and there's one problem I have with the original Perlin noise thing.
According to this frequently linked Math FAQ, the output range will be between -1
and 1
, but I don't understand how the value gets to be in that range.
As I understand it, the algorithm is basically this: each grid point has an associated random gradient vector of length 1
. Then, for each point, for all four surrounding grid points, you calculate the dot product of the random gradient and the vector going from that grid-point. Then you use a fancy ease curve and linear interpolation to get that down to one value.
But, here's my problem: these dot-products are, occasionally, going to be outside the range [-1, 1]
, and since you do the linear interpolation ultimately between the dot products, doesn't that mean that the final value will, on occasion, be outside the range of [-1, 1]
?
Say, for instance, that one of the random vectors is (sqrt(2)/2, sqrt(2)/2)
(which has a length of 1) and (0.8, 0.8)
(which is in the unit square), you get a result of roughly 1.131
. If that value is used in the linear interpolation, it's entirely possible that the value generated will be greater than 1
. And, indeed, with my straight-forward implementation, that happens quite frequently.
Am I missing something here?
For reference, here's my code in Java. Vec
is a simple class to do simple 2d vector arithmetic, fade()
is the ease curve, lerp()
is linear interpolation, and gradient(x, y)
gives you the gradient for that grid-point as a Vec
. The gridSize
variable gives you the size of the grid in pixels (it has type 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);
}
Edit: here's the code for generating the random gradients:
double theta = gen.nextDouble() * 2 * Math.PI;
gradients[i] = new Vec(Math.cos(theta), Math.sin(theta));
Where gen
is a java.util.Random
.
See Question&Answers more detail:
os