Here I will write some interesting facts about perlin noise. Yes, incredibly enough, you will find here some information that you cannot find anywhere else,
not that much poeple speaks about the derivatives of the perlin noise, like how to calculate them analytically and what to do with them. So, why not
to do it here? Let's see.
The derivativesLet's call our 3d Perlin Noise n(x,y,z). Everything it's the same for any amount of dimensions of course. We adopt the usual notation for derivatives
The (3d) noise function it's based on a (tri)linear interpolation of random values at some given latice points. Something like this:
n = lerp(w, lerp(v, lerp(u, a, b) ,lerp(u, c, d)),
lerp(v, lerp(u, e, f), lerp(u, g, h)));
where u(x), v(y), w(z) are normally a cubic or quintic polinomial of the form
n(x,y,z) can be expanded as follows
The derivatives can now be computed easily, for example, for the x dimension:
Therefpre it's very easy to make a function than returns the noise value and the three derivatives in one go, making it extremelly cheap compared to the central difference method, that is 5 times slower.
void dnoise3f( float *vout, const float x, const float y, const float z )
{
int i, j, k;
float u, v, w;
iGetIntegerAndFractional( x, &i, &u );
iGetIntegerAndFractional( y, &j, &v );
iGetIntegerAndFractional( z, &k, &w );
const float du = 30.0f*u*u*(u*(u-2.0f)+1.0f);
const float dv = 30.0f*v*v*(v*(v-2.0f)+1.0f);
const float dw = 30.0f*w*w*(w*(w-2.0f)+1.0f);
u = u*u*u*(u*(u*6.0f-15.0f)+10.0f);
v = v*v*v*(v*(v*6.0f-15.0f)+10.0f);
w = w*w*w*(w*(w*6.0f-15.0f)+10.0f);
const float a = myRandomMagic( i+0, j+0, k+0 );
const float b = myRandomMagic( i+1, j+0, k+0 );
const float c = myRandomMagic( i+0, j+1, k+0 );
const float d = myRandomMagic( i+1, j+1, k+0 );
const float e = myRandomMagic( i+0, j+0, k+1 );
const float f = myRandomMagic( i+1, j+0, k+1 );
const float g = myRandomMagic( i+0, j+1, k+1 );
const float h = myRandomMagic( i+1, j+1, k+1 );
const float k0 = a;
const float k1 = b - a;
const float k2 = c - a;
const float k3 = e - a;
const float k4 = a - b - c + d;
const float k5 = a - c - e + g;
const float k6 = a - b - e + f;
const float k7 = - a + b + c - d + e - f - g + h;
vout[0] = k0 + k1*u + k2*v + k3*w + k4*u*v + k5*v*w + k6*w*u + k7*u*v*w;
vout[1] = du * (k1 + k4*v + k6*w + k7*v*w);
vout[2] = dv * (k2 + k5*w + k4*u + k7*w*u);
vout[3] = dw * (k3 + k6*u + k5*v + k7*u*v);
}
Modified fbmThe fbm (fractional brownian motion) is normally implemented as a fractal sum of Perlin Noise functions.
Now, a simple modification is enough to this scheme injects some rich variety of shapes to the terrain, with flat areas as well as more rough areas (click to enlarge the images).
This terrain was computed with the following variation of the fractal sum:
float f = 0.0f;
float w = 0.5f;
float dx = 0.0f;
float dz = 0.0f;
for( int i=0; i < ioct ; i++ )
{
float n[3];
dnoise2f( n, x, y );
dx += n[1];
dz += n[2];
f += w * n[0] / (1.0f + dx*dx + dz*dz); // replace with "w * n[0]" for a classic fbm()
w *= 0.5f;
x *= 2.0f;
y *= 2.0f;
}
The image on the left was computed with the cubic version of u(x) while the one on the left used the quintic. Note how the left image
has some discontinuity artefacts, due to the non continuity of the second derivatives of u(x) and therefore of the fbm function.Other usages of the derivatives produce interesting shapes too:
|