parent
75cd9d66a0
commit
e7b0e06c11
@ -0,0 +1,298 @@ |
||||
/*
|
||||
OneLoneCoder.com - What Is Perlin Noise? |
||||
"Mountains, Clouds, Worms Landscapes?" - @Javidx9 |
||||
|
||||
Disclaimer |
||||
~~~~~~~~~~ |
||||
I don't care what you use this for. It's intended to be educational, and perhaps |
||||
to the oddly minded - a little bit of fun. Please hack this, change it and use it |
||||
in any way you see fit. BUT, you acknowledge that I am not responsible for anything |
||||
bad that happens as a result of your actions. However, if good stuff happens, I |
||||
would appreciate a shout out, or at least give the blog some publicity for me. |
||||
Cheers! |
||||
|
||||
Background |
||||
~~~~~~~~~~ |
||||
Noise is random - a bad thing when trying to procedurally generate content. Perlin |
||||
noise adds coherence at varying spatial scales which create natural looking noise |
||||
arrays. Perlin noise can be further processed into all sorts of assets, such as |
||||
mountains, maps, rooms, textures, data sets. |
||||
|
||||
|
||||
Author |
||||
~~~~~~ |
||||
Twitter: @javidx9 |
||||
Blog: www.onelonecoder.com |
||||
|
||||
Video: |
||||
~~~~~~ |
||||
https://youtu.be/6-0UaeJBumA
|
||||
|
||||
Last Updated: 29/10/2017 |
||||
*/ |
||||
|
||||
#include <iostream> |
||||
#include <string> |
||||
#include <algorithm> |
||||
using namespace std; |
||||
|
||||
#include "olcConsoleGameEngine.h" |
||||
|
||||
class OneLoneCoder_PerlinNoiseDemo : public olcConsoleGameEngine |
||||
{ |
||||
public: |
||||
OneLoneCoder_PerlinNoiseDemo() |
||||
{ |
||||
m_sAppName = L"Perlin Noise"; |
||||
} |
||||
|
||||
private: |
||||
// 2D noise variables
|
||||
int nOutputWidth = 256; |
||||
int nOutputHeight = 256; |
||||
float *fNoiseSeed2D = nullptr; |
||||
float *fPerlinNoise2D = nullptr; |
||||
|
||||
// 1D noise variables
|
||||
float *fNoiseSeed1D = nullptr; |
||||
float *fPerlinNoise1D = nullptr; |
||||
int nOutputSize = 256; |
||||
|
||||
|
||||
int nOctaveCount = 1; |
||||
float fScalingBias = 2.0f; |
||||
int nMode = 1; |
||||
|
||||
|
||||
virtual bool OnUserCreate() |
||||
{ |
||||
nOutputWidth = ScreenWidth(); |
||||
nOutputHeight = ScreenHeight(); |
||||
|
||||
fNoiseSeed2D = new float[nOutputWidth * nOutputHeight]; |
||||
fPerlinNoise2D = new float[nOutputWidth * nOutputHeight]; |
||||
for (int i = 0; i < nOutputWidth * nOutputHeight; i++) fNoiseSeed2D[i] = (float)rand() / (float)RAND_MAX; |
||||
|
||||
nOutputSize = ScreenWidth(); |
||||
fNoiseSeed1D = new float[nOutputSize]; |
||||
fPerlinNoise1D = new float[nOutputSize]; |
||||
for (int i = 0; i < nOutputSize; i++) fNoiseSeed1D[i] = (float)rand() / (float)RAND_MAX; |
||||
|
||||
return true; |
||||
} |
||||
|
||||
virtual bool OnUserUpdate(float fElapsedTime) |
||||
{ |
||||
Fill(0, 0, ScreenWidth(), ScreenHeight(), L' '); |
||||
|
||||
if (m_keys[VK_SPACE].bReleased) |
||||
nOctaveCount++; |
||||
|
||||
if (m_keys[L'1'].bReleased) |
||||
nMode = 1; |
||||
|
||||
if (m_keys[L'2'].bReleased) |
||||
nMode = 2; |
||||
|
||||
if (m_keys[L'3'].bReleased) |
||||
nMode = 3; |
||||
|
||||
if (m_keys[L'Q'].bReleased) |
||||
fScalingBias += 0.2f; |
||||
|
||||
if (m_keys[L'A'].bReleased) |
||||
fScalingBias -= 0.2f; |
||||
|
||||
if (fScalingBias < 0.2f) |
||||
fScalingBias = 0.2f; |
||||
|
||||
if (nOctaveCount == 9) |
||||
nOctaveCount = 1; |
||||
|
||||
if (nMode == 1) // 1D Noise
|
||||
{ |
||||
if (m_keys[L'Z'].bReleased) // Noise Between 0 and +1
|
||||
for (int i = 0; i < nOutputSize; i++) fNoiseSeed1D[i] = (float)rand() / (float)RAND_MAX; |
||||
|
||||
if (m_keys[L'X'].bReleased) // Noise Between -1 and +1
|
||||
for (int i = 0; i < nOutputSize; i++) fNoiseSeed1D[i] = 2.0f * ((float)rand() / (float)RAND_MAX) - 1.0f; |
||||
|
||||
PerlinNoise1D(nOutputSize, fNoiseSeed1D, nOctaveCount, fScalingBias, fPerlinNoise1D); |
||||
|
||||
for (int x = 0; x < nOutputSize; x++) |
||||
{ |
||||
int y = -(fPerlinNoise1D[x] * (float)ScreenHeight() / 2.0f) + (float)ScreenHeight() / 2.0f; |
||||
if (y < ScreenHeight() / 2) |
||||
for (int f = y; f < ScreenHeight() / 2; f++) |
||||
Draw(x, f, PIXEL_SOLID, FG_GREEN); |
||||
else |
||||
for (int f = ScreenHeight() / 2; f <= y; f++) |
||||
Draw(x, f, PIXEL_SOLID, FG_RED); |
||||
} |
||||
} |
||||
|
||||
if (nMode == 2) // 2D Noise
|
||||
{ |
||||
if (m_keys[L'Z'].bReleased) // Noise Between 0 and +1
|
||||
for (int i = 0; i < nOutputWidth * nOutputHeight; i++) fNoiseSeed2D[i] = (float)rand() / (float)RAND_MAX; |
||||
|
||||
|
||||
PerlinNoise2D(nOutputWidth, nOutputHeight, fNoiseSeed2D, nOctaveCount, fScalingBias, fPerlinNoise2D); |
||||
|
||||
for (int x = 0; x < nOutputWidth; x++) |
||||
{ |
||||
for (int y = 0; y < nOutputHeight; y++) |
||||
{ |
||||
short bg_col, fg_col; |
||||
wchar_t sym; |
||||
int pixel_bw = (int)(fPerlinNoise2D[y * nOutputWidth + x] * 12.0f); |
||||
switch (pixel_bw) |
||||
{ |
||||
case 0: bg_col = BG_BLACK; fg_col = FG_BLACK; sym = PIXEL_SOLID; break; |
||||
|
||||
case 1: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_QUARTER; break; |
||||
case 2: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_HALF; break; |
||||
case 3: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_THREEQUARTERS; break; |
||||
case 4: bg_col = BG_BLACK; fg_col = FG_DARK_GREY; sym = PIXEL_SOLID; break; |
||||
|
||||
case 5: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_QUARTER; break; |
||||
case 6: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_HALF; break; |
||||
case 7: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_THREEQUARTERS; break; |
||||
case 8: bg_col = BG_DARK_GREY; fg_col = FG_GREY; sym = PIXEL_SOLID; break; |
||||
|
||||
case 9: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_QUARTER; break; |
||||
case 10: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_HALF; break; |
||||
case 11: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_THREEQUARTERS; break; |
||||
case 12: bg_col = BG_GREY; fg_col = FG_WHITE; sym = PIXEL_SOLID; break; |
||||
} |
||||
|
||||
Draw(x, y, sym, fg_col | bg_col); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (nMode == 3) // 2D Noise - colourised
|
||||
{ |
||||
if (m_keys[L'Z'].bReleased) // Noise Between 0 and +1
|
||||
for (int i = 0; i < nOutputWidth * nOutputHeight; i++) fNoiseSeed2D[i] = (float)rand() / (float)RAND_MAX; |
||||
|
||||
|
||||
PerlinNoise2D(nOutputWidth, nOutputHeight, fNoiseSeed2D, nOctaveCount, fScalingBias, fPerlinNoise2D); |
||||
|
||||
for (int x = 0; x < nOutputWidth; x++) |
||||
{ |
||||
for (int y = 0; y < nOutputHeight; y++) |
||||
{ |
||||
short bg_col, fg_col; |
||||
wchar_t sym; |
||||
int pixel_bw = (int)(fPerlinNoise2D[y * nOutputWidth + x] * 16.0f); |
||||
switch (pixel_bw) |
||||
{ |
||||
case 0: bg_col = BG_DARK_BLUE; fg_col = FG_DARK_BLUE; sym = PIXEL_SOLID; break; |
||||
|
||||
case 1: bg_col = BG_DARK_BLUE; fg_col = FG_BLUE; sym = PIXEL_QUARTER; break; |
||||
case 2: bg_col = BG_DARK_BLUE; fg_col = FG_BLUE; sym = PIXEL_HALF; break; |
||||
case 3: bg_col = BG_DARK_BLUE; fg_col = FG_BLUE; sym = PIXEL_THREEQUARTERS; break; |
||||
case 4: bg_col = BG_DARK_BLUE; fg_col = FG_BLUE; sym = PIXEL_SOLID; break; |
||||
|
||||
case 5: bg_col = BG_BLUE; fg_col = FG_GREEN; sym = PIXEL_QUARTER; break; |
||||
case 6: bg_col = BG_BLUE; fg_col = FG_GREEN; sym = PIXEL_HALF; break; |
||||
case 7: bg_col = BG_BLUE; fg_col = FG_GREEN; sym = PIXEL_THREEQUARTERS; break; |
||||
case 8: bg_col = BG_BLUE; fg_col = FG_GREEN; sym = PIXEL_SOLID; break; |
||||
|
||||
case 9: bg_col = BG_GREEN; fg_col = FG_DARK_GREY; sym = PIXEL_QUARTER; break; |
||||
case 10: bg_col = BG_GREEN; fg_col = FG_DARK_GREY; sym = PIXEL_HALF; break; |
||||
case 11: bg_col = BG_GREEN; fg_col = FG_DARK_GREY; sym = PIXEL_THREEQUARTERS; break; |
||||
case 12: bg_col = BG_GREEN; fg_col = FG_DARK_GREY; sym = PIXEL_SOLID; break; |
||||
|
||||
case 13: bg_col = BG_DARK_GREY; fg_col = FG_WHITE; sym = PIXEL_QUARTER; break; |
||||
case 14: bg_col = BG_DARK_GREY; fg_col = FG_WHITE; sym = PIXEL_HALF; break; |
||||
case 15: bg_col = BG_DARK_GREY; fg_col = FG_WHITE; sym = PIXEL_THREEQUARTERS; break; |
||||
case 16: bg_col = BG_DARK_GREY; fg_col = FG_WHITE; sym = PIXEL_SOLID; break; |
||||
} |
||||
|
||||
Draw(x, y, sym, fg_col | bg_col); |
||||
} |
||||
} |
||||
} |
||||
|
||||
return true; |
||||
} |
||||
|
||||
|
||||
void PerlinNoise1D(int nCount, float *fSeed, int nOctaves, float fBias, float *fOutput) |
||||
{ |
||||
// Used 1D Perlin Noise
|
||||
for (int x = 0; x < nCount; x++) |
||||
{ |
||||
float fNoise = 0.0f; |
||||
float fScaleAcc = 0.0f; |
||||
float fScale = 1.0f; |
||||
|
||||
for (int o = 0; o < nOctaves; o++) |
||||
{ |
||||
int nPitch = nCount >> o; |
||||
int nSample1 = (x / nPitch) * nPitch; |
||||
int nSample2 = (nSample1 + nPitch) % nCount; |
||||
|
||||
float fBlend = (float)(x - nSample1) / (float)nPitch; |
||||
|
||||
float fSample = (1.0f - fBlend) * fSeed[nSample1] + fBlend * fSeed[nSample2]; |
||||
|
||||
fScaleAcc += fScale; |
||||
fNoise += fSample * fScale; |
||||
fScale = fScale / fBias; |
||||
} |
||||
|
||||
// Scale to seed range
|
||||
fOutput[x] = fNoise / fScaleAcc; |
||||
} |
||||
} |
||||
|
||||
void PerlinNoise2D(int nWidth, int nHeight, float *fSeed, int nOctaves, float fBias, float *fOutput) |
||||
{ |
||||
// Used 1D Perlin Noise
|
||||
for (int x = 0; x < nWidth; x++) |
||||
for (int y = 0; y < nHeight; y++) |
||||
{
|
||||
float fNoise = 0.0f; |
||||
float fScaleAcc = 0.0f; |
||||
float fScale = 1.0f; |
||||
|
||||
for (int o = 0; o < nOctaves; o++) |
||||
{ |
||||
int nPitch = nWidth >> o; |
||||
int nSampleX1 = (x / nPitch) * nPitch; |
||||
int nSampleY1 = (y / nPitch) * nPitch; |
||||
|
||||
int nSampleX2 = (nSampleX1 + nPitch) % nWidth;
|
||||
int nSampleY2 = (nSampleY1 + nPitch) % nWidth; |
||||
|
||||
float fBlendX = (float)(x - nSampleX1) / (float)nPitch; |
||||
float fBlendY = (float)(y - nSampleY1) / (float)nPitch; |
||||
|
||||
float fSampleT = (1.0f - fBlendX) * fSeed[nSampleY1 * nWidth + nSampleX1] + fBlendX * fSeed[nSampleY1 * nWidth + nSampleX2]; |
||||
float fSampleB = (1.0f - fBlendX) * fSeed[nSampleY2 * nWidth + nSampleX1] + fBlendX * fSeed[nSampleY2 * nWidth + nSampleX2]; |
||||
|
||||
fScaleAcc += fScale; |
||||
fNoise += (fBlendY * (fSampleB - fSampleT) + fSampleT) * fScale; |
||||
fScale = fScale / fBias; |
||||
} |
||||
|
||||
// Scale to seed range
|
||||
fOutput[y * nWidth + x] = fNoise / fScaleAcc; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
}; |
||||
|
||||
|
||||
int main() |
||||
{ |
||||
OneLoneCoder_PerlinNoiseDemo game; |
||||
game.ConstructConsole(256, 256, 3, 3); |
||||
game.Start(); |
||||
return 0; |
||||
} |
Loading…
Reference in new issue