//----------------------------------------------------------------------------------------------- // Computes a random Perlin noise value in the range [-1,1] based on a 2D input <position> and // various Perlin noise parameters. // // <perlinNoiseGridCellSize>: Noise density. Larger values produce longer wavelength noise // (e.g. gentle, sweeping hills). // <numOctaves>: 0 is flat, 1 is simple smoothed noise. Values of 2+ add one or more additional // "octave" harmonics. Each additional octave has double the frequency/density but only a // fraction of the amplitude of the base noise. // <persistence>: The fraction of amplitude of each subsequent octave, based on the amplitude of // the previous octave. For example, with a persistence of 0.3, each octave is only 30% as // strong as the previous octave. // float ComputePerlinNoise2D( const Vector2& position, float perlinNoiseGridCellSize, int numOctaves, float persistence ) { float totalPerlinNoise = 0.f; float currentOctaveAmplitude = 1.f; float totalMaxAmplitude = 0.f; float perlinGridFrequency = (1.f / perlinNoiseGridCellSize); for( int octaveNumber = 0; octaveNumber < numOctaves; ++octaveNumber ) { Vector2 perlinPosition( position.x * perlinGridFrequency, position.y * perlinGridFrequency ); Vector2 perlinPositionFloor( (float) floor( perlinPosition.x ), (float) floor( perlinPosition.y ) ); Vector2Int perlinCell( (int) perlinPositionFloor.x, (int) perlinPositionFloor.y ); Vector2 perlinPositionUV = perlinPosition - perlinPositionFloor; Vector2 perlinPositionAntiUV( perlinPositionUV.x - 1.f, perlinPositionUV.y - 1.f ); float eastWeight = MathUtils::SmoothStep( perlinPositionUV.x ); float northWeight = MathUtils::SmoothStep(perlinPositionUV.y); float westWeight = 1.f - eastWeight; float southWeight = 1.f - northWeight; Vector2 southwestNoiseGradient = GetPseudoRandomNoiseDirection2D( perlinCell.x, perlinCell.y ); Vector2 southeastNoiseGradient = GetPseudoRandomNoiseDirection2D( perlinCell.x + 1, perlinCell.y ); Vector2 northeastNoiseGradient = GetPseudoRandomNoiseDirection2D( perlinCell.x + 1, perlinCell.y + 1 ); Vector2 northwestNoiseGradient = GetPseudoRandomNoiseDirection2D( perlinCell.x, perlinCell.y + 1 ); float southwestDot = MathUtils::Dot(southwestNoiseGradient, perlinPositionUV); float southeastDot = MathUtils::Dot(southeastNoiseGradient, Vector2(perlinPositionAntiUV.x, perlinPositionUV.y)); float northeastDot = MathUtils::Dot(northeastNoiseGradient, perlinPositionAntiUV); float northwestDot = MathUtils::Dot(northwestNoiseGradient, Vector2(perlinPositionUV.x, perlinPositionAntiUV.y)); float southBlend = (eastWeight * southeastDot) + (westWeight * southwestDot); float northBlend = (eastWeight * northeastDot) + (westWeight * northwestDot); float fourWayBlend = (southWeight * southBlend) + (northWeight * northBlend); float perlinNoiseForThisOctave = currentOctaveAmplitude * fourWayBlend; totalPerlinNoise += perlinNoiseForThisOctave; perlinGridFrequency *= 2.f; totalMaxAmplitude += currentOctaveAmplitude; currentOctaveAmplitude *= persistence; } if( totalMaxAmplitude != 0.f ) totalPerlinNoise /= totalMaxAmplitude; return totalPerlinNoise; }
//--------------------------------------------------------------------------- // Computes a random Perlin noise value based on a 2D input <position> and // Perlin noise parameters. Recursive (for additional octaves). // // <perlinNoiseGridCellSize>: Noise density. Larger values produce longer // wavelength noise (e.g. gentle, sweeping hills). // <numOctaves>: 0 is flat, 1 is simple smoothed noise. Values of 2+ add one // or more additional "octave" harmonics. Each additional octave has // double the frequency/density but only a fraction of the amplitude of // the base noise. // <baseAmplitude>: The minimum (-amplitude) and maximum (+amplitude) values // produced by the first octave of the noise. Note that adding // additional octaves can push the final total Perlin noise values above // or below the maximum base amplitude; the noise can be "normalized" by // the caller (omitted from this function for optimization purposes) via: // noise *= A / (A + (A * P)) // ...where A is the <baseAmplitude> and P is the <persistance>. // <persistance>: The fraction of amplitude of each subsequent octave, based on the amplitude of the previous octave. For // example, with a persistance of 0.3, each octave is only 30% as strong as the previous octave. // float ComputePerlinNoiseValueAtPosition2D( const Vector2& position, float perlinNoiseGridCellSize, int numOctaves, float baseAmplitude, float persistance, int randomSeed ) { int numOctavesRemaining = numOctaves; float amplitude = baseAmplitude; float gridSize = perlinNoiseGridCellSize; float totalPerlinNoise = 0.0f; while( numOctavesRemaining > 0 ) { Vector2 perlinPosition = position / gridSize; Vector2 perlinPositionFloor( floor( perlinPosition.x ), floor( perlinPosition.y ) ); IntVec2 perlinCell( (int) perlinPositionFloor.x, (int) perlinPositionFloor.y ); Vector2 perlinPositionUV = perlinPosition - perlinPositionFloor; Vector2 perlinPositionAntiUV( perlinPositionUV.x - 1.f, perlinPositionUV.y - 1.f ); float eastWeight = SmoothStep( perlinPositionUV.x ); float northWeight = SmoothStep( perlinPositionUV.y ); float westWeight = 1.f - eastWeight; float southWeight = 1.f - northWeight; Vector2 southwestNoiseGradient = GetPseudoRandomNoiseUnitVector2D( perlinCell.x, perlinCell.y, randomSeed ); Vector2 southeastNoiseGradient = GetPseudoRandomNoiseUnitVector2D( perlinCell.x + 1, perlinCell.y, randomSeed ); Vector2 northeastNoiseGradient = GetPseudoRandomNoiseUnitVector2D( perlinCell.x + 1, perlinCell.y + 1, randomSeed ); Vector2 northwestNoiseGradient = GetPseudoRandomNoiseUnitVector2D( perlinCell.x, perlinCell.y + 1, randomSeed ); float southwestDot = DotProduct( southwestNoiseGradient, perlinPositionUV ); float southeastDot = DotProduct( southeastNoiseGradient, Vector2( perlinPositionAntiUV.x, perlinPositionUV.y ) ); float northeastDot = DotProduct( northeastNoiseGradient, perlinPositionAntiUV ); float northwestDot = DotProduct( northwestNoiseGradient, Vector2( perlinPositionUV.x, perlinPositionAntiUV.y ) ); float southBlend = (eastWeight * southeastDot) + (westWeight * southwestDot); float northBlend = (eastWeight * northeastDot) + (westWeight * northwestDot); float fourWayBlend = (southWeight * southBlend) + (northWeight * northBlend); float perlinNoiseAtThisOctave = amplitude * fourWayBlend; -- numOctavesRemaining; totalPerlinNoise += perlinNoiseAtThisOctave; amplitude *= persistance; gridSize *= 0.5f; } return totalPerlinNoise; }
//--------------------------------------------------------------------------- // Computes a random Perlin noise value based on a 2D input <position> and // Perlin noise parameters. Recursive (for additional octaves). // // <perlinNoiseGridCellSize>: Noise density. Larger values produce longer // wavelength noise (e.g. gentle, sweeping hills). // <numOctaves>: 0 is flat, 1 is simple smoothed noise. Values of 2+ add one // or more additional "octave" harmonics. Each additional octave has // double the frequency/density but only a fraction of the amplitude of // the base noise. // <baseAmplitude>: The minimum (-amplitude) and maximum (+amplitude) values // produced by the first octave of the noise. Note that adding // additional octaves can push the final total Perlin noise values above // or below the maximum base amplitude; the noise can be "normalized" by // the caller (omitted from this function for optimization purposes) via: // noise *= A / (A + (A * P)) // ...where A is the <baseAmplitude> and P is the <persistance>. // <persistance>: The fraction of amplitude of each subsequent octave, based on the amplitude of the previous octave. For // example, with a persistance of 0.3, each octave is only 30% as strong as the previous octave. // float ComputePerlinNoiseValueAtPosition2D( const Vector2& position, float perlinNoiseGridCellSize, int numOctaves, float baseAmplitude, float persistance ) { if( numOctaves == 0 ) return 0.f; Vector2 perlinPosition = position / perlinNoiseGridCellSize; Vector2 perlinPositionFloor(floor(perlinPosition.x), floor(perlinPosition.y)); Vector2Int perlinCell((int)perlinPositionFloor.x, (int)perlinPositionFloor.y); Vector2 perlinPositionUV = perlinPosition - perlinPositionFloor; Vector2 perlinPositionAntiUV(perlinPositionUV.x - 1.f, perlinPositionUV.y - 1.f); float eastWeight = SmoothStep(perlinPositionUV.x); float northWeight = SmoothStep(perlinPositionUV.y); float westWeight = 1.f - eastWeight; float southWeight = 1.f - northWeight; Vector2 southwestNoiseGradient = GetPseudoRandomNoiseUnitVector2D( perlinCell.x, perlinCell.y ); Vector2 southeastNoiseGradient = GetPseudoRandomNoiseUnitVector2D( perlinCell.x + 1, perlinCell.y ); Vector2 northeastNoiseGradient = GetPseudoRandomNoiseUnitVector2D( perlinCell.x + 1, perlinCell.y + 1 ); Vector2 northwestNoiseGradient = GetPseudoRandomNoiseUnitVector2D( perlinCell.x, perlinCell.y + 1 ); float southwestDot = MathUtils::Dot( southwestNoiseGradient, perlinPositionUV ); float southeastDot = MathUtils::Dot( southeastNoiseGradient, Vector2( perlinPositionAntiUV.x, perlinPositionUV.y ) ); float northeastDot = MathUtils::Dot( northeastNoiseGradient, perlinPositionAntiUV ); float northwestDot = MathUtils::Dot( northwestNoiseGradient, Vector2( perlinPositionUV.x, perlinPositionAntiUV.y ) ); float southBlend = (eastWeight * southeastDot) + (westWeight * southwestDot); float northBlend = (eastWeight * northeastDot) + (westWeight * northwestDot); float fourWayBlend = (southWeight * southBlend) + (northWeight * northBlend); float perlinNoiseAtThisOctave = baseAmplitude * fourWayBlend; float perlinNoiseFromAllHigherOctaves = ComputePerlinNoiseValueAtPosition2D( position, 0.5f * perlinNoiseGridCellSize, numOctaves - 1, baseAmplitude * persistance, persistance ); float totalPerlinNoise = perlinNoiseAtThisOctave + perlinNoiseFromAllHigherOctaves; return totalPerlinNoise; }