// perlinNoise.cpp // Class to implement coherent noise over 1, 2, or 3 dimensions. // Implementation based on the Perlin noise function. Thanks to // Ken Perlin of NYU for publishing his algorithm online. // Copyright (c) 2001, Matt Zucker // You may use this source code as you wish, but it is provided // with no warranty. Please email me at mazucker@vassar.edu if // you find it useful. #include #include #include #include "PerlinNoise.h" // TODO: change these preprocessor macros into inline functions // S curve is (3x^2 - 2x^3) because it's quick to calculate // though -cos(x * PI) * 0.5 + 0.5 would work too #define easeCurve(t)( t * t * (3.0 - 2.0 * t) ) #define linearInterp(t, a, b)( a + t * (b - a) ) #define dot2(rx, ry, q)( rx * q[0] + ry * q[1] ) #define dot3(rx, ry, rz, q) ( rx * q[0] + ry * q[1] + rz * q[2] ) #define setupValues(t, axis, g0, g1, d0, d1, pos) \ t = pos[axis] + NOISE_LARGE_PWR2; \ g0 = ((int)t) & NOISE_MOD_MASK; \ g1 = (g0 + 1) & NOISE_MOD_MASK; \ d0 = t - (int)t; \ d1 = d0 - 1.0; \ // Konstruktor PerlinNoise::PerlinNoise() { } // Destruktor PerlinNoise::~PerlinNoise() { } // initialize static variables unsigned PerlinNoise::initialized = 0; unsigned PerlinNoise::permutationTable[ NOISE_WRAP_INDEX*2 + 2 ] = { 0 }; float PerlinNoise::gradientTable1d[ NOISE_WRAP_INDEX*2 + 2 ] = { 0 }; float PerlinNoise::gradientTable2d[ NOISE_WRAP_INDEX*2 + 2 ][ 2 ] = { { 0 } }; float PerlinNoise::gradientTable3d[ NOISE_WRAP_INDEX*2 + 2 ][ 3 ] = { { 0 } }; // return a random float in [-1,1] inline float PerlinNoise::randNoiseFloat() { return ( float ) ( ( rand() % ( NOISE_WRAP_INDEX + NOISE_WRAP_INDEX ) ) - NOISE_WRAP_INDEX ) / NOISE_WRAP_INDEX; }; // convert a 2D vector into unit length void PerlinNoise::normalize2d( float vector[ 2 ] ) { float length = sqrt( ( vector[ 0 ] * vector[ 0 ] ) + ( vector[ 1 ] * vector[ 1 ] ) ); vector[ 0 ] /= length; vector[ 1 ] /= length; } // convert a 3D vector into unit length void PerlinNoise::normalize3d( float vector[ 3 ] ) { float length = sqrt( ( vector[ 0 ] * vector[ 0 ] ) + ( vector[ 1 ] * vector[ 1 ] ) + ( vector[ 2 ] * vector[ 2 ] ) ); vector[ 0 ] /= length; vector[ 1 ] /= length; vector[ 2 ] /= length; } // // Mnemonics used in the following 3 functions: // L = left(-X direction) // R = right (+X direction) // D = down (-Y direction) // U = up (+Y direction) // B = backwards(-Z direction) // F = forwards (+Z direction) // // Not that it matters to the math, but a reader might want to know. // // noise1d - create 1-dimensional coherent noise // if you want to learn about how noise works, look at this // and then look at noise2d. float PerlinNoise::Noise1d( float pos[ 1 ] ) { int gridPointL, gridPointR; float distFromL, distFromR, sX, t, u, v; if ( ! initialized ) { reseed(); } // find out neighboring grid points to pos and signed distances from pos to them. setupValues( t, 0, gridPointL, gridPointR, distFromL, distFromR, pos ); sX = easeCurve( distFromL ); // u, v, are the vectors from the grid pts. times the random gradients for the grid points // they are actually dot products, but this looks like scalar multiplication u = distFromL * gradientTable1d[ permutationTable[ gridPointL ] ]; v = distFromR * gradientTable1d[ permutationTable[ gridPointR ] ]; // return the linear interpretation between u and v (0 = u, 1 = v) at sX. return linearInterp( sX, u, v ); } // create 2d coherent noise float PerlinNoise::Noise2d( float pos[ 2 ] ) { int gridPointL, gridPointR, gridPointD, gridPointU; int indexLD, indexRD, indexLU, indexRU; float distFromL, distFromR, distFromD, distFromU; float *q, sX, sY, a, b, t, u, v; register int indexL, indexR; if ( ! initialized ) { reseed(); } // find out neighboring grid points to pos and signed distances from pos to them. setupValues( t, 0, gridPointL, gridPointR, distFromL, distFromR, pos ); setupValues( t, 1, gridPointD, gridPointU, distFromD, distFromU, pos ); // Generate some temporary indexes associated with the left and right grid values indexL = permutationTable[ gridPointL ]; indexR = permutationTable[ gridPointR ]; // Generate indexes in the permutation table for all 4 corners indexLD = permutationTable[ indexL + gridPointD ]; indexRD = permutationTable[ indexR + gridPointD ]; indexLU = permutationTable[ indexL + gridPointU ]; indexRU = permutationTable[ indexR + gridPointU ]; // Get the s curves at the proper values sX = easeCurve( distFromL ); sY = easeCurve( distFromD ); // Do the dot products for the lower left corner and lower right corners. // Interpolate between those dot products value sX to get a. q = gradientTable2d[ indexLD ]; u = dot2( distFromL, distFromD, q ); q = gradientTable2d[ indexRD ]; v = dot2( distFromR, distFromD, q ); a = linearInterp( sX, u, v ); // Do the dot products for the upper left corner and upper right corners. // Interpolate between those dot products at value sX to get b. q = gradientTable2d[ indexLU ]; u = dot2( distFromL, distFromU, q ); q = gradientTable2d[ indexRU ]; v = dot2( distFromR, distFromU, q ); b = linearInterp( sX, u, v ); // Interpolate between a and b at value sY to get the noise return value. return linearInterp( sY, a, b ); } // you guessed it -- 3D coherent noise float PerlinNoise::Noise3d( float pos[ 3 ] ) { int gridPointL, gridPointR, gridPointD, gridPointU, gridPointB, gridPointF; int indexLD, indexLU, indexRD, indexRU; float distFromL, distFromR, distFromD, distFromU, distFromB, distFromF; float *q, sX, sY, sZ, a, b, c, d, t, u, v; register int indexL, indexR; if ( ! initialized ) { reseed(); } // find out neighboring grid points to pos and signed distances from pos to them. setupValues( t, 0, gridPointL, gridPointR, distFromL, distFromR, pos ); setupValues( t, 1, gridPointD, gridPointU, distFromD, distFromU, pos ); setupValues( t, 2, gridPointB, gridPointF, distFromB, distFromF, pos ); indexL = permutationTable[ gridPointL ]; indexR = permutationTable[ gridPointR ]; indexLD = permutationTable[ indexL + gridPointD ]; indexRD = permutationTable[ indexR + gridPointD ]; indexLU = permutationTable[ indexL + gridPointU ]; indexRU = permutationTable[ indexR + gridPointU ]; sX = easeCurve( distFromL ); sY = easeCurve( distFromD ); sZ = easeCurve( distFromB ); q = gradientTable3d[ indexLD + gridPointB ]; u = dot3( distFromL, distFromD, distFromB, q ); q = gradientTable3d[ indexRD + gridPointB ]; v = dot3( distFromR, distFromD, distFromB, q ); a = linearInterp( sX, u, v ); q = gradientTable3d[ indexLU + gridPointB ]; u = dot3( distFromL, distFromU, distFromB, q ); q = gradientTable3d[ indexRU + gridPointB ]; v = dot3( distFromR, distFromU, distFromB, q ); b = linearInterp( sX, u, v ); c = linearInterp( sY, a, b ); q = gradientTable3d[ indexLD + gridPointF ]; u = dot3( distFromL, distFromD, distFromF, q ); q = gradientTable3d[ indexRD + gridPointF ]; v = dot3( distFromR, distFromD, distFromF, q ); a = linearInterp( sX, u, v ); q = gradientTable3d[ indexLU + gridPointF ]; u = dot3( distFromL, distFromU, distFromF, q ); q = gradientTable3d[ indexRU + gridPointF ]; v = dot3( distFromR, distFromU, distFromF, q ); b = linearInterp( sX, u, v ); d = linearInterp( sY, a, b ); return linearInterp( sZ, c, d ); } // you can call noise component-wise, too. float PerlinNoise::Noise( float x ) { return Noise1d( &x ); } float PerlinNoise::Noise( float x, float y ) { float p[ 2 ] = { x, y }; return Noise2d( p ); } float PerlinNoise::Noise( float x, float y, float z ) { float p[ 3 ] = { x, y, z }; return Noise3d( p ); } // reinitialize with new, random values. void PerlinNoise::reseed() { srand( ( unsigned int ) ( time( NULL ) + rand() ) ); generateLookupTables(); } // reinitialize using a user-specified random seed. void PerlinNoise::reseed( unsigned int rSeed ) { srand( rSeed ); generateLookupTables(); } // initialize everything during constructor or reseed -- note // that space was already allocated for the gradientTable // during the constructor void PerlinNoise::generateLookupTables() { unsigned i, j, temp; for ( i = 0; i < NOISE_WRAP_INDEX; i++ ) { // put index into permutationTable[index], we will shuffle later permutationTable[ i ] = i; gradientTable1d[ i ] = randNoiseFloat(); for ( j = 0; j < 2; j++ ) { gradientTable2d[ i ][ j ] = randNoiseFloat(); } normalize2d( gradientTable2d[ i ] ); for ( j = 0; j < 3; j++ ) { gradientTable3d[ i ][ j ] = randNoiseFloat(); } normalize3d( gradientTable3d[ i ] ); } // Shuffle permutation table up to NOISE_WRAP_INDEX for ( i = 0; i < NOISE_WRAP_INDEX; i++ ) { j = rand() & NOISE_MOD_MASK; temp = permutationTable[ i ]; permutationTable[ i ] = permutationTable[ j ]; permutationTable[ j ] = temp; } // Add the rest of the table entries in, duplicating // indices and entries so that they can effectively be indexed // by unsigned chars. I think. Ask Perlin what this is really doing. // // This is the only part of the algorithm that I don't understand 100%. for ( i = 0; i < NOISE_WRAP_INDEX + 2; i++ ) { permutationTable[ NOISE_WRAP_INDEX + i ] = permutationTable[ i ]; gradientTable1d[ NOISE_WRAP_INDEX + i ] = gradientTable1d[ i ]; for ( j = 0; j < 2; j++ ) { gradientTable2d[ NOISE_WRAP_INDEX + i ][ j ] = gradientTable2d[ i ][ j ]; } for ( j = 0; j < 3; j++ ) { gradientTable3d[ NOISE_WRAP_INDEX + i ][ j ] = gradientTable3d[ i ][ j ]; } } // And we're done. Set initialized to true initialized = 1; } // ---------------------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------- /*float PerlinNoise::Turbulence( Vec3f &p, int oct, bool hard ) { Vec3f tp = p; float f_tp[ 3 ] = { tp.x, tp.y, tp.z }; float val, amp = 1.0, sum = 0.0; for ( int i = 0;i < oct;i++, amp *= 0.5, tp *= 2.0 ) { val = Noise3d( f_tp ); if ( hard ) val = fabs( val ); sum += amp * val; } return 0.5 + 0.5*( sum * ( ( float ) ( 1 << oct ) / ( float ) ( ( 1 << ( oct + 1 ) ) - 1 ) ) ); }*/