Created
January 9, 2018 00:45
-
-
Save bit-hack/e8af574fc4c30a7c5f95f025d7e7bf92 to your computer and use it in GitHub Desktop.
Perlin noise generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SDL/SDL.h> | |
#include <array> | |
#include <memory> | |
#include <assert.h> | |
namespace { | |
// min value | |
template <typename type_t> | |
constexpr type_t minv(type_t a, type_t b) | |
{ | |
return (a < b) ? a : b; | |
} | |
// max value | |
template <typename type_t> | |
constexpr type_t maxv(type_t a, type_t b) | |
{ | |
return (a > b) ? a : b; | |
} | |
// clamp value | |
template <typename type_t> | |
constexpr type_t clampv(type_t lo, type_t in, type_t hi) | |
{ | |
return (in < lo) ? lo : ((in > hi) ? hi : in); | |
} | |
} // namespace {} | |
struct perlin_t | |
{ | |
const size_t WIDTH; | |
const size_t AREA; | |
std::unique_ptr<float[]> map; | |
perlin_t(size_t width) | |
: WIDTH(width) | |
, AREA(width*width) | |
, map(new float[AREA]) | |
{ | |
assert(AREA && map); | |
for (size_t i = 0; i<AREA; ++i) | |
map[i] = 0.f; | |
} | |
// generate perlin | |
void generate(uint32_t seed, float smooth, size_t octaves) | |
{ | |
assert(octaves); | |
float lo = 0.f, hi = 0.f; | |
// perlin noise step | |
for (size_t i = 0; i<AREA; ++i) { | |
const float x = float(i%WIDTH); | |
const float y = float(i/WIDTH); | |
const float v = perlin(x, y, seed, smooth, octaves); | |
lo = i==0 ? v : minv(v, lo); | |
hi = i==0 ? v : maxv(v, hi); | |
map[i] = v; | |
} | |
// normalization step | |
const float dc = lo; | |
const float norm = 1.f/(hi-lo); | |
for (size_t i = 0; i<AREA; ++i) { | |
map[i] = (map[i]-dc) * norm; | |
} | |
} | |
protected: | |
// smoothstep lerp | |
float slerp(float a, float b, float i) const | |
{ | |
return a + (b-a) * (i*i*(3-2*i)); | |
} | |
// linear lerp | |
float lerp(float a, float b, float i) const | |
{ | |
return a + (b-a) * i; | |
} | |
// simple falloff | |
float falloff(float x, float r) const | |
{ | |
return clampv(0.f, 1.f-(x*x)/(r*r), 1.f); | |
} | |
// 2d integer rand based on wang hash | |
uint32_t rand2d(uint32_t x, uint32_t y, uint32_t seed) const | |
{ | |
// munge seed, x and y | |
uint32_t z = (x+y*509) * seed; | |
// wang hash below | |
z = (z^61)^(z>>16); | |
z *= 9; | |
z = z^(z>>4); | |
z *= 0x27d4eb2d; | |
z = z^(z>>15); | |
return z; | |
} | |
// interpolated normalized rand2d (0.f -> 1.f) | |
float rand2df(float x, float y, uint32_t seed) const | |
{ | |
const float fx = x-int(x), fy = y-int(y); | |
const uint32_t ix0 = uint32_t(x), ix1 = uint32_t(x+1); | |
const uint32_t iy0 = uint32_t(y), iy1 = uint32_t(y+1); | |
const float q00 = float(rand2d(ix0, iy0, seed)&0xfff); | |
const float q10 = float(rand2d(ix1, iy0, seed)&0xfff); | |
const float q01 = float(rand2d(ix0, iy1, seed)&0xfff); | |
const float q11 = float(rand2d(ix1, iy1, seed)&0xfff); | |
const float j0 = slerp(q00, q10, fx); | |
const float j1 = slerp(q01, q11, fx); | |
return slerp(j0, j1, fy)/4096.f; | |
} | |
// perlin noise function | |
float perlin( | |
float x,float y, uint32_t seed, float smooth, size_t octaves) const | |
{ | |
float accum = 0.f, scale = 1.f; | |
for (size_t i = 0; i<octaves; ++i) { | |
accum += rand2df(x, y, seed+i) * scale; | |
x *= .512367592345f; | |
y *= .534968120897f; | |
scale *= (smooth+1.f); | |
} | |
return accum; | |
} | |
}; | |
int main() | |
{ | |
static const size_t WIDTH = 256; | |
static const float SMOOTH = 1.f; | |
static const size_t OCTAVES = 8; | |
if (SDL_Init(SDL_INIT_VIDEO)!=0) { | |
return 1; | |
} | |
SDL_Surface * screen = SDL_SetVideoMode(WIDTH, WIDTH, 32, 0); | |
if (!screen) { | |
return 2; | |
} | |
// object is too big for the stack, lets just fudge it :) | |
perlin_t & map = *(new perlin_t(WIDTH)); | |
uint32_t seed = SDL_GetTicks(); | |
map.generate(seed, SMOOTH, OCTAVES); | |
bool active = true; | |
while (active) { | |
SDL_Delay(10); | |
SDL_Event event; | |
if (SDL_PollEvent(&event)) { | |
if (event.type==SDL_QUIT) { | |
active = false; | |
} | |
if (event.type==SDL_KEYDOWN || event.type==SDL_MOUSEBUTTONDOWN) { | |
map.generate(++seed, SMOOTH, OCTAVES); | |
} | |
} | |
uint32_t * pix = (uint32_t*) screen->pixels; | |
#if 1 | |
for (size_t i = 0; i<map.AREA; ++i) { | |
uint32_t rgb = uint32_t(clampv(0.f, map.map[i], 1.f)*255); | |
pix[i] = rgb|(rgb<<8)|(rgb<<16); | |
} | |
#endif | |
SDL_Flip(screen); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment