noise: use points instead of scalars in eval
This commit is contained in:
parent
b96ad81d32
commit
c9812f7c78
@ -34,7 +34,7 @@ util::noise::fill (image::buffer<T> &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<float>&, const util::noise::fractal<float>&);
|
||||
@ -49,7 +49,7 @@ util::noise::image2d (uint8_t *restrict pixels,
|
||||
const util::noise::fractal<float> &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<uint8_t> (v * std::numeric_limits<uint8_t>::max ());
|
||||
}
|
||||
}
|
||||
|
126
noise/basis.cpp
126
noise/basis.cpp
@ -35,11 +35,11 @@ using util::noise::cellular;
|
||||
// Generate a type from [-UNIT..UNIT]
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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<T>::~basis ()
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
T
|
||||
basis<T>::operator() (T, T) const
|
||||
basis<T>::operator() (util::point<2,T>) const
|
||||
{
|
||||
unreachable ();
|
||||
}
|
||||
@ -153,29 +132,27 @@ value<T,L>::bounds (void) const
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T, util::noise::lerp_t<T> L>
|
||||
T
|
||||
value<T,L>::operator() (T x, T y) const
|
||||
value<T,L>::operator() (util::point<2,T> p) const
|
||||
{
|
||||
intmax_t x_int = static_cast<intmax_t> (x);
|
||||
intmax_t y_int = static_cast<intmax_t> (y);
|
||||
T x_fac = x - x_int;
|
||||
T y_fac = y - y_int;
|
||||
auto p_int = p.template cast<intmax_t> ();
|
||||
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<T> (x_int, y_int, this->seed);
|
||||
T p1 = generate<T> (x_int + 1, y_int, this->seed);
|
||||
T p2 = generate<T> (x_int, y_int + 1, this->seed);
|
||||
T p3 = generate<T> (x_int + 1, y_int + 1, this->seed);
|
||||
T p0 = gen_scalar<T> (p_int + util::vector<2,T>{ 0, 0 }, this->seed);
|
||||
T p1 = gen_scalar<T> (p_int + util::vector<2,T>{ 1, 0 }, this->seed);
|
||||
T p2 = gen_scalar<T> (p_int + util::vector<2,T>{ 0, 0 }, this->seed);
|
||||
T p3 = gen_scalar<T> (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<T,L>::bounds (void) const
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T, util::noise::lerp_t<T> L>
|
||||
T
|
||||
gradient<T,L>::operator() (T x, T y) const
|
||||
gradient<T,L>::operator() (util::point<2,T> p) const
|
||||
{
|
||||
intmax_t x_int = static_cast<intmax_t> (x);
|
||||
intmax_t y_int = static_cast<intmax_t> (y);
|
||||
T x_fac = x - x_int;
|
||||
T y_fac = y - y_int;
|
||||
auto p_int = p.template cast<intmax_t> ();
|
||||
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<vector<2,T>> (x_int, y_int, this->seed).normalise ();
|
||||
auto p1 = generate<vector<2,T>> (x_int + 1, y_int, this->seed).normalise ();
|
||||
auto p2 = generate<vector<2,T>> (x_int, y_int + 1, this->seed).normalise ();
|
||||
auto p3 = generate<vector<2,T>> (x_int + 1, y_int + 1, this->seed).normalise ();
|
||||
auto p0 = gen_vector<T> (p_int + util::vector<2,T> { 0, 0 }, this->seed).normalise ();
|
||||
auto p1 = gen_vector<T> (p_int + util::vector<2,T> { 1, 0 }, this->seed).normalise ();
|
||||
auto p2 = gen_vector<T> (p_int + util::vector<2,T> { 0, 1 }, this->seed).normalise ();
|
||||
auto p3 = gen_vector<T> (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<T>::bounds (void) const
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
T
|
||||
cellular<T>::operator() (T x, T y) const
|
||||
cellular<T>::operator() (util::point<2,T> p) const
|
||||
{
|
||||
intmax_t x_int = static_cast<intmax_t> (x);
|
||||
intmax_t y_int = static_cast<intmax_t> (y);
|
||||
T x_fac = x - x_int;
|
||||
T y_fac = y - y_int;
|
||||
auto p_int = p.template cast<intmax_t> ();
|
||||
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<T>::operator() (T x, T y) const
|
||||
// | 6 | 7 | 8 |
|
||||
// +---+---+---+
|
||||
|
||||
point<2,T> centre = { x_fac, y_fac };
|
||||
T distances[9] = { std::numeric_limits<T>::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<vector<2,T>> (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<T> (p_int + pos, this->seed);
|
||||
off += T{1};
|
||||
off /= T{2};
|
||||
|
||||
@ -322,7 +294,7 @@ cellular<T>::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));
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#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<T> 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<T> 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<T> 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<T> bounds (void) const final;
|
||||
virtual T operator() (T x, T y) const final;
|
||||
virtual T operator() (util::point<2,T>) const final;
|
||||
};
|
||||
} }
|
||||
|
||||
|
@ -52,7 +52,7 @@ fractal<T>::~fractal ()
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T>
|
||||
T
|
||||
fractal<T>::operator() (T, T) const
|
||||
fractal<T>::operator() (util::point<2,T>) const
|
||||
{
|
||||
unreachable ();
|
||||
}
|
||||
@ -102,13 +102,13 @@ fbm<T,B>::fbm ():
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T, typename B>
|
||||
T
|
||||
fbm<T,B>::operator() (T x, T y) const {
|
||||
fbm<T,B>::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<T,B>::rmf ():
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T, typename B>
|
||||
T
|
||||
rmf<T,B>::operator() (T x, T y) const {
|
||||
rmf<T,B>::operator() (util::point<2,T> p) const {
|
||||
const T offset = 1;
|
||||
const T H = 1.f;
|
||||
|
||||
@ -179,12 +179,11 @@ rmf<T,B>::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<T,B>::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<T,B>::hmf ():
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T, typename B>
|
||||
T
|
||||
hmf<T,B>::operator() (T x, T y) const
|
||||
hmf<T,B>::operator() (util::point<2,T> p) const
|
||||
{
|
||||
T exponents[octaves];
|
||||
for (size_t i = 0; i < octaves; ++i)
|
||||
@ -249,19 +247,17 @@ hmf<T,B>::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<T,B>::hetero():
|
||||
//-----------------------------------------------------------------------------
|
||||
template <typename T, typename B>
|
||||
T
|
||||
hetero<T,B>::operator() (T x, T y) const
|
||||
hetero<T,B>::operator() (util::point<2,T> p) const
|
||||
{
|
||||
T exponents[octaves];
|
||||
for (size_t i = 0; i < octaves; ++i)
|
||||
@ -300,23 +296,18 @@ hetero<T,B>::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;
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -12,15 +12,18 @@ main (void)
|
||||
|
||||
// setup the noise generator
|
||||
//util::noise::fbm<float, util::noise::cellular<float>> b;
|
||||
util::noise::rmf<float, util::noise::gradient<float,util::lerp::quintic>> b;
|
||||
b.octaves = 3;
|
||||
//util::noise::rmf<float, util::noise::cellular<float>> b;
|
||||
//util::noise::hmf<float, util::noise::gradient<float,util::lerp::quintic>> b;
|
||||
util::noise::hetero<float, util::noise::gradient<float,util::lerp::quintic>> 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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user