website articles
advanced perlin noise


Intro


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.




Noise derivatives are used to slightly modify the traditional fbm construction
in a very simple way. Note the much nicer variety obtained compared to a
regular fbm.

Analytical derivatives computation is much faster and more accurate than the
central differences method, and deppending on the fractal sum function (ridged
noise, turbulence, etc), analytical normals can be computed for the complete
heighmap.

The image is rendered by directly raymarching the procedural function (no
normal maps, no materials), only diffuse lighting and fog are used.



The derivatives




Let'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

orin short.


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

or

n(x,y,z) can be expanded as follows

with


The derivatives can now be computed easily, for example, for the x dimension:

with

ordepending the chosen cubic or quintic u(x) function.


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 fbm




The fbm (fractional brownian motion) is normally implemented as a fractal sum of Perlin Noise functions.
w=1/2 and s=2 or something close, normally.
(when s=2 each iteration is called a "octave" - for the doubling of the frequency, like in music). The total derivative is in that case the weigthed sum of the derivatives for each octave of course, as in regular derivative rules. If you implement a ridged Perlin Noise or other variations you can also easily drive the right way to combine the derivatives, unless you have a discontinuous shaping function like a fabsf().

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).


Rendered with the cubic u(x). Note the continuity artefacts.

Rendered with the quintic u(x)


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: