Skip to content

Instantly share code, notes, and snippets.

@bit-hack
Created January 9, 2018 00:45
Show Gist options
  • Save bit-hack/e8af574fc4c30a7c5f95f025d7e7bf92 to your computer and use it in GitHub Desktop.
Save bit-hack/e8af574fc4c30a7c5f95f025d7e7bf92 to your computer and use it in GitHub Desktop.
Perlin noise generator
#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