From c9812f7c781297707a314defdcee8a3f240b87ce Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Thu, 28 May 2015 10:17:19 +1000 Subject: [PATCH] noise: use points instead of scalars in eval --- noise.cpp | 4 +- noise/basis.cpp | 126 ++++++++++++++++++---------------------------- noise/basis.hpp | 9 ++-- noise/fractal.cpp | 43 +++++++--------- noise/fractal.hpp | 10 ++-- tools/noise.cpp | 9 ++-- 6 files changed, 84 insertions(+), 117 deletions(-) diff --git a/noise.cpp b/noise.cpp index a2f1f94d..33b0ed97 100644 --- a/noise.cpp +++ b/noise.cpp @@ -34,7 +34,7 @@ util::noise::fill (image::buffer &pixels, for (size_t y = 0; y < h; ++y) for (size_t x = 0; x < w; ++x) - data[y * s + x] = gen (x, y); + data[y * s + x] = gen ({T(x), T(y)}); } template void util::noise::fill (image::buffer&, const util::noise::fractal&); @@ -49,7 +49,7 @@ util::noise::image2d (uint8_t *restrict pixels, const util::noise::fractal &p) { for (size_t y = 0; y < height; ++y) for (size_t x = 0; x < width; ++x) { - double v = p (x, y); + float v = p ({float(x), float(y)}); pixels[x + y * width] = static_cast (v * std::numeric_limits::max ()); } } diff --git a/noise/basis.cpp b/noise/basis.cpp index 2003edef..264f5fce 100644 --- a/noise/basis.cpp +++ b/noise/basis.cpp @@ -35,11 +35,11 @@ using util::noise::cellular; // Generate a type from [-UNIT..UNIT] template T -generate (intmax_t x, intmax_t y, uint64_t seed) +gen_scalar (util::point<2,T> p, uint64_t seed) { using util::hash::murmur2::mix; - T v = mix (seed, mix (uint64_t (y), uint64_t (x))) & 0xffff; + T v = mix (seed, mix (uint64_t (p.y), uint64_t (p.x))) & 0xffff; v = v / T{0xffff} * 2 - 1; CHECK_GE (v, T{0}); @@ -50,43 +50,22 @@ generate (intmax_t x, intmax_t y, uint64_t seed) //----------------------------------------------------------------------------- -template <> -util::vector2d -generate (intmax_t x, intmax_t y, uint64_t seed) +template +util::vector<2,T> +gen_vector (util::point<2,T> p, uint64_t seed) { using util::hash::murmur2::mix; - auto u = mix (seed, mix (uint64_t (x), uint64_t (y))); + auto u = mix (seed, mix (uint64_t (p.x), uint64_t (p.y))); auto v = mix (u, seed); - auto r = util::vector2d { - (u & 0xffff) / double{0xffff}, - (v & 0xffff) / double{0xffff} - } * 2.0 - 1.0; + auto r = util::vector<2,T> { + (u & 0xffff) / T{0xffff}, + (v & 0xffff) / T{0xffff} + } * 2 - 1; - CHECK_GE (r, double{-1}); - CHECK_LE (r, double{ 1}); - - return r; -} - -//----------------------------------------------------------------------------- -template <> -util::vector2f -generate (intmax_t x, intmax_t y, uint64_t seed) -{ - using util::hash::murmur2::mix; - - auto u = mix (seed, mix (uint64_t (x), uint64_t (y))); - auto v = mix (u, seed); - - auto r = util::vector2f { - (u & 0xffff) / float{0xffff}, - (v & 0xffff) / float{0xffff} - } * 2.f - 1.f; - - CHECK_GE (r, float (-1)); - CHECK_LE (r, float ( 1)); + CHECK_GE (r, T{-1}); + CHECK_LE (r, T{ 1}); return r; } @@ -115,7 +94,7 @@ basis::~basis () //----------------------------------------------------------------------------- template T -basis::operator() (T, T) const +basis::operator() (util::point<2,T>) const { unreachable (); } @@ -153,29 +132,27 @@ value::bounds (void) const //----------------------------------------------------------------------------- template L> T -value::operator() (T x, T y) const +value::operator() (util::point<2,T> p) const { - intmax_t x_int = static_cast (x); - intmax_t y_int = static_cast (y); - T x_fac = x - x_int; - T y_fac = y - y_int; + auto p_int = p.template cast (); + auto p_rem = p - p_int; // Shift the coordinate system down a little to ensure we get unit weights // for the lerp. It's better to do this than abs the fractional portion so // we don't get reflections along the origin. - if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; } - if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; } + if (p.x < 0) { p_rem.x = 1 + p_rem.x; p_int.x -= 1; } + if (p.y < 0) { p_rem.y = 1 + p_rem.y; p_int.y -= 1; } // Generate the four corner values - T p0 = generate (x_int, y_int, this->seed); - T p1 = generate (x_int + 1, y_int, this->seed); - T p2 = generate (x_int, y_int + 1, this->seed); - T p3 = generate (x_int + 1, y_int + 1, this->seed); + T p0 = gen_scalar (p_int + util::vector<2,T>{ 0, 0 }, this->seed); + T p1 = gen_scalar (p_int + util::vector<2,T>{ 1, 0 }, this->seed); + T p2 = gen_scalar (p_int + util::vector<2,T>{ 0, 0 }, this->seed); + T p3 = gen_scalar (p_int + util::vector<2,T>{ 1, 1 }, this->seed); // Interpolate on one dimension, then the other. - return L (L (p0, p1, x_fac), - L (p2, p3, x_fac), - y_fac); + return L (L (p0, p1, p_rem.x), + L (p2, p3, p_rem.x), + p_rem.y); } @@ -216,35 +193,33 @@ gradient::bounds (void) const //----------------------------------------------------------------------------- template L> T -gradient::operator() (T x, T y) const +gradient::operator() (util::point<2,T> p) const { - intmax_t x_int = static_cast (x); - intmax_t y_int = static_cast (y); - T x_fac = x - x_int; - T y_fac = y - y_int; + auto p_int = p.template cast (); + auto p_rem = p - p_int; // Shift the coordinate system down a little to ensure we get unit weights // for the lerp. It's better to do this than abs the fractional portion so // we don't get reflections along the origin. - if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; } - if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; } + if (p.x < 0) { p_rem.x = 1 + p_rem.x; p_int.x -= 1; } + if (p.y < 0) { p_rem.y = 1 + p_rem.y; p_int.y -= 1; } // Generate the four corner values. It's not strictly necessary to // normalise the values, but we get a more consistent and visually // appealing range of outputs with normalised values. - auto p0 = generate> (x_int, y_int, this->seed).normalise (); - auto p1 = generate> (x_int + 1, y_int, this->seed).normalise (); - auto p2 = generate> (x_int, y_int + 1, this->seed).normalise (); - auto p3 = generate> (x_int + 1, y_int + 1, this->seed).normalise (); + auto p0 = gen_vector (p_int + util::vector<2,T> { 0, 0 }, this->seed).normalise (); + auto p1 = gen_vector (p_int + util::vector<2,T> { 1, 0 }, this->seed).normalise (); + auto p2 = gen_vector (p_int + util::vector<2,T> { 0, 1 }, this->seed).normalise (); + auto p3 = gen_vector (p_int + util::vector<2,T> { 1, 1 }, this->seed).normalise (); - T v0 = p0.x * x_fac + p0.y * y_fac; - T v1 = p1.x * (x_fac - 1) + p1.y * y_fac; - T v2 = p2.x * x_fac + p2.y * (y_fac - 1); - T v3 = p3.x * (x_fac - 1) + p3.y * (y_fac - 1); + T v0 = p0.x * p_rem.x + p0.y * p_rem.y; + T v1 = p1.x * (p_rem.x - 1) + p1.y * p_rem.y; + T v2 = p2.x * p_rem.x + p2.y * (p_rem.y - 1); + T v3 = p3.x * (p_rem.x - 1) + p3.y * (p_rem.y - 1); - auto L0 = L (v0, v1, x_fac); - auto L1 = L (v2, v3, x_fac); - auto L_ = L (L0, L1, y_fac); + auto L0 = L (v0, v1, p_rem.x); + auto L1 = L (v2, v3, p_rem.x); + auto L_ = L (L0, L1, p_rem.y); return L_; } @@ -286,18 +261,16 @@ cellular::bounds (void) const //----------------------------------------------------------------------------- template T -cellular::operator() (T x, T y) const +cellular::operator() (util::point<2,T> p) const { - intmax_t x_int = static_cast (x); - intmax_t y_int = static_cast (y); - T x_fac = x - x_int; - T y_fac = y - y_int; + auto p_int = p.template cast (); + auto p_rem = p - p_int; // Generate the four corner values. It's not strictly necessary to // normalise the values, but we get a more consistent and visually // appealing range of outputs with normalised values. - if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; } - if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; } + if (p.x < 0) { p_rem.x = 1 + p_rem.x; p_int.x -= 1; } + if (p.y < 0) { p_rem.y = 1 + p_rem.y; p_int.y -= 1; } // +---+---+---+ // | 0 | 1 | 2 | @@ -307,14 +280,13 @@ cellular::operator() (T x, T y) const // | 6 | 7 | 8 | // +---+---+---+ - point<2,T> centre = { x_fac, y_fac }; T distances[9] = { std::numeric_limits::quiet_NaN () }; T *cursor = distances; for (signed y_off = -1; y_off <= 1 ; ++y_off) for (signed x_off = -1; x_off <= 1; ++x_off) { - auto pos = point<2,T> (T (x_off), T (y_off)); - auto off = generate> (x_int + x_off, y_int + y_off, this->seed); + auto pos = vector<2,T> (T (x_off), T (y_off)); + auto off = gen_vector (p_int + pos, this->seed); off += T{1}; off /= T{2}; @@ -322,7 +294,7 @@ cellular::operator() (T x, T y) const CHECK (off.y >= 0 && off.y <= 1); pos += off; - *cursor++ = pos.distance2 (centre); + *cursor++ = pos.difference2 (p_rem); } std::sort (std::begin (distances), std::end (distances)); diff --git a/noise/basis.hpp b/noise/basis.hpp index 721ae581..cb285a87 100644 --- a/noise/basis.hpp +++ b/noise/basis.hpp @@ -19,6 +19,7 @@ #include #include "lerp.hpp" +#include "../point.hpp" #include "../range.hpp" namespace util { namespace noise { @@ -36,7 +37,7 @@ namespace util { namespace noise { seed_t seed; virtual range bounds (void) const = 0; - virtual T operator() (T x, T y) const = 0; + virtual T operator() (util::point<2,T>) const = 0; }; @@ -49,7 +50,7 @@ namespace util { namespace noise { value (); virtual range bounds (void) const final; - virtual T operator() (T x, T y) const final; + virtual T operator() (util::point<2,T>) const final; }; @@ -62,7 +63,7 @@ namespace util { namespace noise { gradient (); virtual range bounds (void) const final; - virtual T operator() (T x, T y) const final; + virtual T operator() (util::point<2,T>) const final; }; @@ -75,7 +76,7 @@ namespace util { namespace noise { cellular (); virtual range bounds (void) const final; - virtual T operator() (T x, T y) const final; + virtual T operator() (util::point<2,T>) const final; }; } } diff --git a/noise/fractal.cpp b/noise/fractal.cpp index 4fd03bb5..45b15c46 100644 --- a/noise/fractal.cpp +++ b/noise/fractal.cpp @@ -52,7 +52,7 @@ fractal::~fractal () //----------------------------------------------------------------------------- template T -fractal::operator() (T, T) const +fractal::operator() (util::point<2,T>) const { unreachable (); } @@ -102,13 +102,13 @@ fbm::fbm (): //----------------------------------------------------------------------------- template T -fbm::operator() (T x, T y) const { +fbm::operator() (util::point<2,T> p) const { T total = 0; T f = frequency; T a = amplitude; for (size_t i = 0; i < octaves; ++i) { - total += basis (x * f, y * f) * a; + total += basis (p * f) * a; f *= lacunarity; a *= gain; @@ -167,7 +167,7 @@ rmf::rmf (): //----------------------------------------------------------------------------- template T -rmf::operator() (T x, T y) const { +rmf::operator() (util::point<2,T> p) const { const T offset = 1; const T H = 1.f; @@ -179,12 +179,11 @@ rmf::operator() (T x, T y) const { T result = 0; T weight = 1; - x *= frequency; - y *= frequency; + p *= frequency; for (size_t i = 0; i < octaves; ++i) { // generates ridged noise - signal = basis (x, y); + signal = basis (p); signal = std::fabs (signal); signal = offset - signal; @@ -201,8 +200,7 @@ rmf::operator() (T x, T y) const { // record and continue result += signal * exponents[i]; - x *= lacunarity; - y *= lacunarity; + p *= lacunarity; } return result; @@ -239,7 +237,7 @@ hmf::hmf (): //----------------------------------------------------------------------------- template T -hmf::operator() (T x, T y) const +hmf::operator() (util::point<2,T> p) const { T exponents[octaves]; for (size_t i = 0; i < octaves; ++i) @@ -249,19 +247,17 @@ hmf::operator() (T x, T y) const T signal = 0; T weight = 1; - x *= frequency; - y *= frequency; + p *= frequency; for (size_t i = 0; i < octaves; ++i) { - signal = (basis (x, y) + offset) * exponents[i]; + signal = (basis (p) + offset) * exponents[i]; result += weight * signal; weight *= gain * signal; if (weight > 1) weight = 1; - x *= lacunarity; - y *= lacunarity; + p *= lacunarity; } return result; @@ -291,7 +287,7 @@ hetero::hetero(): //----------------------------------------------------------------------------- template T -hetero::operator() (T x, T y) const +hetero::operator() (util::point<2,T> p) const { T exponents[octaves]; for (size_t i = 0; i < octaves; ++i) @@ -300,23 +296,18 @@ hetero::operator() (T x, T y) const T result = 0; T increment = 0; - x *= frequency; - y *= frequency; - - result = basis (x, y) + offset; - - x *= lacunarity; - y *= lacunarity; + p *= frequency; + result = basis (p) + offset; + p *= lacunarity; for (size_t i = 0; i < octaves; ++i) { - increment = basis (x, y) + offset; + increment = basis (p) + offset; increment *= exponents[i]; increment *= result; result += increment; - x *= lacunarity; - y *= lacunarity; + p *= lacunarity; } return result; diff --git a/noise/fractal.hpp b/noise/fractal.hpp index 170ee342..f19d886a 100644 --- a/noise/fractal.hpp +++ b/noise/fractal.hpp @@ -30,7 +30,7 @@ namespace util { fractal (); virtual ~fractal (); - virtual T operator() (T x, T y) const = 0; + virtual T operator() (util::point<2,T>) const = 0; seed_t seed; }; @@ -73,7 +73,7 @@ namespace util { T gain; B basis; - virtual T operator() (T x, T y) const override; + virtual T operator() (util::point<2,T>) const override; }; @@ -109,7 +109,7 @@ namespace util { T gain; B basis; - virtual T operator() (T x, T y) const override; + virtual T operator() (util::point<2,T>) const override; }; @@ -133,7 +133,7 @@ namespace util { B basis; - virtual T operator() (T x, T y) const override; + virtual T operator() (util::point<2,T>) const override; }; @@ -158,7 +158,7 @@ namespace util { B basis; - virtual T operator() (T x, T y) const override; + virtual T operator() (util::point<2,T>) const override; }; } } diff --git a/tools/noise.cpp b/tools/noise.cpp index c99fdcbb..dfb082ed 100644 --- a/tools/noise.cpp +++ b/tools/noise.cpp @@ -12,15 +12,18 @@ main (void) // setup the noise generator //util::noise::fbm> b; - util::noise::rmf> b; - b.octaves = 3; + //util::noise::rmf> b; + //util::noise::hmf> b; + util::noise::hetero> b; + //b.octaves = 3; b.frequency = 10.f / size.w; + b.lacunarity = 2; b.basis.seed = time (NULL); // generate the values. offset positions slightly to avoid simple axis issues with perlin basis for (size_t y = 0; y < size.h; ++y) for (size_t x = 0; x < size.w; ++x) { - auto v = b (x + 20, y + 20); + auto v = b ({float(x + 20), float(y + 20)}); img.data ()[y * size.w + x] = v; }