Smooth 2D Noise Viewer
Perlin and Value Noise
perlin.cpp
Go to the documentation of this file.
1
4
5// MIT License
6//
7// Copyright (c) 2022 Ian Parberry
8//
9// Permission is hereby granted, free of charge, to any person obtaining a copy
10// of this software and associated documentation files (the "Software"), to
11// deal in the Software without restriction, including without limitation the
12// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
13// sell copies of the Software, and to permit persons to whom the Software is
14// furnished to do so, subject to the following conditions:
15//
16// The above copyright notice and this permission notice shall be included in
17// all copies or substantial portions of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25// IN THE SOFTWARE.
26
27#include <stdlib.h>
28#include <algorithm>
29#include <functional>
30
31#include "Perlin.h"
32#include "Helpers.h"
33#include "Includes.h"
34
36// Constructor and destructor.
37
38#pragma region Constructor and destructor
39
41
43 SetSeed();
44 Initialize();
45} //constructor
46
48
50 delete [] m_fTable;
51 delete [] m_nPerm;
52} //destructor
53
57
59 assert(isPowerOf2(m_nSize)); //safety
60 assert(m_nSize > 1); //safety
61
62 m_nMask = m_nSize - 1; //mask of n consecutive 1s
63 m_nPerm = new size_t[m_nSize]; //permutation
64 m_fTable = new float[m_nSize]; //gradients or height values
65
66 RandomizeTable(m_eDistribution); //randomize gradient/value table
67 RandomizePermutation(); //randomize permutation
68} //Initialize
69
70#pragma endregion Constructor and destructor
71
73// Functions that change noise settings.
74
75#pragma region Functions that change noise settings
76
85
87 for(size_t i=0; i<m_nSize; i++) //identity permutation
88 m_nPerm[i] = i;
89
90 m_stdRandom.seed(m_nSeed); //reset PRNG
91
92 for(size_t i=0; i<m_nSize; i++){ //randomize
93 std::uniform_int_distribution<size_t> d(i, m_nSize - 1);
94 std::swap(m_nPerm[i], m_nPerm[d(m_stdRandom)]);
95 } //for
96} //RandomizePermutation
97
115
116void CPerlinNoise2D::RandomizeTableMidpoint(size_t i, size_t j, float alpha){
117 assert(i < j && j < m_nSize);
118 assert(alpha < 0.0f);
119
120 std::uniform_real_distribution<float> d(-1.0f, 1.0f);
121
122 if(j > i + 1){ //there is a midpoint to fill in
123 const size_t mid = (i + j)/2; //mid point
124
125 assert(i < mid && mid < j);
126
127 const float fMean = (m_fTable[i] + m_fTable[j])/2.0f; //average of ends
128 const float fRand = alpha*d(m_stdRandom); //random offset
129 m_fTable[mid] = clamp(-1.0f, fMean + fRand, 1.0f); //mid point is average plus offset
130 alpha *= 0.5f; //increase lacunarity
131
132 RandomizeTableMidpoint(i, mid, alpha); //recurse on first half
133 RandomizeTableMidpoint(mid, j, alpha); //recurse on second half
134 } //if
135} //RandomizeTableMidpoint
136
140
142 m_fTable[0] = 1.0f;
143 m_fTable[m_nSize - 1] = -1.0f;
144
145 RandomizeTableMidpoint(0, m_nSize - 1, 0.5f);
146} //RandomizeTableMidpoint
147
151
153 std::uniform_real_distribution<float> d(-1.0f, 1.0f);
154
155 for(size_t i=0; i<m_nSize; i++){
156 m_fTable[i] = d(m_stdRandom);
157 assert(-1.0f <= m_fTable[i] && m_fTable[i] <= 1.0f);
158 } //for
159} //RandomizeTableUniform
160
165
167 std::uniform_real_distribution<float> d(-1.0f, 1.0f);
168
169 for(size_t i=0; i<m_nSize; i++){
170 m_fTable[i] = (d(m_stdRandom) > 0.0f)? 1.0f: -1.0f;
171 assert(-1.0f <= m_fTable[i] && m_fTable[i] <= 1.0f);
172 } //for
173} //RandomizeTableMaximal
174
178
180 std::normal_distribution<float> d(500.0f, 200.0f);
181
182 for(size_t i=0; i<m_nSize; i++){
183 m_fTable[i] = 2.0f*clamp(0.0f, d(m_stdRandom)/1000.0f, 1.0f) - 1.0f;
184 assert(-1.0f <= m_fTable[i] && m_fTable[i] <= 1.0f);
185 } //for
186} //RandomizeTableNormal
187
193
195 std::uniform_real_distribution<float> d(0.0f, 1.0f);
196
197 for(size_t i=0; i<m_nSize; i++){
198 m_fTable[i] = cosf(PI*d(m_stdRandom));
199 assert(-1.0f <= m_fTable[i] && m_fTable[i] <= 1.0f);
200 } //for
201} //RandomizeTableCos
202
207
209 std::exponential_distribution<float> d(4.0f);
210
211 const size_t half = m_nSize/2;
212
213 for(size_t i=0; i<half; i++) //positive values
214 m_fTable[i] = clamp(0.0f, d(m_stdRandom), 1.0f);
215
216 for(size_t i=half; i<m_nSize; i++) //negative values
217 m_fTable[i] = -clamp(0.0f, d(m_stdRandom), 1.0f);
218} //RandomizeTableExp
219
226
228 m_stdRandom.seed(m_nSeed); //reset PRNG
229 m_eDistribution = d; //current distribution
230
231 switch(d){
232 case eDistribution::Uniform: RandomizeTableUniform(); break;
233 case eDistribution::Maximal: RandomizeTableMaximal(); break;
234 case eDistribution::Cosine: RandomizeTableCos(); break;
235 case eDistribution::Normal: RandomizeTableNormal(); break;
236 case eDistribution::Exponential: RandomizeTableExp(); break;
237 case eDistribution::Midpoint: RandomizeTableMidpoint(); break;
238 } //switch
239} //RandomizeTable
240
244
247 delete [] m_fTable;
248 delete [] m_nPerm;
249
250 m_nSize *= 2; //size must be a power of 2
251 Initialize();
252 return true;
253 } //if
254
255 return false;
256} //DoubleTableSize
257
261
264 delete [] m_fTable;
265 delete [] m_nPerm;
266
267 m_nSize /= 2; //size must be a power of 2
268 Initialize();
269 return true;
270 } //if
271
272 return false;
273} //HalveTableSize
274
277
280 delete [] m_fTable;
281 delete [] m_nPerm;
282
284 Initialize();
285 return true;
286 } //if
287
288 return false;
289} //DefaultTableSize
290
294
296 m_nSeed = timeGetTime();
297} //SetSeed
298
301
303 m_eSpline = d;
304} //SetSpline
305
308
310 m_eHash = d;
311} //SetHash
312
313#pragma endregion Functions that change noise settings
314
316// Helper functions.
317
318#pragma region Helper functions
319
324
325inline const float CPerlinNoise2D::spline(float x) const{
326 assert(-1.0f <= x && x <= 1.0f);
327
328 float fResult = 0.0f;
329
330 switch(m_eSpline){
331 case eSpline::None: fResult = x; break;
332 case eSpline::Cubic: fResult = spline3(x); break;
333 case eSpline::Quintic: fResult = spline5(x); break;
334 } //switch
335
336 assert(-1.0f <= fResult && fResult <= 1.0f);
337
338 return fResult;
339} //spline
340
345
346inline const size_t CPerlinNoise2D::pair(size_t x, size_t y) const{
347 return hash(x) + y;
348} //pair
349
357
358inline const size_t CPerlinNoise2D::pairstd(size_t x, size_t y) const{
359 return (std::hash<size_t>{}(x) << 1) ^ std::hash<size_t>{}(y);
360} //pairstd
361
366
367inline const size_t CPerlinNoise2D::hash(size_t x) const{
368 return m_nPerm[x & m_nMask];
369} //hash
370
376
377inline const size_t CPerlinNoise2D::hashstd(size_t x) const{
378 return std::hash<size_t>{}(x) & m_nMask;
379} //hashstd
380
388
389inline const size_t CPerlinNoise2D::hash2(size_t x, size_t y) const{
390 const uint64_t p0 = 11903454645187951493LL; //a prime
391 const uint64_t p1 = 2078231835154824277LL; //another prime
392 const uint64_t p2 = 5719147207009855033LL; //another prime
393
394 const uint64_t h = (p0*(uint64_t)x + p1*(uint64_t)y)%p2; //hash
395
396 return size_t(h >> 8) & m_nMask; //shift and mask
397} //hash2
398
403
404void CPerlinNoise2D::HashCorners(size_t x, size_t y, size_t c[4]) const{
405 switch(m_eHash){
406 case eHash::Permutation:
407 c[0] = hash(pair(x, y)); c[1] = hash(pair(x + 1, y));
408 c[2] = hash(pair(x, y + 1)); c[3] = hash(pair(x + 1, y + 1));
409 break;
410
411 case eHash::LinearCongruential:
412 c[0] = hash2(x, y); c[1] = hash2(x + 1, y);
413 c[2] = hash2(x, y + 1); c[3] = hash2(x + 1, y + 1);
414 break;
415
416 case eHash::Std:
417 c[0] = hashstd(pairstd(x, y)); c[1] = hashstd(pairstd(x + 1, y));
418 c[2] = hashstd(pairstd(x, y + 1)); c[3] = hashstd(pairstd(x + 1, y + 1));
419 break;
420 } //switch
421} //HashCorners
422
434
435inline const float CPerlinNoise2D::z(size_t h, float x, float y, eNoise t) const{
436 assert(-1.0f <= x && x <= 1.0f);
437 assert(-1.0f <= y && y <= 1.0f);
438 assert(h == (h & m_nMask));
439
440 float result = 0; //return result
441
442 switch(t){ //noise type
443 case eNoise::Perlin:
444 result = x*m_fTable[h] + y*m_fTable[hash(h)]; //gradient times position
445 assert(-2.0f <= result && result <= 2.0f);
446 break;
447
448 case eNoise::Value:
449 result = m_fTable[h]; //get value directly from table
450 assert(-1.0f <= result && result <= 1.0f);
451 break;
452 } //switch
453
454 return result;
455} //z
456
465
466const float CPerlinNoise2D::Lerp(float sX, float fX, float fY, size_t* c,
467 eNoise t) const
468{
469 assert(-1.0f <= sX && sX <= 1.0f);
470 assert( 0.0f <= fX && fX <= 1.0f);
471 assert(-1.0f <= fY && fY <= 1.0f);
472
473 const float result = lerp(sX, z(c[0], fX, fY, t), z(c[1], fX - 1, fY, t));
474
475 switch(t){ //noise type
476 case eNoise::Perlin:
477 //the worst case in the lerp above is fX and fX - 1 have the same
478 //magnitude, that is, fX == 0.5f, in which case each z gets 1.0f from fY
479 //and 0.5f from fX.
480 assert(-1.5f <= result && result <= 1.5f);
481 return result;
482 break;
483
484 case eNoise::Value:
485 assert(-1.0f <= result && result <= 1.0f);
486 return result;
487 break;
488
489 default: return 0.0f;
490 } //switch
491} //Lerp
492
493#pragma endregion Helper functions
494
496// Noise generation functions.
497
498#pragma region Noise generation functions
499
505
506const float CPerlinNoise2D::noise(float x, float y, eNoise t) const{
507 const size_t nX = (size_t)floorf(x); //integer part of x
508 const size_t nY = (size_t)floorf(y); //integer part of y
509
510 const float fX = x - floorf(x); //fractional part of x
511 const float fY = y - floorf(y); //fractional part of y
512
513 //smooth fractional parts of x and y using spline curves
514
515 const float sX = spline(fX); //apply spline curve to fractional part of x
516 const float sY = spline(fY); //apply spline curve to fractional part of y
517
518 //hash value at corners of enclosing grid square with integer coordinates
519
520 size_t c[4] = {0}; //for hashed values at corners
521 HashCorners(nX, nY, c); //get hashed values at corners
522
523 //lerp along the top and bottom along the X-axis
524
525 const float a = Lerp(sX, fX, fY, c, t);
526 const float b = Lerp(sX, fX, fY - 1, &(c[2]), t);
527
528 //now lerp these values along the Y-axis
529
530 const float result = lerp(sY, a, b);
531 assert(-1.0f <= result && result <= 1.0f);
532 return result;
533} //noise
534
547
548const float CPerlinNoise2D::generate(float x, float y, eNoise t, size_t n,
549 float alpha, float beta) const
550{
551 assert(0.0f <= alpha && alpha < 1.0f);
552 assert(beta > 1.0f);
553
554 float sum = 0.0f; //for result
555 float amplitude = 1.0f; //octave amplitude
556
557 for(size_t i=0; i<n; i++){ //for each octave
558 sum += amplitude*noise(x, y, t); //scale noise by amplitude
559 amplitude *= alpha; //reduce amplitude by lacunarity
560 x *= beta; y *= beta; //multiply frequency by persistence
561 } //for
562
563 assert(amplitude == powf(alpha, (float)n));
564
565 float result = (1 - alpha)*sum/(1 - amplitude); //sum of geometric progression
566 if(t == eNoise::Perlin)result *= 4.0f/3.0f; //scale up Perlin noise
567 assert(-1.0f <= result && result <= 1.0f); //safety
568 return result;
569} //generate
570
571#pragma endregion Noise generation functions
572
574// Reader functions.
575
576#pragma region Reader functions
577
580
581const size_t CPerlinNoise2D::GetTableSize() const{
582 return m_nSize;
583} //GetTableSize
584
587
589 return m_nMinTableSize;
590} //GetMinTableSize
591
594
596 return m_nMaxTableSize;
597} //GetMaxTableSize
598
601
603 return m_nDefTableSize;
604} //GetDefTableSize
605
608
610 return m_eHash;
611} //GetHash
612
615
617 return m_eSpline;
618} //GetSpline
619
622
624 return m_eDistribution;
625} //GetDistribution
626
627#pragma endregion Reader functions
eDistribution
Distribution.
Definition: Defines.h:54
eSpline
Spline type.
Definition: Defines.h:62
eHash
Hash function type.
Definition: Defines.h:46
const float PI
Pi.
Definition: Defines.h:32
eNoise
Perlin noise type.
Definition: Defines.h:38
const float lerp(float t, float a, float b)
Linear interpolation.
Definition: Helpers.cpp:56
const float spline5(float t)
Quintic spline.
Definition: Helpers.cpp:45
const bool isPowerOf2(size_t n)
Power of 2 test.
Definition: Helpers.cpp:94
const float spline3(float t)
Cubic spline.
Definition: Helpers.cpp:36
const float clamp(float a, float x, float b)
Clamp between two values.
Definition: Helpers.cpp:67
Interface for helper functions.
Useful includes.
const size_t GetMinTableSize() const
Get minimum table size.
Definition: perlin.cpp:588
const size_t m_nDefTableSize
Default table size.
Definition: perlin.h:60
CPerlinNoise2D()
Constructor.
Definition: perlin.cpp:42
const size_t hashstd(size_t) const
std::hash function.
Definition: perlin.cpp:377
void RandomizeTableExp()
Randomize table using exponential distribution.
Definition: perlin.cpp:208
void RandomizeTableMidpoint()
Randomize table using midpoint displacement.
Definition: perlin.cpp:141
const float z(size_t, float, float, eNoise) const
Apply gradients.
Definition: perlin.cpp:435
const size_t pair(size_t, size_t) const
Perlin pairing function.
Definition: perlin.cpp:346
const size_t m_nMinTableSize
Min table size.
Definition: perlin.h:61
eDistribution m_eDistribution
Uniform distribution..
Definition: perlin.h:52
const float noise(float, float, eNoise) const
Perlin noise.
Definition: perlin.cpp:506
const eSpline GetSpline() const
Get spline function type.
Definition: perlin.cpp:616
void SetHash(eHash)
Set hash function.
Definition: perlin.cpp:309
eHash m_eHash
Hash function type.
Definition: perlin.h:50
void RandomizeTableUniform()
Randomize table using uniform distribution.
Definition: perlin.cpp:152
float * m_fTable
Table of gradients or values.
Definition: perlin.h:55
const size_t GetMaxTableSize() const
Get maximum table size.
Definition: perlin.cpp:595
const size_t hash2(size_t, size_t) const
Hash function.
Definition: perlin.cpp:389
const size_t m_nMaxTableSize
Max table size.
Definition: perlin.h:62
size_t m_nSize
Table size, must be a power of 2.
Definition: perlin.h:64
const size_t GetTableSize() const
Get table size.
Definition: perlin.cpp:581
const size_t pairstd(size_t, size_t) const
Std pairing function.
Definition: perlin.cpp:358
bool DefaultTableSize()
Set table size to default.
Definition: perlin.cpp:278
void HashCorners(size_t, size_t, size_t[4]) const
Hash grid corners.
Definition: perlin.cpp:404
void SetSpline(eSpline)
Set spline function.
Definition: perlin.cpp:302
size_t * m_nPerm
Random permutation, used for hash function.
Definition: perlin.h:54
size_t m_nMask
Mask for values less than m_nSize.
Definition: perlin.h:65
eSpline m_eSpline
Spline function type.
Definition: perlin.h:51
const float generate(float, float, eNoise, size_t, float=0.5f, float=2.0f) const
Generate noise at a point.
Definition: perlin.cpp:548
void RandomizeTableMaximal()
Randomize table using large magnitude values.
Definition: perlin.cpp:166
void RandomizeTableNormal()
Randomize table using normal distribution.
Definition: perlin.cpp:179
std::default_random_engine m_stdRandom
PRNG.
Definition: perlin.h:57
void RandomizeTable(eDistribution)
Randomize table from distribution.
Definition: perlin.cpp:227
~CPerlinNoise2D()
Destructor.
Definition: perlin.cpp:49
const float Lerp(float, float, float, size_t *, eNoise) const
Linear interpolation.
Definition: perlin.cpp:466
const eHash GetHash() const
Get hash function type.
Definition: perlin.cpp:609
bool DoubleTableSize()
Double table size.
Definition: perlin.cpp:245
const eDistribution GetDistribution() const
Get distribution type.
Definition: perlin.cpp:623
bool HalveTableSize()
Halve table size.
Definition: perlin.cpp:262
UINT m_nSeed
PRNG seed.
Definition: perlin.h:58
const size_t hash(size_t) const
Perlin hash function.
Definition: perlin.cpp:367
const size_t GetDefTableSize() const
Get default table size.
Definition: perlin.cpp:602
void SetSeed()
Set seed for PRNG.
Definition: perlin.cpp:295
void RandomizePermutation()
Randomize permutation.
Definition: perlin.cpp:86
const float spline(float) const
Spline curve.
Definition: perlin.cpp:325
void Initialize()
Initialize.
Definition: perlin.cpp:58
void RandomizeTableCos()
Randomize table using cosine.
Definition: perlin.cpp:194
Interface for the Perlin and Value noise generators.