Skip to content

Instantly share code, notes, and snippets.

@apkd
Last active May 14, 2023 20:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apkd/eb733523bee790c3b15e9aac39713ef8 to your computer and use it in GitHub Desktop.
Save apkd/eb733523bee790c3b15e9aac39713ef8 to your computer and use it in GitHub Desktop.
SquirrelNoise5 - Squirrel's Raw Noise utilities (version 5), ported to C#. http://tryfinally.dev/squirrel-noise-5-ported-to-csharp
{
"displayName": "SquirrelNoise5",
"name": "squirrelnoise",
"version": "5.0.0",
"unity": "2019.4",
"description": "SquirrelNoise5 - Squirrel's Raw Noise utilities (version 5)",
"license": "Creative Commons attribution 3.0 license (CC-BY-3.0 US)",
"category": "Libraries"
}
guid: 892ef7640108ad742b90fa71a901f79f
{ "name": "squirrelnoise", "autoreferenced": true }
guid: f4113414f2ce2f4419f8c1bdcc61f1fa
// ReSharper disable MemberHidesStaticFromOuterClass
/// <summary>
/// SquirrelNoise5 - Squirrel's Raw Noise utilities (version 5)
///
/// This code is made available under the Creative Commons attribution 3.0 license (CC-BY-3.0 US):
/// Attribution in source code comments (even closed-source/commercial code) is sufficient.
/// License summary and text available at: https://creativecommons.org/licenses/by/3.0/us/
///
/// These noise functions were written by Squirrel Eiserloh as a cheap and simple substitute for
/// the [sometimes awful] bit-noise sample code functions commonly found on the web, many of which
/// are hugely biased or terribly patterned, e.g. having bits which are on (or off) 75% or even
/// 100% of the time (or are excessively overkill/slow for our needs, such as MD5 or SHA).
///
/// Note: This is work in progress; not all functions have been tested. Use at your own risk.
/// Please report any bugs, issues, or bothersome cases to SquirrelEiserloh at gmail.com.
///
/// The following functions are all based on a simple bit-noise hash function which returns an
/// unsigned integer containing 32 reasonably-well-scrambled bits, based on a given (signed)
/// integer input parameter (position/index) and [optional] seed. Kind of like looking up a
/// value in an infinitely large [non-existent] table of previously rolled random numbers.
///
/// These functions are deterministic and random-access / order-independent (i.e. state-free),
/// so they are particularly well-suited for use in smoothed/fractal/simplex/Perlin noise
/// functions and out-of-order (or on-demand) procedural content generation (i.e. that mountain
/// village is the same whether you generated it first or last, ahead of time or just now).
///
/// The N-dimensional variations simply hash their multidimensional coordinates down to a single
/// 32-bit index and then proceed as usual, so while results are not unique they should
/// (hopefully) not seem locally predictable or repetitive.
/// </summary>
public static class SquirrelNoise5
{
const double ONE_OVER_MAX_UINT = 1.0 / uint.MaxValue;
const double ONE_OVER_MAX_INT = 1.0 / int.MaxValue;
/// <summary>
/// Fast hash of an int32 into a different (unrecognizable) uint32.
/// Returns an unsigned integer containing 32 reasonably-well-scrambled bits, based on the hash
/// of a given (signed) integer input parameter (position/index) and [optional] seed. Kind of
/// like looking up a value in an infinitely large table of previously generated random numbers.
/// I call this particular approach SquirrelNoise5 (5th iteration of my 1D raw noise function).
/// Many thanks to Peter Schmidt-Nielsen whose outstanding analysis helped identify a weakness
/// in the SquirrelNoise3 code I originally used in my GDC 2017 talk, "Noise-based RNG".
/// Version 5 avoids a noise repetition found in version 3 at extremely high position values
/// caused by a lack of influence by some of the high input bits onto some of the low output bits.
/// The revised SquirrelNoise5 function ensures all input bits affect all output bits, and to
/// (for me) a statistically acceptable degree. I believe the worst-case here is in the amount
/// of influence input position bit #30 has on output noise bit #0 (49.99%, vs. 50% ideal).
/// </summary>
static uint Hash(int position, uint seed)
{
const uint SQ5_BIT_NOISE1 = 0b11010010101010000000101000111111; // 11010010101010000000101000111111
const uint SQ5_BIT_NOISE2 = 0b10101000100001001111000110010111; // 10101000100001001111000110010111
const uint SQ5_BIT_NOISE3 = 0b01101100011100110110111101001011; // 01101100011100110110111101001011
const uint SQ5_BIT_NOISE4 = 0b10110111100111110011101010111011; // 10110111100111110011101010111011
const uint SQ5_BIT_NOISE5 = 0b00011011010101101100010011110101; // 00011011010101101100010011110101
uint mangledBits = (uint)position;
mangledBits *= SQ5_BIT_NOISE1;
mangledBits += seed;
mangledBits ^= mangledBits >> 9;
mangledBits += SQ5_BIT_NOISE2;
mangledBits ^= mangledBits >> 11;
mangledBits *= SQ5_BIT_NOISE3;
mangledBits ^= mangledBits >> 13;
mangledBits += SQ5_BIT_NOISE4;
mangledBits ^= mangledBits >> 15;
mangledBits *= SQ5_BIT_NOISE5;
mangledBits ^= mangledBits >> 17;
return mangledBits;
}
/// <summary> Returns a random float in the range [-1, 1] based on the given 1D position and seed. </summary>
public static float Noise1D(int x, uint seed = 0)
=> (float)(ONE_OVER_MAX_INT * (int)Hash(x, seed));
/// <summary> Returns a random float in the range [-1, 1] based on the given 2D position and seed. </summary>
public static float Noise2D(int x, int y, uint seed = 0)
=> (float)(ONE_OVER_MAX_INT * (int)UInt.Noise2D(x, y, seed));
/// <summary> Returns a random float in the range [-1, 1] based on the given 3D position and seed. </summary>
public static float Noise3D(int x, int y, int z, uint seed = 0)
=> (float)(ONE_OVER_MAX_INT * (int)UInt.Noise3D(x, y, z, seed));
/// <summary> Returns a random float in the range [-1, 1] based on the given 4D position and seed. </summary>
public static float Noise4D(int x, int y, int z, int w, uint seed = 0)
=> (float)(ONE_OVER_MAX_INT * (int)UInt.Noise4D(x, y, z, w, seed));
/// <summary>
/// Variants of Squirrel5 which return an unsigned integer instead of a floating point number.
/// </summary>
public static class UInt
{
/// <summary> Returns a random unsigned integer based on the given 1D position and seed. </summary>
public static uint Noise1D(int x, uint seed = 0)
=> Hash(x, seed);
/// <summary> Returns a random unsigned integer based on the given 2D position and seed. </summary>
public static uint Noise2D(int x, int y, uint seed = 0)
{
const int PRIME_NUMBER = 198491317; // Large prime number with non-boring bits
return Hash(x + PRIME_NUMBER * y, seed);
}
/// <summary> Returns a random unsigned integer based on the given 3D position and seed. </summary>
public static uint Noise3D(int x, int y, int z, uint seed = 0)
{
const int PRIME1 = 198491317; // Large prime number with non-boring bits
const int PRIME2 = 6542989; // Large prime number with distinct and non-boring bits
return Hash(x + PRIME1 * y + PRIME2 * z, seed);
}
/// <summary> Returns a random unsigned integer based on the given 4D position and seed. </summary>
public static uint Noise4D(int x, int y, int z, int w, uint seed = 0)
{
const int PRIME1 = 198491317; // Large prime number with non-boring bits
const int PRIME2 = 6542989; // Large prime number with distinct and non-boring bits
const int PRIME3 = 357239; // Large prime number with distinct and non-boring bits
return Hash(x + PRIME1 * y + PRIME2 * z + PRIME3 * w, seed);
}
}
/// <summary>
/// Variants of Squirrel5 which return a float in the range [0, 1] instead of [-1, 1].
/// </summary>
public static class ZeroToOne
{
/// <summary> Returns a random float in the range [0, 1] based on the given 1D position and seed. </summary>
public static float Noise1D(int x, uint seed = 0)
=> (float)(ONE_OVER_MAX_UINT * Hash(x, seed));
/// <summary> Returns a random float in the range [0, 1] based on the given 2D position and seed. </summary>
public static float Noise2D(int x, int y, uint seed = 0)
=> (float)(ONE_OVER_MAX_UINT * UInt.Noise2D(x, y, seed));
/// <summary> Returns a random float in the range [0, 1] based on the given 3D position and seed. </summary>
public static float Noise3D(int x, int y, int z, uint seed = 0)
=> (float)(ONE_OVER_MAX_UINT * UInt.Noise3D(x, y, z, seed));
/// <summary> Returns a random float in the range [0, 1] based on the given 4D position and seed. </summary>
public static float Noise4D(int x, int y, int z, int w, uint seed = 0)
=> (float)(ONE_OVER_MAX_UINT * UInt.Noise4D(x, y, z, w, seed));
}
}
guid: 1385c6ca61cff6142ad092ca5af430ac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment