· 4 years ago · Aug 17, 2021, 10:32 AM
1//-----------------------------------------------------------------------------------------------
2// SquirrelNoise5.hpp
3// based on GDC 2017 talk "Math for Game Programmers: Noise-Based RNG": https://www.youtube.com/watch?v=LWFzPP8ZbdU
4#pragma once
5
6
7/////////////////////////////////////////////////////////////////////////////////////////////////
8// SquirrelNoise5 - Squirrel's Raw Noise utilities (version 5)
9//
10// This code is made available under the Creative Commons attribution 3.0 license (CC-BY-3.0 US):
11// Attribution in source code comments (even closed-source/commercial code) is sufficient.
12// License summary and text available at: https://creativecommons.org/licenses/by/3.0/us/
13//
14// These noise functions were written by Squirrel Eiserloh as a cheap and simple substitute for
15// the [sometimes awful] bit-noise sample code functions commonly found on the web, many of which
16// are hugely biased or terribly patterned, e.g. having bits which are on (or off) 75% or even
17// 100% of the time (or are excessively overkill/slow for our needs, such as MD5 or SHA).
18//
19// Note: This is work in progress; not all functions have been tested. Use at your own risk.
20// Please report any bugs, issues, or bothersome cases to SquirrelEiserloh at gmail.com.
21//
22// The following functions are all based on a simple bit-noise hash function which returns an
23// unsigned integer containing 32 reasonably-well-scrambled bits, based on a given (signed)
24// integer input parameter (position/index) and [optional] seed. Kind of like looking up a
25// value in an infinitely large [non-existent] table of previously rolled random numbers.
26//
27// These functions are deterministic and random-access / order-independent (i.e. state-free),
28// so they are particularly well-suited for use in smoothed/fractal/simplex/Perlin noise
29// functions and out-of-order (or or-demand) procedural content generation (i.e. that mountain
30// village is the same whether you generated it first or last, ahead of time or just now).
31//
32// The N-dimensional variations simply hash their multidimensional coordinates down to a single
33// 32-bit index and then proceed as usual, so while results are not unique they should
34// (hopefully) not seem locally predictable or repetitive.
35//
36/////////////////////////////////////////////////////////////////////////////////////////////////
37
38
39//-----------------------------------------------------------------------------------------------
40// Raw pseudorandom noise functions (random-access / deterministic). Basis of all other noise.
41//
42constexpr unsigned int Get1dNoiseUint( int index, unsigned int seed=0 );
43constexpr unsigned int Get2dNoiseUint( int indexX, int indexY, unsigned int seed=0 );
44constexpr unsigned int Get3dNoiseUint( int indexX, int indexY, int indexZ, unsigned int seed=0 );
45constexpr unsigned int Get4dNoiseUint( int indexX, int indexY, int indexZ, int indexT, unsigned int seed=0 );
46
47//-----------------------------------------------------------------------------------------------
48// Same functions, mapped to floats in [0,1] for convenience.
49//
50constexpr float Get1dNoiseZeroToOne( int index, unsigned int seed=0 );
51constexpr float Get2dNoiseZeroToOne( int indexX, int indexY, unsigned int seed=0 );
52constexpr float Get3dNoiseZeroToOne( int indexX, int indexY, int indexZ, unsigned int seed=0 );
53constexpr float Get4dNoiseZeroToOne( int indexX, int indexY, int indexZ, int indexT, unsigned int seed=0 );
54
55//-----------------------------------------------------------------------------------------------
56// Same functions, mapped to floats in [-1,1] for convenience.
57//
58constexpr float Get1dNoiseNegOneToOne( int index, unsigned int seed=0 );
59constexpr float Get2dNoiseNegOneToOne( int indexX, int indexY, unsigned int seed=0 );
60constexpr float Get3dNoiseNegOneToOne( int indexX, int indexY, int indexZ, unsigned int seed=0 );
61constexpr float Get4dNoiseNegOneToOne( int indexX, int indexY, int indexZ, int indexT, unsigned int seed=0 );
62
63
64/////////////////////////////////////////////////////////////////////////////////////////////////
65// Inline function definitions below
66/////////////////////////////////////////////////////////////////////////////////////////////////
67
68//-----------------------------------------------------------------------------------------------
69// Fast hash of an int32 into a different (unrecognizable) uint32.
70//
71// Returns an unsigned integer containing 32 reasonably-well-scrambled bits, based on the hash
72// of a given (signed) integer input parameter (position/index) and [optional] seed. Kind of
73// like looking up a value in an infinitely large table of previously generated random numbers.
74//
75// I call this particular approach SquirrelNoise5 (5th iteration of my 1D raw noise function).
76//
77// Many thanks to Peter Schmidt-Nielsen whose outstanding analysis helped identify a weakness
78// in the SquirrelNoise3 code I originally used in my GDC 2017 talk, "Noise-based RNG".
79// Version 5 avoids a noise repetition found in version 3 at extremely high position values
80// caused by a lack of influence by some of the high input bits onto some of the low output bits.
81//
82// The revised SquirrelNoise5 function ensures all input bits affect all output bits, and to
83// (for me) a statistically acceptable degree. I believe the worst-case here is in the amount
84// of influence input position bit #30 has on output noise bit #0 (49.99%, vs. 50% ideal).
85//
86constexpr unsigned int SquirrelNoise5( int positionX, unsigned int seed )
87{
88 constexpr unsigned int SQ5_BIT_NOISE1 = 0xd2a80a3f; // 11010010101010000000101000111111
89 constexpr unsigned int SQ5_BIT_NOISE2 = 0xa884f197; // 10101000100001001111000110010111
90 constexpr unsigned int SQ5_BIT_NOISE3 = 0x6C736F4B; // 01101100011100110110111101001011
91 constexpr unsigned int SQ5_BIT_NOISE4 = 0xB79F3ABB; // 10110111100111110011101010111011
92 constexpr unsigned int SQ5_BIT_NOISE5 = 0x1b56c4f5; // 00011011010101101100010011110101
93
94 unsigned int mangledBits = (unsigned int) positionX;
95 mangledBits *= SQ5_BIT_NOISE1;
96 mangledBits += seed;
97 mangledBits ^= (mangledBits >> 9);
98 mangledBits += SQ5_BIT_NOISE2;
99 mangledBits ^= (mangledBits >> 11);
100 mangledBits *= SQ5_BIT_NOISE3;
101 mangledBits ^= (mangledBits >> 13);
102 mangledBits += SQ5_BIT_NOISE4;
103 mangledBits ^= (mangledBits >> 15);
104 mangledBits *= SQ5_BIT_NOISE5;
105 mangledBits ^= (mangledBits >> 17);
106 return mangledBits;
107}
108
109
110//------------------------------------------------------------------------------------------------
111constexpr unsigned int Get1dNoiseUint( int positionX, unsigned int seed )
112{
113 return SquirrelNoise5( positionX, seed );
114}
115
116
117//-----------------------------------------------------------------------------------------------
118constexpr unsigned int Get2dNoiseUint( int indexX, int indexY, unsigned int seed )
119{
120 constexpr int PRIME_NUMBER = 198491317; // Large prime number with non-boring bits
121 return SquirrelNoise5( indexX + (PRIME_NUMBER * indexY), seed );
122}
123
124//-----------------------------------------------------------------------------------------------
125constexpr unsigned int Get3dNoiseUint( int indexX, int indexY, int indexZ, unsigned int seed )
126{
127 constexpr int PRIME1 = 198491317; // Large prime number with non-boring bits
128 constexpr int PRIME2 = 6542989; // Large prime number with distinct and non-boring bits
129 return SquirrelNoise5( indexX + (PRIME1 * indexY) + (PRIME2 * indexZ), seed );
130}
131
132//-----------------------------------------------------------------------------------------------
133constexpr unsigned int Get4dNoiseUint( int indexX, int indexY, int indexZ, int indexT, unsigned int seed )
134{
135 constexpr int PRIME1 = 198491317; // Large prime number with non-boring bits
136 constexpr int PRIME2 = 6542989; // Large prime number with distinct and non-boring bits
137 constexpr int PRIME3 = 357239; // Large prime number with distinct and non-boring bits
138 return SquirrelNoise5( indexX + (PRIME1 * indexY) + (PRIME2 * indexZ) + (PRIME3 * indexT), seed );
139}
140
141//-----------------------------------------------------------------------------------------------
142constexpr float Get1dNoiseZeroToOne( int index, unsigned int seed )
143{
144 constexpr double ONE_OVER_MAX_UINT = (1.0 / (double) 0xFFFFFFFF);
145 return (float)( ONE_OVER_MAX_UINT * (double) SquirrelNoise5( index, seed ) );
146}
147
148//-----------------------------------------------------------------------------------------------
149constexpr float Get2dNoiseZeroToOne( int indexX, int indexY, unsigned int seed )
150{
151 constexpr double ONE_OVER_MAX_UINT = (1.0 / (double) 0xFFFFFFFF);
152 return (float)( ONE_OVER_MAX_UINT * (double) Get2dNoiseUint( indexX, indexY, seed ) );
153}
154
155//-----------------------------------------------------------------------------------------------
156constexpr float Get3dNoiseZeroToOne( int indexX, int indexY, int indexZ, unsigned int seed )
157{
158 constexpr double ONE_OVER_MAX_UINT = (1.0 / (double) 0xFFFFFFFF);
159 return (float)( ONE_OVER_MAX_UINT * (double) Get3dNoiseUint( indexX, indexY, indexZ, seed ) );
160}
161
162//-----------------------------------------------------------------------------------------------
163constexpr float Get4dNoiseZeroToOne( int indexX, int indexY, int indexZ, int indexT, unsigned int seed )
164{
165 constexpr double ONE_OVER_MAX_UINT = (1.0 / (double) 0xFFFFFFFF);
166 return (float)( ONE_OVER_MAX_UINT * (double) Get4dNoiseUint( indexX, indexY, indexZ, indexT, seed ) );
167}
168
169
170//-----------------------------------------------------------------------------------------------
171constexpr float Get1dNoiseNegOneToOne( int index, unsigned int seed )
172{
173 constexpr double ONE_OVER_MAX_INT = (1.0 / (double) 0x7FFFFFFF);
174 return (float)( ONE_OVER_MAX_INT * (double) (int) SquirrelNoise5( index, seed ) );
175}
176
177
178//-----------------------------------------------------------------------------------------------
179constexpr float Get2dNoiseNegOneToOne( int indexX, int indexY, unsigned int seed )
180{
181 constexpr double ONE_OVER_MAX_INT = (1.0 / (double) 0x7FFFFFFF);
182 return (float)( ONE_OVER_MAX_INT * (double) (int) Get2dNoiseUint( indexX, indexY, seed ) );
183}
184
185
186//-----------------------------------------------------------------------------------------------
187constexpr float Get3dNoiseNegOneToOne( int indexX, int indexY, int indexZ, unsigned int seed )
188{
189 constexpr double ONE_OVER_MAX_INT = (1.0 / (double) 0x7FFFFFFF);
190 return (float)( ONE_OVER_MAX_INT * (double) (int) Get3dNoiseUint( indexX, indexY, indexZ, seed ) );
191}
192
193
194//-----------------------------------------------------------------------------------------------
195constexpr float Get4dNoiseNegOneToOne( int indexX, int indexY, int indexZ, int indexT, unsigned int seed )
196{
197 constexpr double ONE_OVER_MAX_INT = (1.0 / (double) 0x7FFFFFFF);
198 return (float)( ONE_OVER_MAX_INT * (double) (int) Get4dNoiseUint( indexX, indexY, indexZ, indexT, seed ) );
199}