//----------------------------------------------------------------------------------------------- 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; }
void ShortCircuitBridge::QueueAbandon(UInt2 uberMins, UInt2 uberMaxs) { // look for overlapping cells for (const auto&i:_cells) { const auto& r = i.second; if ( r._uberMins[0] >= uberMaxs[0] || r._uberMaxs[0] < uberMins[0] || r._uberMins[1] >= uberMaxs[1] || r._uberMaxs[1] < uberMins[1]) continue; // remove any pending updates -- because they've all be abandoned now. auto compare = CellRegion { i.first, Float2(0.f, 0.f), Float2(0.f, 0.f) }; auto range = std::equal_range( _pendingUpdates.begin(), _pendingUpdates.end(), compare, CompareCellHash); _pendingUpdates.erase(range.first, range.second); // queue a new abandon event Float2 cellMins( (int(uberMins[0]) - int(r._uberMins[0])) / float(r._uberMaxs[0] - r._uberMins[0]), (int(uberMins[1]) - int(r._uberMins[1])) / float(r._uberMaxs[1] - r._uberMins[1])); Float2 cellMaxs( (int(uberMaxs[0]) - int(r._uberMins[0])) / float(r._uberMaxs[0] - r._uberMins[0]), (int(uberMaxs[1]) - int(r._uberMins[1])) / float(r._uberMaxs[1] - r._uberMins[1])); cellMins[0] = std::max(cellMins[0], 0.f); cellMins[1] = std::max(cellMins[1], 0.f); cellMaxs[0] = std::min(cellMaxs[0], 1.f); cellMaxs[1] = std::min(cellMaxs[1], 1.f); auto q = std::lower_bound( _pendingAbandons.begin(), _pendingAbandons.end(), compare, CompareCellHash); // we can choose to insert this as a separate event, or just combine it with // what is already there. if (q != _pendingAbandons.end() && q->_cellHash == i.first) { q->_cellMins[0] = std::min(q->_cellMins[0], cellMins[0]); q->_cellMins[1] = std::min(q->_cellMins[1], cellMins[1]); q->_cellMaxs[0] = std::max(q->_cellMaxs[0], cellMaxs[0]); q->_cellMaxs[1] = std::max(q->_cellMaxs[1], cellMaxs[1]); } else { CellRegion region { i.first, cellMins, cellMaxs }; _pendingAbandons.insert(q, region); } } }
void ShortCircuitBridge::QueueShortCircuit(UInt2 uberMins, UInt2 uberMaxs) { for (const auto&i:_cells) { const auto& r = i.second; if ( r._uberMins[0] >= uberMaxs[0] || r._uberMaxs[0] < uberMins[0] || r._uberMins[1] >= uberMaxs[1] || r._uberMaxs[1] < uberMins[1]) continue; // queue a new update event Float2 cellMins( (int(uberMins[0]) - int(r._uberMins[0])) / float(r._uberMaxs[0] - r._uberMins[0]), (int(uberMins[1]) - int(r._uberMins[1])) / float(r._uberMaxs[1] - r._uberMins[1])); Float2 cellMaxs( (int(uberMaxs[0]) - int(r._uberMins[0])) / float(r._uberMaxs[0] - r._uberMins[0]), (int(uberMaxs[1]) - int(r._uberMins[1])) / float(r._uberMaxs[1] - r._uberMins[1])); cellMins[0] = std::max(cellMins[0], 0.f); cellMins[1] = std::max(cellMins[1], 0.f); cellMaxs[0] = std::min(cellMaxs[0], 1.f); cellMaxs[1] = std::min(cellMaxs[1], 1.f); auto compare = CellRegion { i.first, Float2(0.f, 0.f), Float2(0.f, 0.f) }; auto q = std::lower_bound( _pendingUpdates.begin(), _pendingUpdates.end(), compare, CompareCellHash); // we can choose to insert this as a separate event, or just combine it with // what is already there. if (q != _pendingUpdates.end() && q->_cellHash == i.first) { q->_cellMins[0] = std::min(q->_cellMins[0], cellMins[0]); q->_cellMins[1] = std::min(q->_cellMins[1], cellMins[1]); q->_cellMaxs[0] = std::max(q->_cellMaxs[0], cellMaxs[0]); q->_cellMaxs[1] = std::max(q->_cellMaxs[1], cellMaxs[1]); } else { CellRegion region { i.first, cellMins, cellMaxs }; _pendingUpdates.insert(q, region); } } }
//----------------------------------------------------------------------------------------------- // 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; }