//----------------------------------------------------------------------------------------------- float Compute2dFractalNoise( float posX, float posY, float scale, unsigned int numOctaves, float octavePersistence, float octaveScale, bool renormalize, unsigned int seed ) { const float OCTAVE_OFFSET = 0.636764989593174f; // Translation/bias to add to each octave float totalNoise = 0.f; float totalAmplitude = 0.f; float currentAmplitude = 1.f; float invScale = (1.f / scale); Vector2 currentPos( posX * invScale, posY * invScale ); for( unsigned int octaveNum = 0; octaveNum < numOctaves; ++ octaveNum ) { // Determine noise values at nearby integer "grid point" positions Vector2 cellMins( FastFloor( currentPos.x ), FastFloor( currentPos.y ) ); int indexWestX = (int) cellMins.x; int indexSouthY = (int) cellMins.y; int indexEastX = indexWestX + 1; int indexNorthY = indexSouthY + 1; float valueSouthWest = Get2dNoiseZeroToOne( indexWestX, indexSouthY, seed ); float valueSouthEast = Get2dNoiseZeroToOne( indexEastX, indexSouthY, seed ); float valueNorthWest = Get2dNoiseZeroToOne( indexWestX, indexNorthY, seed ); float valueNorthEast = Get2dNoiseZeroToOne( indexEastX, indexNorthY, seed ); // Do a smoothed (nonlinear) weighted average of nearby grid point values Vector2 displacementFromMins = currentPos - cellMins; float weightEast = SmoothStep( displacementFromMins.x ); float weightNorth = SmoothStep( displacementFromMins.y ); float weightWest = 1.f - weightEast; float weightSouth = 1.f - weightNorth; float blendSouth = (weightEast * valueSouthEast) + (weightWest * valueSouthWest); float blendNorth = (weightEast * valueNorthEast) + (weightWest * valueNorthWest); float blendTotal = (weightSouth * blendSouth) + (weightNorth * blendNorth); float noiseThisOctave = 2.f * (blendTotal - 0.5f); // Map from [0,1] to [-1,1] // Accumulate results and prepare for next octave (if any) totalNoise += noiseThisOctave * currentAmplitude; totalAmplitude += currentAmplitude; currentAmplitude *= octavePersistence; currentPos *= octaveScale; currentPos.x += OCTAVE_OFFSET; // Add "irrational" offsets to noise position components currentPos.y += OCTAVE_OFFSET; // at each octave to break up their grid alignment ++ seed; // Eliminates octaves "echoing" each other (since each octave is uniquely seeded) } // Re-normalize total noise to within [-1,1] and fix octaves pulling us far away from limits if( renormalize && totalAmplitude > 0.f ) { totalNoise /= totalAmplitude; // Amplitude exceeds 1.0 if octaves are used totalNoise = (totalNoise * 0.5f) + 0.5f; // Map to [0,1] totalNoise = SmoothStep( totalNoise ); // Push towards extents (octaves pull us away) totalNoise = (totalNoise * 2.0f) - 1.f; // Map back to [-1,1] } return totalNoise; }
//----------------------------------------------------------------------------------------------- // Perlin noise is fractal noise with "gradient vector smoothing" applied. // // In 1D, the gradients are trivial: -1.0 or 1.0, so resulting noise is boring at one octave. // float Compute1dPerlinNoise( float position, float scale, unsigned int numOctaves, float octavePersistence, float octaveScale, bool renormalize, unsigned int seed ) { const float OCTAVE_OFFSET = 0.636764989593174f; // Translation/bias to add to each octave const float gradients[2] = { -1.f, 1.f }; // 1D unit "gradient" vectors; one back, one forward float totalNoise = 0.f; float totalAmplitude = 0.f; float currentAmplitude = 1.f; float currentPosition = position * (1.f / scale); for( unsigned int octaveNum = 0; octaveNum < numOctaves; ++ octaveNum ) { // Determine random "gradient vectors" (just +1 or -1 for 1D Perlin) for surrounding corners float positionFloor = (float) FastFloor( currentPosition ); int indexWest = (int) positionFloor; int indexEast = indexWest + 1; float gradientWest = gradients[ Get1dNoiseUint( indexWest, seed ) & 0x00000001 ]; float gradientEast = gradients[ Get1dNoiseUint( indexEast, seed ) & 0x00000001 ]; // Dot each point's gradient with displacement from point to position float displacementFromWest = currentPosition - positionFloor; // always positive float displacementFromEast = displacementFromWest - 1.f; // always negative float dotWest = gradientWest * displacementFromWest; // 1D "dot product" is... multiply float dotEast = gradientEast * displacementFromEast; // Do a smoothed (nonlinear) weighted average of dot results float weightEast = SmoothStep( displacementFromWest ); float weightWest = 1.f - weightEast; float blendTotal = (weightWest * dotWest) + (weightEast * dotEast); float noiseThisOctave = 2.f * blendTotal; // 1D Perlin is in [-.5,.5]; map to [-1,1] // Accumulate results and prepare for next octave (if any) totalNoise += noiseThisOctave * currentAmplitude; totalAmplitude += currentAmplitude; currentAmplitude *= octavePersistence; currentPosition *= octaveScale; currentPosition += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids ++ seed; // Eliminates octaves "echoing" each other (since each octave is uniquely seeded) } // Re-normalize total noise to within [-1,1] and fix octaves pulling us far away from limits if( renormalize && totalAmplitude > 0.f ) { totalNoise /= totalAmplitude; // Amplitude exceeds 1.0 if octaves are used totalNoise = (totalNoise * 0.5f) + 0.5f; // Map to [0,1] totalNoise = SmoothStep( totalNoise ); // Push towards extents (octaves pull us away) totalNoise = (totalNoise * 2.0f) - 1.f; // Map back to [-1,1] } return totalNoise; }
//----------------------------------------------------------------------------------------------- float Compute1dFractalNoise( float position, float scale, unsigned int numOctaves, float octavePersistence, float octaveScale, bool renormalize, unsigned int seed ) { const float OCTAVE_OFFSET = 0.636764989593174f; // Translation/bias to add to each octave float totalNoise = 0.f; float totalAmplitude = 0.f; float currentAmplitude = 1.f; float currentPosition = position * (1.f / scale); for( unsigned int octaveNum = 0; octaveNum < numOctaves; ++ octaveNum ) { // Determine noise values at nearby integer "grid point" positions float positionFloor = FastFloor( currentPosition ); int indexWest = (int) positionFloor; int indexEast = indexWest + 1; float valueWest = Get1dNoiseZeroToOne( indexWest, seed ); float valueEast = Get1dNoiseZeroToOne( indexEast, seed ); // Do a smoothed (nonlinear) weighted average of nearby grid point values float distanceFromWest = currentPosition - positionFloor; float weightEast = SmoothStep( distanceFromWest ); // Gives rounder (nonlinear) results float weightWest = 1.f - weightEast; float noiseZeroToOne = (valueWest * weightWest) + (valueEast * weightEast); float noiseThisOctave = 2.f * (noiseZeroToOne - 0.5f); // Map from [0,1] to [-1,1] // Accumulate results and prepare for next octave (if any) totalNoise += noiseThisOctave * currentAmplitude; totalAmplitude += currentAmplitude; currentAmplitude *= octavePersistence; currentPosition *= octaveScale; currentPosition += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids ++ seed; // Eliminates octaves "echoing" each other (since each octave is uniquely seeded) } // Re-normalize total noise to within [-1,1] and fix octaves pulling us far away from limits if( renormalize && totalAmplitude > 0.f ) { totalNoise /= totalAmplitude; // Amplitude exceeds 1.0 if octaves are used! totalNoise = (totalNoise * 0.5f) + 0.5f; // Map to [0,1] totalNoise = SmoothStep( totalNoise ); // Push towards extents (octaves pull us away) totalNoise = (totalNoise * 2.0f) - 1.f; // Map back to [-1,1] } return totalNoise; }
/// <summary> /// 2D simplex noise /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> public static float Generate(float x, float y) { const float F2 = 0.366025403f; // F2 = 0.5*(sqrt(3.0)-1.0) const float G2 = 0.211324865f; // G2 = (3.0-Math.sqrt(3.0))/6.0 float n0, n1, n2; // Noise contributions from the three corners // Skew the input space to determine which simplex cell we're in float s = (x+y)*F2; // Hairy factor for 2D float xs = x + s; float ys = y + s; int i = FastFloor(xs); int j = FastFloor(ys); float t = (float)(i+j)*G2; float X0 = i-t; // Unskew the cell origin back to (x,y) space float Y0 = j-t; float x0 = x-X0; // The x,y distances from the cell origin float y0 = y-Y0; // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where // c = (3-sqrt(3))/6 float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords float y1 = y0 - j1 + G2; float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords float y2 = y0 - 1.0f + 2.0f * G2; // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds int ii = i % 256; int jj = j % 256; // Calculate the contribution from the three corners float t0 = 0.5f - x0*x0-y0*y0; if(t0 < 0.0f) n0 = 0.0f; else { t0 *= t0; n0 = t0 * t0 * grad(perm[ii+perm[jj]], x0, y0); } float t1 = 0.5f - x1*x1-y1*y1; if(t1 < 0.0f) n1 = 0.0f; else { t1 *= t1; n1 = t1 * t1 * grad(perm[ii+i1+perm[jj+j1]], x1, y1); } float t2 = 0.5f - x2*x2-y2*y2; if(t2 < 0.0f) n2 = 0.0f; else { t2 *= t2; n2 = t2 * t2 * grad(perm[ii+1+perm[jj+1]], x2, y2); } // Add contributions from each corner to get the final noise value. // The result is scaled to return values in the interval [-1,1]. return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary! }
float SimplexNoise::Raw_Sample_2D(float x, float y) { // Noise contributions from the three corners float n0, n1, n2; // Skew the input space to determine which simplex cell we're in float F2 = 0.5f * (sqrtf(3.0f) - 1.0f); // Hairy factor for 2D float s = (x + y) * F2; int i = FastFloor( x + s ); int j = FastFloor( y + s ); float G2 = (3.0f - sqrtf(3.0f)) / 6.0f; float t = (i + j) * G2; // Unskew the cell origin back to (x,y) space float X0 = i-t; float Y0 = j-t; // The x,y distances from the cell origin float x0 = x-X0; float y0 = y-Y0; // For the 2D case, the simplex shape is an equilateral triangle. // Determine which simplex we are in. int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords // lower triangle, XY order: (0,0)->(1,0)->(1,1) if (x0 > y0) { i1=1; j1=0; } // upper triangle, YX order: (0,0)->(0,1)->(1,1) else { i1=0; j1=1; } // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where // c = (3-sqrt(3))/6 float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords float y1 = y0 - j1 + G2; float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords float y2 = y0 - 1.0f + 2.0f * G2; // Work out the hashed gradient indices of the three simplex corners int ii = i & 255; int jj = j & 255; int gi0 = m_permutations[ii+m_permutations[jj]] % 12; int gi1 = m_permutations[ii+i1+m_permutations[jj+j1]] % 12; int gi2 = m_permutations[ii+1+m_permutations[jj+1]] % 12; // Calculate the contribution from the three corners float t0 = 0.5f - x0*x0-y0*y0; if (t0 < 0) { n0 = 0.0f; } else { t0 *= t0; n0 = t0 * t0 * Dot(m_gradiant3D[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient } float t1 = 0.5f - x1*x1-y1*y1; if (t1 < 0) { n1 = 0.0f; } else { t1 *= t1; n1 = t1 * t1 * Dot(m_gradiant3D[gi1], x1, y1); } float t2 = 0.5f - x2*x2-y2*y2; if (t2 < 0) { n2 = 0.0f; } else { t2 *= t2; n2 = t2 * t2 * Dot(m_gradiant3D[gi2], x2, y2); } // Add contributions from each corner to get the final noise value. // The result is scaled to return values in the interval [-1,1]. return 70.0f * (n0 + n1 + n2); }
float SimplexNoise::Raw_Sample_4D(float x, float y, float z, float w) { // The skewing and unskewing factors are hairy again for the 4D case float F4 = (sqrtf(5.0f)-1.0f)/4.0f; float G4 = (5.0f-sqrtf(5.0f))/20.0f; float n0, n1, n2, n3, n4; // Noise contributions from the five corners // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in float s = (x + y + z + w) * F4; // Factor for 4D skewing int i = FastFloor(x + s); int j = FastFloor(y + s); int k = FastFloor(z + s); int l = FastFloor(w + s); float t = (i + j + k + l) * G4; // Factor for 4D unskewing float X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space float Y0 = j - t; float Z0 = k - t; float W0 = l - t; float x0 = x - X0; // The x,y,z,w distances from the cell origin float y0 = y - Y0; float z0 = z - Z0; float w0 = w - W0; // For the 4D case, the simplex is a 4D shape I won't even try to describe. // To find out which of the 24 possible simplices we're in, we need to // determine the magnitude ordering of x0, y0, z0 and w0. // The method below is a good way of finding the ordering of x,y,z,w and // then find the correct traversal order for the simplex we're in. // First, six pair-wise comparisons are performed between each possible pair // of the four coordinates, and the results are used to add up binary bits // for an integer index. int c1 = (x0 > y0) ? 32 : 0; int c2 = (x0 > z0) ? 16 : 0; int c3 = (y0 > z0) ? 8 : 0; int c4 = (x0 > w0) ? 4 : 0; int c5 = (y0 > w0) ? 2 : 0; int c6 = (z0 > w0) ? 1 : 0; int c = c1 + c2 + c3 + c4 + c5 + c6; int i1, j1, k1, l1; // The integer offsets for the second simplex corner int i2, j2, k2, l2; // The integer offsets for the third simplex corner int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. // Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w // impossible. Only the 24 indices which have non-zero entries make any sense. // We use a thresholding to set the coordinates in turn from the largest magnitude. // The number 3 in the "simplex" array is at the position of the largest coordinate. i1 = m_simplex[c][0]>=3 ? 1 : 0; j1 = m_simplex[c][1]>=3 ? 1 : 0; k1 = m_simplex[c][2]>=3 ? 1 : 0; l1 = m_simplex[c][3]>=3 ? 1 : 0; // The number 2 in the "simplex" array is at the second largest coordinate. i2 = m_simplex[c][0]>=2 ? 1 : 0; j2 = m_simplex[c][1]>=2 ? 1 : 0; k2 = m_simplex[c][2]>=2 ? 1 : 0; l2 = m_simplex[c][3]>=2 ? 1 : 0; // The number 1 in the "simplex" array is at the second smallest coordinate. i3 = m_simplex[c][0]>=1 ? 1 : 0; j3 = m_simplex[c][1]>=1 ? 1 : 0; k3 = m_simplex[c][2]>=1 ? 1 : 0; l3 = m_simplex[c][3]>=1 ? 1 : 0; // The fifth corner has all coordinate offsets = 1, so no need to look that up. float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords float y1 = y0 - j1 + G4; float z1 = z0 - k1 + G4; float w1 = w0 - l1 + G4; float x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords float y2 = y0 - j2 + 2.0f*G4; float z2 = z0 - k2 + 2.0f*G4; float w2 = w0 - l2 + 2.0f*G4; float x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords float y3 = y0 - j3 + 3.0f*G4; float z3 = z0 - k3 + 3.0f*G4; float w3 = w0 - l3 + 3.0f*G4; float x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords float y4 = y0 - 1.0f + 4.0f*G4; float z4 = z0 - 1.0f + 4.0f*G4; float w4 = w0 - 1.0f + 4.0f*G4; // Work out the hashed gradient indices of the five simplex corners int ii = i & 255; int jj = j & 255; int kk = k & 255; int ll = l & 255; int gi0 = m_permutations[ii+m_permutations[jj+m_permutations[kk+m_permutations[ll]]]] % 32; int gi1 = m_permutations[ii+i1+m_permutations[jj+j1+m_permutations[kk+k1+m_permutations[ll+l1]]]] % 32; int gi2 = m_permutations[ii+i2+m_permutations[jj+j2+m_permutations[kk+k2+m_permutations[ll+l2]]]] % 32; int gi3 = m_permutations[ii+i3+m_permutations[jj+j3+m_permutations[kk+k3+m_permutations[ll+l3]]]] % 32; int gi4 = m_permutations[ii+1+m_permutations[jj+1+m_permutations[kk+1+m_permutations[ll+1]]]] % 32; // Calculate the contribution from the five corners float t0 = 0.6f - x0*x0 - y0*y0 - z0*z0 - w0*w0; if (t0 < 0) { n0 = 0.0f; } else { t0 *= t0; n0 = t0 * t0 * Dot(m_gradiant4D[gi0], x0, y0, z0, w0); } float t1 = 0.6f - x1*x1 - y1*y1 - z1*z1 - w1*w1; if(t1 < 0) { n1 = 0.0f; } else { t1 *= t1; n1 = t1 * t1 * Dot(m_gradiant4D[gi1], x1, y1, z1, w1); } float t2 = 0.6f - x2*x2 - y2*y2 - z2*z2 - w2*w2; if (t2 < 0) { n2 = 0.0f; } else { t2 *= t2; n2 = t2 * t2 * Dot(m_gradiant4D[gi2], x2, y2, z2, w2); } float t3 = 0.6f - x3*x3 - y3*y3 - z3*z3 - w3*w3; if (t3 < 0) { n3 = 0.0f; } else { t3 *= t3; n3 = t3 * t3 * Dot(m_gradiant4D[gi3], x3, y3, z3, w3); } float t4 = 0.6f - x4*x4 - y4*y4 - z4*z4 - w4*w4; if (t4 < 0) { n4 = 0.0f; } else { t4 *= t4; n4 = t4 * t4 * Dot(m_gradiant4D[gi4], x4, y4, z4, w4); } // Sum up and scale the result to cover the range [-1,1] return 27.0f * (n0 + n1 + n2 + n3 + n4); }
float SimplexNoise::Raw_Sample_3D(float x, float y, float z) { float n0, n1, n2, n3; // Noise contributions from the four corners // Skew the input space to determine which simplex cell we're in float F3 = 1.0f/3.0f; float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D int i = FastFloor(x+s); int j = FastFloor(y+s); int k = FastFloor(z+s); float G3 = 1.0f/6.0f; // Very nice and simple unskew factor, too float t = (i+j+k)*G3; float X0 = i-t; // Unskew the cell origin back to (x,y,z) space float Y0 = j-t; float Z0 = k-t; float x0 = x-X0; // The x,y,z distances from the cell origin float y0 = y-Y0; float z0 = z-Z0; // For the 3D case, the simplex shape is a slightly irregular tetrahedron. // Determine which simplex we are in. int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords if (x0 >= y0) { // X Y Z order if (y0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Z Y order else if (x0 >= z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // Z X Y order else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } } // x0<y0 else { // Z Y X order if (y0 < z0) { i1=0; j1=0; k1=1; i2=0; j2=1; k2=1; } // Y Z X order else if (x0 < z0) { i1=0; j1=1; k1=0; i2=0; j2=1; k2=1; } // Y X Z order else { i1=0; j1=1; k1=0; i2=1; j2=1; k2=0; } } // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z), // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where // c = 1/6. float x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords float y1 = y0 - j1 + G3; float z1 = z0 - k1 + G3; float x2 = x0 - i2 + 2.0f*G3; // Offsets for third corner in (x,y,z) coords float y2 = y0 - j2 + 2.0f*G3; float z2 = z0 - k2 + 2.0f*G3; float x3 = x0 - 1.0f + 3.0f*G3; // Offsets for last corner in (x,y,z) coords float y3 = y0 - 1.0f + 3.0f*G3; float z3 = z0 - 1.0f + 3.0f*G3; // Work out the hashed gradient indices of the four simplex corners int ii = i & 255; int jj = j & 255; int kk = k & 255; int gi0 = m_permutations[ii+m_permutations[jj+m_permutations[kk]]] % 12; int gi1 = m_permutations[ii+i1+m_permutations[jj+j1+m_permutations[kk+k1]]] % 12; int gi2 = m_permutations[ii+i2+m_permutations[jj+j2+m_permutations[kk+k2]]] % 12; int gi3 = m_permutations[ii+1+m_permutations[jj+1+m_permutations[kk+1]]] % 12; // Calculate the contribution from the four corners float t0 = 0.6f - x0*x0 - y0*y0 - z0*z0; if (t0 < 0) { n0 = 0.0f; } else { t0 *= t0; n0 = t0 * t0 * Dot(m_gradiant3D[gi0], x0, y0, z0); } float t1 = 0.6f - x1*x1 - y1*y1 - z1*z1; if (t1 < 0) { n1 = 0.0f; } else { t1 *= t1; n1 = t1 * t1 * Dot(m_gradiant3D[gi1], x1, y1, z1); } float t2 = 0.6f - x2*x2 - y2*y2 - z2*z2; if (t2 < 0) { n2 = 0.0f; } else { t2 *= t2; n2 = t2 * t2 * Dot(m_gradiant3D[gi2], x2, y2, z2); } float t3 = 0.6f - x3*x3 - y3*y3 - z3*z3; if (t3 < 0) { n3 = 0.0f; } else { t3 *= t3; n3 = t3 * t3 * Dot(m_gradiant3D[gi3], x3, y3, z3); } // Add contributions from each corner to get the final noise value. // The result is scaled to stay just inside [-1,1] return 32.0f*(n0 + n1 + n2 + n3); }
//----------------------------------------------------------------------------------------------- // Perlin noise is fractal noise with "gradient vector smoothing" applied. // // In 4D, gradients are unit-length hyper-vectors in random (4D) directions. // float Compute4dPerlinNoise( float posX, float posY, float posZ, float posT, float scale, unsigned int numOctaves, float octavePersistence, float octaveScale, bool renormalize, unsigned int seed ) { const float OCTAVE_OFFSET = 0.636764989593174f; // Translation/bias to add to each octave const Vector4 gradients[ 16 ] = // Hard to tell if this is any better in 4D than just having 8 { Vector4( +0.5f, +0.5f, +0.5f, +0.5f ), // Normalized unit 4D vectors pointing toward each Vector4( -0.5f, +0.5f, +0.5f, +0.5f ), // of the 16 hypercube corners, so components are Vector4( +0.5f, -0.5f, +0.5f, +0.5f ), // all sqrt(4)/4, i.e. one-half. Vector4( -0.5f, -0.5f, +0.5f, +0.5f ), // Vector4( +0.5f, +0.5f, -0.5f, +0.5f ), // It's hard to tell whether these are any better Vector4( -0.5f, +0.5f, -0.5f, +0.5f ), // or worse than vectors facing axes (1,0,0,0) or Vector4( +0.5f, -0.5f, -0.5f, +0.5f ), // 3D edges (.7,.7,0,0) or 4D edges (.57,.57,.57,0) Vector4( -0.5f, -0.5f, -0.5f, +0.5f ), // but less-axial gradients looked a little better Vector4( +0.5f, +0.5f, +0.5f, -0.5f ), // with 2D and 3D noise so I'm assuming this is as Vector4( -0.5f, +0.5f, +0.5f, -0.5f ), // good or better as any other gradient-selection Vector4( +0.5f, -0.5f, +0.5f, -0.5f ), // scheme (and is crazy-fast). *shrug* Vector4( -0.5f, -0.5f, +0.5f, -0.5f ), // Vector4( +0.5f, +0.5f, -0.5f, -0.5f ), // Plus, we want a power-of-two number of evenly- Vector4( -0.5f, +0.5f, -0.5f, -0.5f ), // distributed gradients, so we can cheaply select Vector4( +0.5f, -0.5f, -0.5f, -0.5f ), // one from bit-noise (use bit-mask, not modulus). Vector4( -0.5f, -0.5f, -0.5f, -0.5f ) // }; float totalNoise = 0.f; float totalAmplitude = 0.f; float currentAmplitude = 1.f; float invScale = (1.f / scale); Vector4 currentPos( posX * invScale, posY * invScale, posZ * invScale, posT * invScale ); for( unsigned int octaveNum = 0; octaveNum < numOctaves; ++ octaveNum ) { // Determine random unit "gradient vectors" for 16 surrounding 4D (hypercube) cell corners Vector4 cellMins( FastFloor( currentPos.x ), FastFloor( currentPos.y ), FastFloor( currentPos.z ), FastFloor( currentPos.w ) ); Vector4 cellMaxs( cellMins.x + 1.f, cellMins.y + 1.f, cellMins.z + 1.f, cellMins.w + 1.f ); int indexWestX = (int) cellMins.x; int indexSouthY = (int) cellMins.y; int indexBelowZ = (int) cellMins.z; int indexBeforeT = (int) cellMins.w; int indexEastX = indexWestX + 1; int indexNorthY = indexSouthY + 1; int indexAboveZ = indexBelowZ + 1; int indexAfterT = indexBeforeT + 1; // "BeforeBSW" stands for "BeforeBelowSouthWest" below (i.e. 4D hypercube mins), etc. unsigned int noiseBeforeBSW = Get4dNoiseUint( indexWestX, indexSouthY, indexBelowZ, indexBeforeT, seed ); unsigned int noiseBeforeBSE = Get4dNoiseUint( indexEastX, indexSouthY, indexBelowZ, indexBeforeT, seed ); unsigned int noiseBeforeBNW = Get4dNoiseUint( indexWestX, indexNorthY, indexBelowZ, indexBeforeT, seed ); unsigned int noiseBeforeBNE = Get4dNoiseUint( indexEastX, indexNorthY, indexBelowZ, indexBeforeT, seed ); unsigned int noiseBeforeASW = Get4dNoiseUint( indexWestX, indexSouthY, indexAboveZ, indexBeforeT, seed ); unsigned int noiseBeforeASE = Get4dNoiseUint( indexEastX, indexSouthY, indexAboveZ, indexBeforeT, seed ); unsigned int noiseBeforeANW = Get4dNoiseUint( indexWestX, indexNorthY, indexAboveZ, indexBeforeT, seed ); unsigned int noiseBeforeANE = Get4dNoiseUint( indexEastX, indexNorthY, indexAboveZ, indexBeforeT, seed ); unsigned int noiseAfterBSW = Get4dNoiseUint( indexWestX, indexSouthY, indexBelowZ, indexAfterT, seed ); unsigned int noiseAfterBSE = Get4dNoiseUint( indexEastX, indexSouthY, indexBelowZ, indexAfterT, seed ); unsigned int noiseAfterBNW = Get4dNoiseUint( indexWestX, indexNorthY, indexBelowZ, indexAfterT, seed ); unsigned int noiseAfterBNE = Get4dNoiseUint( indexEastX, indexNorthY, indexBelowZ, indexAfterT, seed ); unsigned int noiseAfterASW = Get4dNoiseUint( indexWestX, indexSouthY, indexAboveZ, indexAfterT, seed ); unsigned int noiseAfterASE = Get4dNoiseUint( indexEastX, indexSouthY, indexAboveZ, indexAfterT, seed ); unsigned int noiseAfterANW = Get4dNoiseUint( indexWestX, indexNorthY, indexAboveZ, indexAfterT, seed ); unsigned int noiseAfterANE = Get4dNoiseUint( indexEastX, indexNorthY, indexAboveZ, indexAfterT, seed ); Vector4 gradientBeforeBSW = gradients[ noiseBeforeBSW & 0x0000000F ]; Vector4 gradientBeforeBSE = gradients[ noiseBeforeBSE & 0x0000000F ]; Vector4 gradientBeforeBNW = gradients[ noiseBeforeBNW & 0x0000000F ]; Vector4 gradientBeforeBNE = gradients[ noiseBeforeBNE & 0x0000000F ]; Vector4 gradientBeforeASW = gradients[ noiseBeforeASW & 0x0000000F ]; Vector4 gradientBeforeASE = gradients[ noiseBeforeASE & 0x0000000F ]; Vector4 gradientBeforeANW = gradients[ noiseBeforeANW & 0x0000000F ]; Vector4 gradientBeforeANE = gradients[ noiseBeforeANE & 0x0000000F ]; Vector4 gradientAfterBSW = gradients[ noiseAfterBSW & 0x0000000F ]; Vector4 gradientAfterBSE = gradients[ noiseAfterBSE & 0x0000000F ]; Vector4 gradientAfterBNW = gradients[ noiseAfterBNW & 0x0000000F ]; Vector4 gradientAfterBNE = gradients[ noiseAfterBNE & 0x0000000F ]; Vector4 gradientAfterASW = gradients[ noiseAfterASW & 0x0000000F ]; Vector4 gradientAfterASE = gradients[ noiseAfterASE & 0x0000000F ]; Vector4 gradientAfterANW = gradients[ noiseAfterANW & 0x0000000F ]; Vector4 gradientAfterANE = gradients[ noiseAfterANE & 0x0000000F ]; // Dot each corner's gradient with displacement from corner to position Vector4 displacementFromBeforeBSW( currentPos.x - cellMins.x, currentPos.y - cellMins.y, currentPos.z - cellMins.z, currentPos.w - cellMins.w ); Vector4 displacementFromBeforeBSE( currentPos.x - cellMaxs.x, currentPos.y - cellMins.y, currentPos.z - cellMins.z, currentPos.w - cellMins.w ); Vector4 displacementFromBeforeBNW( currentPos.x - cellMins.x, currentPos.y - cellMaxs.y, currentPos.z - cellMins.z, currentPos.w - cellMins.w ); Vector4 displacementFromBeforeBNE( currentPos.x - cellMaxs.x, currentPos.y - cellMaxs.y, currentPos.z - cellMins.z, currentPos.w - cellMins.w ); Vector4 displacementFromBeforeASW( currentPos.x - cellMins.x, currentPos.y - cellMins.y, currentPos.z - cellMaxs.z, currentPos.w - cellMins.w ); Vector4 displacementFromBeforeASE( currentPos.x - cellMaxs.x, currentPos.y - cellMins.y, currentPos.z - cellMaxs.z, currentPos.w - cellMins.w ); Vector4 displacementFromBeforeANW( currentPos.x - cellMins.x, currentPos.y - cellMaxs.y, currentPos.z - cellMaxs.z, currentPos.w - cellMins.w ); Vector4 displacementFromBeforeANE( currentPos.x - cellMaxs.x, currentPos.y - cellMaxs.y, currentPos.z - cellMaxs.z, currentPos.w - cellMins.w ); Vector4 displacementFromAfterBSW( currentPos.x - cellMins.x, currentPos.y - cellMins.y, currentPos.z - cellMins.z, currentPos.w - cellMaxs.w ); Vector4 displacementFromAfterBSE( currentPos.x - cellMaxs.x, currentPos.y - cellMins.y, currentPos.z - cellMins.z, currentPos.w - cellMaxs.w ); Vector4 displacementFromAfterBNW( currentPos.x - cellMins.x, currentPos.y - cellMaxs.y, currentPos.z - cellMins.z, currentPos.w - cellMaxs.w ); Vector4 displacementFromAfterBNE( currentPos.x - cellMaxs.x, currentPos.y - cellMaxs.y, currentPos.z - cellMins.z, currentPos.w - cellMaxs.w ); Vector4 displacementFromAfterASW( currentPos.x - cellMins.x, currentPos.y - cellMins.y, currentPos.z - cellMaxs.z, currentPos.w - cellMaxs.w ); Vector4 displacementFromAfterASE( currentPos.x - cellMaxs.x, currentPos.y - cellMins.y, currentPos.z - cellMaxs.z, currentPos.w - cellMaxs.w ); Vector4 displacementFromAfterANW( currentPos.x - cellMins.x, currentPos.y - cellMaxs.y, currentPos.z - cellMaxs.z, currentPos.w - cellMaxs.w ); Vector4 displacementFromAfterANE( currentPos.x - cellMaxs.x, currentPos.y - cellMaxs.y, currentPos.z - cellMaxs.z, currentPos.w - cellMaxs.w ); float dotBeforeBSW = MathUtils::Dot( gradientBeforeBSW, displacementFromBeforeBSW ); float dotBeforeBSE = MathUtils::Dot( gradientBeforeBSE, displacementFromBeforeBSE ); float dotBeforeBNW = MathUtils::Dot( gradientBeforeBNW, displacementFromBeforeBNW ); float dotBeforeBNE = MathUtils::Dot( gradientBeforeBNE, displacementFromBeforeBNE ); float dotBeforeASW = MathUtils::Dot( gradientBeforeASW, displacementFromBeforeASW ); float dotBeforeASE = MathUtils::Dot( gradientBeforeASE, displacementFromBeforeASE ); float dotBeforeANW = MathUtils::Dot( gradientBeforeANW, displacementFromBeforeANW ); float dotBeforeANE = MathUtils::Dot( gradientBeforeANE, displacementFromBeforeANE ); float dotAfterBSW = MathUtils::Dot( gradientAfterBSW, displacementFromAfterBSW ); float dotAfterBSE = MathUtils::Dot( gradientAfterBSE, displacementFromAfterBSE ); float dotAfterBNW = MathUtils::Dot( gradientAfterBNW, displacementFromAfterBNW ); float dotAfterBNE = MathUtils::Dot( gradientAfterBNE, displacementFromAfterBNE ); float dotAfterASW = MathUtils::Dot( gradientAfterASW, displacementFromAfterASW ); float dotAfterASE = MathUtils::Dot( gradientAfterASE, displacementFromAfterASE ); float dotAfterANW = MathUtils::Dot( gradientAfterANW, displacementFromAfterANW ); float dotAfterANE = MathUtils::Dot( gradientAfterANE, displacementFromAfterANE ); // Do a smoothed (nonlinear) weighted average of dot results float weightEast = SmoothStep( displacementFromBeforeBSW.x ); float weightNorth = SmoothStep( displacementFromBeforeBSW.y ); float weightAbove = SmoothStep( displacementFromBeforeBSW.z ); float weightAfter = SmoothStep( displacementFromBeforeBSW.w ); float weightWest = 1.f - weightEast; float weightSouth = 1.f - weightNorth; float weightBelow = 1.f - weightAbove; float weightBefore = 1.f - weightAfter; // 16-way blend (16 -> 8 -> 4 -> 2 -> 1) float blendBeforeBelowSouth = (weightEast * dotBeforeBSE) + (weightWest * dotBeforeBSW); float blendBeforeBelowNorth = (weightEast * dotBeforeBNE) + (weightWest * dotBeforeBNW); float blendBeforeAboveSouth = (weightEast * dotBeforeASE) + (weightWest * dotBeforeASW); float blendBeforeAboveNorth = (weightEast * dotBeforeANE) + (weightWest * dotBeforeANW); float blendAfterBelowSouth = (weightEast * dotAfterBSE) + (weightWest * dotAfterBSW); float blendAfterBelowNorth = (weightEast * dotAfterBNE) + (weightWest * dotAfterBNW); float blendAfterAboveSouth = (weightEast * dotAfterASE) + (weightWest * dotAfterASW); float blendAfterAboveNorth = (weightEast * dotAfterANE) + (weightWest * dotAfterANW); float blendBeforeBelow = (weightSouth * blendBeforeBelowSouth) + (weightNorth * blendBeforeBelowNorth); float blendBeforeAbove = (weightSouth * blendBeforeAboveSouth) + (weightNorth * blendBeforeAboveNorth); float blendAfterBelow = (weightSouth * blendAfterBelowSouth) + (weightNorth * blendAfterBelowNorth); float blendAfterAbove = (weightSouth * blendAfterAboveSouth) + (weightNorth * blendAfterAboveNorth); float blendBefore = (weightBelow * blendBeforeBelow) + (weightAbove * blendBeforeAbove); float blendAfter = (weightBelow * blendAfterBelow) + (weightAbove * blendAfterAbove); float blendTotal = (weightBefore * blendBefore) + (weightAfter * blendAfter); float noiseThisOctave = 1.6f * blendTotal; // 4D Perlin is in ~[-.5,.5]; map to ~[-1,1] // Accumulate results and prepare for next octave (if any) totalNoise += noiseThisOctave * currentAmplitude; totalAmplitude += currentAmplitude; currentAmplitude *= octavePersistence; currentPos *= octaveScale; currentPos.x += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids currentPos.y += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids currentPos.z += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids currentPos.w += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids ++ seed; // Eliminates octaves "echoing" each other (since each octave is uniquely seeded) } // Re-normalize total noise to within [-1,1] and fix octaves pulling us far away from limits if( renormalize && totalAmplitude > 0.f ) { totalNoise /= totalAmplitude; // Amplitude exceeds 1.0 if octaves are used totalNoise = (totalNoise * 0.5f) + 0.5f; // Map to [0,1] totalNoise = SmoothStep( totalNoise ); // Push towards extents (octaves pull us away) totalNoise = (totalNoise * 2.0f) - 1.f; // Map back to [-1,1] } return totalNoise; }
//----------------------------------------------------------------------------------------------- // Perlin noise is fractal noise with "gradient vector smoothing" applied. // // In 3D, gradients are unit-length vectors in random (3D) directions. // float Compute3dPerlinNoise( float posX, float posY, float posZ, float scale, unsigned int numOctaves, float octavePersistence, float octaveScale, bool renormalize, unsigned int seed ) { const float OCTAVE_OFFSET = 0.636764989593174f; // Translation/bias to add to each octave const Vector3 gradients[ 8 ] = // Traditional "12 edges" requires modulus and isn't any better. { Vector3( +fSQRT_3_OVER_3, +fSQRT_3_OVER_3, +fSQRT_3_OVER_3 ), // Normalized unit 3D vectors Vector3( -fSQRT_3_OVER_3, +fSQRT_3_OVER_3, +fSQRT_3_OVER_3 ), // pointing toward cube Vector3( +fSQRT_3_OVER_3, -fSQRT_3_OVER_3, +fSQRT_3_OVER_3 ), // corners, so components Vector3( -fSQRT_3_OVER_3, -fSQRT_3_OVER_3, +fSQRT_3_OVER_3 ), // are all sqrt(3)/3, i.e. Vector3( +fSQRT_3_OVER_3, +fSQRT_3_OVER_3, -fSQRT_3_OVER_3 ), // 0.5773502691896257645091f. Vector3( -fSQRT_3_OVER_3, +fSQRT_3_OVER_3, -fSQRT_3_OVER_3 ), // These are slightly better Vector3( +fSQRT_3_OVER_3, -fSQRT_3_OVER_3, -fSQRT_3_OVER_3 ), // than axes (1,0,0) and much Vector3( -fSQRT_3_OVER_3, -fSQRT_3_OVER_3, -fSQRT_3_OVER_3 ) // faster than edges (1,1,0). }; float totalNoise = 0.f; float totalAmplitude = 0.f; float currentAmplitude = 1.f; float invScale = (1.f / scale); Vector3 currentPos( posX * invScale, posY * invScale, posZ * invScale ); for( unsigned int octaveNum = 0; octaveNum < numOctaves; ++ octaveNum ) { // Determine random unit "gradient vectors" for surrounding corners Vector3 cellMins( FastFloor( currentPos.x ), FastFloor( currentPos.y ), FastFloor( currentPos.z ) ); Vector3 cellMaxs( cellMins.x + 1.f, cellMins.y + 1.f, cellMins.z + 1.f ); int indexWestX = (int) cellMins.x; int indexSouthY = (int) cellMins.y; int indexBelowZ = (int) cellMins.z; int indexEastX = indexWestX + 1; int indexNorthY = indexSouthY + 1; int indexAboveZ = indexBelowZ + 1; unsigned int noiseBelowSW = Get3dNoiseUint( indexWestX, indexSouthY, indexBelowZ, seed ); unsigned int noiseBelowSE = Get3dNoiseUint( indexEastX, indexSouthY, indexBelowZ, seed ); unsigned int noiseBelowNW = Get3dNoiseUint( indexWestX, indexNorthY, indexBelowZ, seed ); unsigned int noiseBelowNE = Get3dNoiseUint( indexEastX, indexNorthY, indexBelowZ, seed ); unsigned int noiseAboveSW = Get3dNoiseUint( indexWestX, indexSouthY, indexAboveZ, seed ); unsigned int noiseAboveSE = Get3dNoiseUint( indexEastX, indexSouthY, indexAboveZ, seed ); unsigned int noiseAboveNW = Get3dNoiseUint( indexWestX, indexNorthY, indexAboveZ, seed ); unsigned int noiseAboveNE = Get3dNoiseUint( indexEastX, indexNorthY, indexAboveZ, seed ); Vector3 gradientBelowSW = gradients[ noiseBelowSW & 0x00000007 ]; Vector3 gradientBelowSE = gradients[ noiseBelowSE & 0x00000007 ]; Vector3 gradientBelowNW = gradients[ noiseBelowNW & 0x00000007 ]; Vector3 gradientBelowNE = gradients[ noiseBelowNE & 0x00000007 ]; Vector3 gradientAboveSW = gradients[ noiseAboveSW & 0x00000007 ]; Vector3 gradientAboveSE = gradients[ noiseAboveSE & 0x00000007 ]; Vector3 gradientAboveNW = gradients[ noiseAboveNW & 0x00000007 ]; Vector3 gradientAboveNE = gradients[ noiseAboveNE & 0x00000007 ]; // Dot each corner's gradient with displacement from corner to position Vector3 displacementFromBelowSW( currentPos.x - cellMins.x, currentPos.y - cellMins.y, currentPos.z - cellMins.z ); Vector3 displacementFromBelowSE( currentPos.x - cellMaxs.x, currentPos.y - cellMins.y, currentPos.z - cellMins.z ); Vector3 displacementFromBelowNW( currentPos.x - cellMins.x, currentPos.y - cellMaxs.y, currentPos.z - cellMins.z ); Vector3 displacementFromBelowNE( currentPos.x - cellMaxs.x, currentPos.y - cellMaxs.y, currentPos.z - cellMins.z ); Vector3 displacementFromAboveSW( currentPos.x - cellMins.x, currentPos.y - cellMins.y, currentPos.z - cellMaxs.z ); Vector3 displacementFromAboveSE( currentPos.x - cellMaxs.x, currentPos.y - cellMins.y, currentPos.z - cellMaxs.z ); Vector3 displacementFromAboveNW( currentPos.x - cellMins.x, currentPos.y - cellMaxs.y, currentPos.z - cellMaxs.z ); Vector3 displacementFromAboveNE( currentPos.x - cellMaxs.x, currentPos.y - cellMaxs.y, currentPos.z - cellMaxs.z ); float dotBelowSW = MathUtils::Dot( gradientBelowSW, displacementFromBelowSW ); float dotBelowSE = MathUtils::Dot( gradientBelowSE, displacementFromBelowSE ); float dotBelowNW = MathUtils::Dot( gradientBelowNW, displacementFromBelowNW ); float dotBelowNE = MathUtils::Dot( gradientBelowNE, displacementFromBelowNE ); float dotAboveSW = MathUtils::Dot( gradientAboveSW, displacementFromAboveSW ); float dotAboveSE = MathUtils::Dot( gradientAboveSE, displacementFromAboveSE ); float dotAboveNW = MathUtils::Dot( gradientAboveNW, displacementFromAboveNW ); float dotAboveNE = MathUtils::Dot( gradientAboveNE, displacementFromAboveNE ); // Do a smoothed (nonlinear) weighted average of dot results float weightEast = SmoothStep5( displacementFromBelowSW.x ); float weightNorth = SmoothStep5( displacementFromBelowSW.y ); float weightAbove = SmoothStep5( displacementFromBelowSW.z ); float weightWest = 1.f - weightEast; float weightSouth = 1.f - weightNorth; float weightBelow = 1.f - weightAbove; // 8-way blend (8 -> 4 -> 2 -> 1) float blendBelowSouth = (weightEast * dotBelowSE) + (weightWest * dotBelowSW); float blendBelowNorth = (weightEast * dotBelowNE) + (weightWest * dotBelowNW); float blendAboveSouth = (weightEast * dotAboveSE) + (weightWest * dotAboveSW); float blendAboveNorth = (weightEast * dotAboveNE) + (weightWest * dotAboveNW); float blendBelow = (weightSouth * blendBelowSouth) + (weightNorth * blendBelowNorth); float blendAbove = (weightSouth * blendAboveSouth) + (weightNorth * blendAboveNorth); float blendTotal = (weightBelow * blendBelow) + (weightAbove * blendAbove); float noiseThisOctave = 1.66666666f * blendTotal; // 3D Perlin is ~[-.6,.6]; map to ~[-1,1] // Accumulate results and prepare for next octave (if any) totalNoise += noiseThisOctave * currentAmplitude; totalAmplitude += currentAmplitude; currentAmplitude *= octavePersistence; currentPos *= octaveScale; currentPos.x += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids currentPos.y += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids currentPos.z += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids ++ seed; // Eliminates octaves "echoing" each other (since each octave is uniquely seeded) } // Re-normalize total noise to within [-1,1] and fix octaves pulling us far away from limits if( renormalize && totalAmplitude > 0.f ) { totalNoise /= totalAmplitude; // Amplitude exceeds 1.0 if octaves are used totalNoise = (totalNoise * 0.5f) + 0.5f; // Map to [0,1] totalNoise = SmoothStep( totalNoise ); // Push towards extents (octaves pull us away) totalNoise = (totalNoise * 2.0f) - 1.f; // Map back to [-1,1] } return totalNoise; }
//----------------------------------------------------------------------------------------------- // Perlin noise is fractal noise with "gradient vector smoothing" applied. // // In 2D, gradients are unit-length vectors in various directions with even angular distribution. // float Compute2dPerlinNoise( float posX, float posY, float scale, unsigned int numOctaves, float octavePersistence, float octaveScale, bool renormalize, unsigned int seed ) { const float OCTAVE_OFFSET = 0.636764989593174f; // Translation/bias to add to each octave const Vector2 gradients[ 8 ] = // Normalized unit vectors in 8 quarter-cardinal directions { Vector2( +0.923879533f, +0.382683432f ), // 22.5 degrees Vector2( +0.382683432f, +0.923879533f ), // 67.5 degrees Vector2( -0.382683432f, +0.923879533f ), // 112.5 degrees Vector2( -0.923879533f, +0.382683432f ), // 157.5 degrees Vector2( -0.923879533f, -0.382683432f ), // 202.5 degrees Vector2( -0.382683432f, -0.923879533f ), // 247.5 degrees Vector2( +0.382683432f, -0.923879533f ), // 292.5 degrees Vector2( +0.923879533f, -0.382683432f ) // 337.5 degrees }; float totalNoise = 0.f; float totalAmplitude = 0.f; float currentAmplitude = 1.f; float invScale = (1.f / scale); Vector2 currentPos( posX * invScale, posY * invScale ); for( unsigned int octaveNum = 0; octaveNum < numOctaves; ++ octaveNum ) { // Determine random unit "gradient vectors" for surrounding corners Vector2 cellMins( FastFloor( currentPos.x ), FastFloor( currentPos.y ) ); Vector2 cellMaxs( cellMins.x + 1.f, cellMins.y + 1.f ); int indexWestX = (int) cellMins.x; int indexSouthY = (int) cellMins.y; int indexEastX = indexWestX + 1; int indexNorthY = indexSouthY + 1; unsigned int noiseSW = Get2dNoiseUint( indexWestX, indexSouthY, seed ); unsigned int noiseSE = Get2dNoiseUint( indexEastX, indexSouthY, seed ); unsigned int noiseNW = Get2dNoiseUint( indexWestX, indexNorthY, seed ); unsigned int noiseNE = Get2dNoiseUint( indexEastX, indexNorthY, seed ); Vector2 gradientSW = gradients[ noiseSW & 0x00000007 ]; Vector2 gradientSE = gradients[ noiseSE & 0x00000007 ]; Vector2 gradientNW = gradients[ noiseNW & 0x00000007 ]; Vector2 gradientNE = gradients[ noiseNE & 0x00000007 ]; // Dot each corner's gradient with displacement from corner to position Vector2 displacementFromSW( currentPos.x - cellMins.x, currentPos.y - cellMins.y ); Vector2 displacementFromSE( currentPos.x - cellMaxs.x, currentPos.y - cellMins.y ); Vector2 displacementFromNW( currentPos.x - cellMins.x, currentPos.y - cellMaxs.y ); Vector2 displacementFromNE( currentPos.x - cellMaxs.x, currentPos.y - cellMaxs.y ); float dotSouthWest = MathUtils::Dot( gradientSW, displacementFromSW ); float dotSouthEast = MathUtils::Dot( gradientSE, displacementFromSE ); float dotNorthWest = MathUtils::Dot( gradientNW, displacementFromNW ); float dotNorthEast = MathUtils::Dot( gradientNE, displacementFromNE ); // Do a smoothed (nonlinear) weighted average of dot results float weightEast = SmoothStep5( displacementFromSW.x ); float weightNorth = SmoothStep5( displacementFromSW.y ); float weightWest = 1.f - weightEast; float weightSouth = 1.f - weightNorth; float blendSouth = (weightEast * dotSouthEast) + (weightWest * dotSouthWest); float blendNorth = (weightEast * dotNorthEast) + (weightWest * dotNorthWest); float blendTotal = (weightSouth * blendSouth) + (weightNorth * blendNorth); float noiseThisOctave = 1.5f * blendTotal; // 2D Perlin is in ~[-.66,.66]; map to ~[-1,1] // Accumulate results and prepare for next octave (if any) totalNoise += noiseThisOctave * currentAmplitude; totalAmplitude += currentAmplitude; currentAmplitude *= octavePersistence; currentPos *= octaveScale; currentPos.x += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids currentPos.y += OCTAVE_OFFSET; // Add "irrational" offset to de-align octave grids ++ seed; // Eliminates octaves "echoing" each other (since each octave is uniquely seeded) } // Re-normalize total noise to within [-1,1] and fix octaves pulling us far away from limits if( renormalize && totalAmplitude > 0.f ) { totalNoise /= totalAmplitude; // Amplitude exceeds 1.0 if octaves are used totalNoise = (totalNoise * 0.5f) + 0.5f; // Map to [0,1] totalNoise = SmoothStep( totalNoise ); // Push towards extents (octaves pull us away) totalNoise = (totalNoise * 2.0f) - 1.f; // Map back to [-1,1] } return totalNoise; }
//----------------------------------------------------------------------------------------------- float Compute4dFractalNoise( float posX, float posY, float posZ, float posT, float scale, unsigned int numOctaves, float octavePersistence, float octaveScale, bool renormalize, unsigned int seed ) { const float OCTAVE_OFFSET = 0.636764989593174f; // Translation/bias to add to each octave float totalNoise = 0.f; float totalAmplitude = 0.f; float currentAmplitude = 1.f; float invScale = (1.f / scale); Vector4 currentPos( posX * invScale, posY * invScale, posZ * invScale, posT * invScale ); for( unsigned int octaveNum = 0; octaveNum < numOctaves; ++ octaveNum ) { // Determine noise values at nearby integer "grid point" positions Vector4 cellMins( FastFloor( currentPos.x ), FastFloor( currentPos.y ), FastFloor( currentPos.z ), FastFloor( currentPos.w ) ); int indexWestX = (int) cellMins.x; int indexSouthY = (int) cellMins.y; int indexBelowZ = (int) cellMins.z; int indexBeforeT = (int) cellMins.w; int indexEastX = indexWestX + 1; int indexNorthY = indexSouthY + 1; int indexAboveZ = indexBelowZ + 1; int indexAfterT = indexBeforeT + 1; // Noise grid cell has 16 "corners" in 4D float beforeBelowSW = Get4dNoiseZeroToOne( indexWestX, indexSouthY, indexBelowZ, indexBeforeT, seed ); float beforeBelowSE = Get4dNoiseZeroToOne( indexEastX, indexSouthY, indexBelowZ, indexBeforeT, seed ); float beforeBelowNW = Get4dNoiseZeroToOne( indexWestX, indexNorthY, indexBelowZ, indexBeforeT, seed ); float beforeBelowNE = Get4dNoiseZeroToOne( indexEastX, indexNorthY, indexBelowZ, indexBeforeT, seed ); float beforeAboveSW = Get4dNoiseZeroToOne( indexWestX, indexSouthY, indexAboveZ, indexBeforeT, seed ); float beforeAboveSE = Get4dNoiseZeroToOne( indexEastX, indexSouthY, indexAboveZ, indexBeforeT, seed ); float beforeAboveNW = Get4dNoiseZeroToOne( indexWestX, indexNorthY, indexAboveZ, indexBeforeT, seed ); float beforeAboveNE = Get4dNoiseZeroToOne( indexEastX, indexNorthY, indexAboveZ, indexBeforeT, seed ); float afterBelowSW = Get4dNoiseZeroToOne( indexWestX, indexSouthY, indexBelowZ, indexAfterT, seed ); float afterBelowSE = Get4dNoiseZeroToOne( indexEastX, indexSouthY, indexBelowZ, indexAfterT, seed ); float afterBelowNW = Get4dNoiseZeroToOne( indexWestX, indexNorthY, indexBelowZ, indexAfterT, seed ); float afterBelowNE = Get4dNoiseZeroToOne( indexEastX, indexNorthY, indexBelowZ, indexAfterT, seed ); float afterAboveSW = Get4dNoiseZeroToOne( indexWestX, indexSouthY, indexAboveZ, indexAfterT, seed ); float afterAboveSE = Get4dNoiseZeroToOne( indexEastX, indexSouthY, indexAboveZ, indexAfterT, seed ); float afterAboveNW = Get4dNoiseZeroToOne( indexWestX, indexNorthY, indexAboveZ, indexAfterT, seed ); float afterAboveNE = Get4dNoiseZeroToOne( indexEastX, indexNorthY, indexAboveZ, indexAfterT, seed ); // Do a smoothed (nonlinear) weighted average of nearby grid point values Vector4 displacementFromMins = currentPos - cellMins; float weightEast = SmoothStep( displacementFromMins.x ); float weightNorth = SmoothStep( displacementFromMins.y ); float weightAbove = SmoothStep( displacementFromMins.z ); float weightAfter = SmoothStep( displacementFromMins.w ); float weightWest = 1.f - weightEast; float weightSouth = 1.f - weightNorth; float weightBelow = 1.f - weightAbove; float weightBefore = 1.f - weightAfter; // 16-way blend (16 -> 8 -> 4 -> 2 -> 1) float blendBeforeBelowSouth = (weightEast * beforeBelowSE) + (weightWest * beforeBelowSW); float blendBeforeBelowNorth = (weightEast * beforeBelowNE) + (weightWest * beforeBelowNW); float blendBeforeAboveSouth = (weightEast * beforeAboveSE) + (weightWest * beforeAboveSW); float blendBeforeAboveNorth = (weightEast * beforeAboveNE) + (weightWest * beforeAboveNW); float blendAfterBelowSouth = (weightEast * afterBelowSE) + (weightWest * afterBelowSW); float blendAfterBelowNorth = (weightEast * afterBelowNE) + (weightWest * afterBelowNW); float blendAfterAboveSouth = (weightEast * afterAboveSE) + (weightWest * afterAboveSW); float blendAfterAboveNorth = (weightEast * afterAboveNE) + (weightWest * afterAboveNW); float blendBeforeBelow = (weightSouth * blendBeforeBelowSouth) + (weightNorth * blendBeforeBelowNorth); float blendBeforeAbove = (weightSouth * blendBeforeAboveSouth) + (weightNorth * blendBeforeAboveNorth); float blendAfterBelow = (weightSouth * blendAfterBelowSouth) + (weightNorth * blendAfterBelowNorth); float blendAfterAbove = (weightSouth * blendAfterAboveSouth) + (weightNorth * blendAfterAboveNorth); float blendBefore = (weightBelow * blendBeforeBelow) + (weightAbove * blendBeforeAbove); float blendAfter = (weightBelow * blendAfterBelow) + (weightAbove * blendAfterAbove); float blendTotal = (weightBefore * blendBefore) + (weightAfter * blendAfter); float noiseThisOctave = 2.f * (blendTotal - 0.5f); // Map from [0,1] to [-1,1] // Accumulate results and prepare for next octave (if any) totalNoise += noiseThisOctave * currentAmplitude; totalAmplitude += currentAmplitude; currentAmplitude *= octavePersistence; currentPos *= octaveScale; currentPos.x += OCTAVE_OFFSET; // Add "irrational" offsets to noise position components currentPos.y += OCTAVE_OFFSET; // at each octave to break up their grid alignment currentPos.z += OCTAVE_OFFSET; currentPos.w += OCTAVE_OFFSET; ++ seed; // Eliminates octaves "echoing" each other (since each octave is uniquely seeded) } // Re-normalize total noise to within [-1,1] and fix octaves pulling us far away from limits if( renormalize && totalAmplitude > 0.f ) { totalNoise /= totalAmplitude; // Amplitude exceeds 1.0 if octaves are used totalNoise = (totalNoise * 0.5f) + 0.5f; // Map to [0,1] totalNoise = SmoothStep( totalNoise ); // Push towards extents (octaves pull us away) totalNoise = (totalNoise * 2.0f) - 1.f; // Map back to [-1,1] } return totalNoise; }
// // 获得光照参数 // BOOL CLightGrid::GetLightParams(DWORD dwChannel, const VEC3 *position, VEC4 *direction, VEC3 *ambient, VEC3 *diffuse, VEC3 *specular, VEC3 *rim, VEC3 *skyLower, VEC3 *skyUpper, VEC3 *indirectUp, VEC3 *indirectDown, VEC3 *indirectLeft, VEC3 *indirectRight, VEC3 *indirectFront, VEC3 *indirectBack) const { ASSERT(position); // // 1. 通道检查 // if ((LIGHT_CHANNEL & dwChannel) == 0) { return FALSE; } // // 2. 获得光照参数 // for (LightVolumeSet::const_iterator itVolume = m_volumes.begin(); itVolume != m_volumes.end(); ++itVolume) { const LIGHT_VOLUME *pVolume = *itVolume; ASSERT(pVolume); if (IsPointInAABB(&pVolume->aabb, (*position)[0], (*position)[1], (*position)[2])) { FLOAT posx = ((*position)[0] - pVolume->aabb.minVertex[0]) / pVolume->stepx; FLOAT posy = ((*position)[1] - pVolume->aabb.minVertex[1]) / pVolume->stepy; FLOAT posz = ((*position)[2] - pVolume->aabb.minVertex[2]) / pVolume->stepz; INT x = FastFloor(posx); INT y = FastFloor(posy); INT z = FastFloor(posz); FLOAT factor; FLOAT factorx = posx - x; FLOAT factory = posy - y; FLOAT factorz = posz - z; VEC3 _direction; VEC3 _ambient; VEC3 _diffuse; VEC3 _indirectUp; VEC3 _indirectDown; VEC3 _indirectLeft; VEC3 _indirectRight; VEC3 _indirectFront; VEC3 _indirectBack; Vec3Zero(&_direction); Vec3Zero(&_ambient); Vec3Zero(&_diffuse); Vec3Zero(&_indirectUp); Vec3Zero(&_indirectDown); Vec3Zero(&_indirectLeft); Vec3Zero(&_indirectRight); Vec3Zero(&_indirectFront); Vec3Zero(&_indirectBack); for (INT xx = 0; xx <= 1; xx++) { factorx = 1.0f - factorx; for (INT yy = 0; yy <= 1; yy++) { factory = 1.0f - factory; for (INT zz = 0; zz <= 1; zz++) { factorz = 1.0f - factorz; factor = factorx * factory * factorz; if (LIGHT_POINT *pLightPoint = &pVolume->pppLightPoints[x + xx][y + yy][z + zz]) { VEC3 __direction; VEC3 __ambient; VEC3 __diffuse; VEC3 __indirectUp; VEC3 __indirectDown; VEC3 __indirectLeft; VEC3 __indirectRight; VEC3 __indirectFront; VEC3 __indirectBack; Vec3LatLongToDirection(&__direction, pLightPoint->direction); Vec3BytesToColor(&__ambient, pLightPoint->ambient); Vec3BytesToColor(&__diffuse, pLightPoint->diffuse); Vec3BytesToColor(&__indirectUp, pLightPoint->indirectup); Vec3BytesToColor(&__indirectDown, pLightPoint->indirectdown); Vec3BytesToColor(&__indirectLeft, pLightPoint->indirectleft); Vec3BytesToColor(&__indirectRight, pLightPoint->indirectright); Vec3BytesToColor(&__indirectFront, pLightPoint->indirectfront); Vec3BytesToColor(&__indirectBack, pLightPoint->indirectback); Vec3Ma(&_direction, &_direction, &__direction, factor); Vec3Ma(&_ambient, &_ambient, &__ambient, factor); Vec3Ma(&_diffuse, &_diffuse, &__diffuse, factor); Vec3Ma(&_indirectUp, &_diffuse, &__indirectUp, factor); Vec3Ma(&_indirectDown, &_diffuse, &__indirectDown, factor); Vec3Ma(&_indirectLeft, &_diffuse, &__indirectLeft, factor); Vec3Ma(&_indirectRight, &_diffuse, &__indirectRight, factor); Vec3Ma(&_indirectFront, &_diffuse, &__indirectFront, factor); Vec3Ma(&_indirectBack, &_diffuse, &__indirectBack, factor); } } } } Vec3Normalize(&_direction); Vec3Clamp(&_ambient); Vec3Clamp(&_diffuse); Vec3Clamp(&_indirectUp); Vec3Clamp(&_indirectDown); Vec3Clamp(&_indirectLeft); Vec3Clamp(&_indirectRight); Vec3Clamp(&_indirectFront); Vec3Clamp(&_indirectBack); static const FLOAT specularFactor = 0.8f; static const FLOAT rimFactor = 0.2f; static const FLOAT skyLowerFactor = 0.2f; static const FLOAT skyUpperFactor = 0.4f; if (direction) Vec4Set(direction, _direction[0], _direction[1], _direction[2], 0.0f); if (ambient) Vec3Set(ambient, _ambient[0], _ambient[1], _ambient[2]); if (diffuse) Vec3Set(diffuse, _diffuse[0], _diffuse[1], _diffuse[2]); if (specular) Vec3Set(specular, specularFactor * _diffuse[0], specularFactor * _diffuse[1], specularFactor * _diffuse[2]); if (rim) Vec3Set(rim, rimFactor * _diffuse[0], rimFactor * _diffuse[1], rimFactor * _diffuse[2]); if (skyLower) Vec3Set(skyLower, skyLowerFactor * _diffuse[0], skyLowerFactor * _diffuse[1], skyLowerFactor * _diffuse[2]); if (skyUpper) Vec3Set(skyUpper, skyUpperFactor * _diffuse[0], skyUpperFactor * _diffuse[1], skyUpperFactor * _diffuse[2]); if (indirectUp) Vec3Set(indirectUp, _indirectUp[0], _indirectUp[1], _indirectUp[2]); if (indirectDown) Vec3Set(indirectDown, _indirectDown[0], _indirectDown[1], _indirectDown[2]); if (indirectLeft) Vec3Set(indirectLeft, _indirectLeft[0], _indirectLeft[1], _indirectLeft[2]); if (indirectRight) Vec3Set(indirectRight, _indirectRight[0], _indirectRight[1], _indirectRight[2]); if (indirectFront) Vec3Set(indirectFront, _indirectFront[0], _indirectFront[1], _indirectFront[2]); if (indirectBack) Vec3Set(indirectBack, _indirectBack[0], _indirectBack[1], _indirectBack[2]); return TRUE; } } return FALSE; }