noise/basis: parameterise basis functions

This commit is contained in:
Danny Robson 2015-05-18 17:16:04 +10:00
parent cacaee4c5d
commit 07513b078c
5 changed files with 171 additions and 121 deletions

View File

@ -34,93 +34,116 @@ using util::noise::cellular;
// Generate a type from [-UNIT..UNIT] // Generate a type from [-UNIT..UNIT]
template <typename T> template <typename T>
T T
generate (intmax_t x, intmax_t y, basis::seed_t); generate (intmax_t x, intmax_t y, uint64_t seed)
{
//-----------------------------------------------------------------------------
template <>
double
generate (intmax_t x, intmax_t y, basis::seed_t seed) {
size_t idx = util::noise::permute (x, y, seed); size_t idx = util::noise::permute (x, y, seed);
return util::noise::LUT[idx]; return T(util::noise::LUT[idx]);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <> template <>
util::vector2d util::vector2d
generate (intmax_t x, intmax_t y, basis::seed_t seed) { generate (intmax_t x, intmax_t y, uint64_t seed)
{
auto u = util::noise::permute (x, y, seed); auto u = util::noise::permute (x, y, seed);
auto v = util::noise::permute (u ^ seed); auto v = util::noise::permute (u ^ seed);
return util::vector2d (util::noise::LUT[u], util::noise::LUT[v]); return {
util::noise::LUT[u],
util::noise::LUT[v]
};
}
//-----------------------------------------------------------------------------
template <>
util::vector2f
generate (intmax_t x, intmax_t y, uint64_t seed)
{
auto u = util::noise::permute (x, y, seed);
auto v = util::noise::permute (u ^ seed);
return {
float (util::noise::LUT[u]),
float (util::noise::LUT[v])
};
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
basis::basis (seed_t _seed): template <typename T>
basis<T>::basis (seed_t _seed):
seed (_seed) seed (_seed)
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
basis::basis (): template <typename T>
basis<T>::basis ():
seed (util::random<seed_t> ()) seed (util::random<seed_t> ())
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
basis::~basis () template <typename T>
basis<T>::~basis ()
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
double template <typename T>
basis::eval (double, double) const T
basis<T>::eval (T, T) const
{ unreachable (); } { unreachable (); }
//-----------------------------------------------------------------------------
namespace util { namespace noise {
template struct basis<float>;
template struct basis<double>;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
template <util::noise::lerp_t<double> L> template <typename T, util::noise::lerp_t<T> L>
value<L>::value (seed_t _seed): value<T,L>::value (seed_t _seed):
basis (_seed) basis<T> (_seed)
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <util::noise::lerp_t<double> L> template <typename T, util::noise::lerp_t<T> L>
value<L>::value () value<T,L>::value ()
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <util::noise::lerp_t<double> L> template <typename T, util::noise::lerp_t<T> L>
util::range<double> util::range<T>
value<L>::bounds (void) const value<T,L>::bounds (void) const
{ return { -1.0, 1.0 }; } { return { -1, 1 }; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <util::noise::lerp_t<double> L> template <typename T, util::noise::lerp_t<T> L>
double T
value<L>::eval (double x, double y) const { value<T,L>::eval (T x, T y) const {
intmax_t x_int = static_cast<intmax_t> (x); intmax_t x_int = static_cast<intmax_t> (x);
intmax_t y_int = static_cast<intmax_t> (y); intmax_t y_int = static_cast<intmax_t> (y);
double x_fac = x - x_int; T x_fac = x - x_int;
double y_fac = y - y_int; T y_fac = y - y_int;
// Shift the coordinate system down a little to ensure we get unit weights // 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 // for the lerp. It's better to do this than abs the fractional portion so
// we don't get reflections along the origin. // we don't get reflections along the origin.
if (x < 0) { x_fac = 1.0 + x_fac; x_int -= 1; } if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; }
if (y < 0) { y_fac = 1.0 + y_fac; y_int -= 1; } if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; }
// Generate the four corner values // Generate the four corner values
double p0 = generate<double> (x_int, y_int, this->seed); T p0 = generate<T> (x_int, y_int, this->seed);
double p1 = generate<double> (x_int + 1, y_int, this->seed); T p1 = generate<T> (x_int + 1, y_int, this->seed);
double p2 = generate<double> (x_int, y_int + 1, this->seed); T p2 = generate<T> (x_int, y_int + 1, this->seed);
double p3 = generate<double> (x_int + 1, y_int + 1, this->seed); T p3 = generate<T> (x_int + 1, y_int + 1, this->seed);
// Interpolate on one dimension, then the other. // Interpolate on one dimension, then the other.
return L (L (p0, p1, x_fac), return L (L (p0, p1, x_fac),
@ -130,62 +153,64 @@ value<L>::eval (double x, double y) const {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
namespace util { namespace util { namespace noise {
namespace noise { template struct value<float, lerp::linear>;
template struct value<util::lerp::linear>; template struct value<float, lerp::cubic>;
template struct value<util::lerp::cubic>; template struct value<float, lerp::quintic>;
template struct value<util::lerp::quintic>;
} template struct value<double, lerp::linear>;
} template struct value<double, lerp::cubic>;
template struct value<double, lerp::quintic>;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
template <util::noise::lerp_t<double> L> template <typename T, util::noise::lerp_t<T> L>
gradient<L>::gradient (seed_t _seed): gradient<T,L>::gradient (seed_t _seed):
basis (_seed) basis<T> (_seed)
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <util::noise::lerp_t<double> L> template <typename T, util::noise::lerp_t<T> L>
gradient<L>::gradient () gradient<T,L>::gradient ()
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <util::noise::lerp_t<double> L> template <typename T, util::noise::lerp_t<T> L>
util::range<double> util::range<T>
gradient<L>::bounds (void) const gradient<T,L>::bounds (void) const
{ return { -sqrt(2.0) / 2.0, sqrt (2.0) / 2.0 }; } { return { -std::sqrt(T{2}) / 2, std::sqrt (T{2}) / 2 }; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template <util::noise::lerp_t<double> L> template <typename T, util::noise::lerp_t<T> L>
double T
gradient<L>::eval (double x, double y) const { gradient<T,L>::eval (T x, T y) const {
intmax_t x_int = static_cast<intmax_t> (x); intmax_t x_int = static_cast<intmax_t> (x);
intmax_t y_int = static_cast<intmax_t> (y); intmax_t y_int = static_cast<intmax_t> (y);
double x_fac = x - x_int; T x_fac = x - x_int;
double y_fac = y - y_int; T y_fac = y - y_int;
// Shift the coordinate system down a little to ensure we get unit weights // 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 // for the lerp. It's better to do this than abs the fractional portion so
// we don't get reflections along the origin. // we don't get reflections along the origin.
if (x < 0) { x_fac = 1.0 + x_fac; x_int -= 1; } if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; }
if (y < 0) { y_fac = 1.0 + y_fac; y_int -= 1; } if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; }
// Generate the four corner values. It's not strictly necessary to // Generate the four corner values. It's not strictly necessary to
// normalise the values, but we get a more consistent and visually // normalise the values, but we get a more consistent and visually
// appealing range of outputs with normalised values. // appealing range of outputs with normalised values.
vector2d p0 = generate<vector2d> (x_int, y_int, this->seed).normalise (); auto p0 = generate<vector<2,T>> (x_int, y_int, this->seed).normalise ();
vector2d p1 = generate<vector2d> (x_int + 1, y_int, this->seed).normalise (); auto p1 = generate<vector<2,T>> (x_int + 1, y_int, this->seed).normalise ();
vector2d p2 = generate<vector2d> (x_int, y_int + 1, this->seed).normalise (); auto p2 = generate<vector<2,T>> (x_int, y_int + 1, this->seed).normalise ();
vector2d p3 = generate<vector2d> (x_int + 1, y_int + 1, this->seed).normalise (); auto p3 = generate<vector<2,T>> (x_int + 1, y_int + 1, this->seed).normalise ();
double v0 = p0.x * x_fac + p0.y * y_fac; T v0 = p0.x * x_fac + p0.y * y_fac;
double v1 = p1.x * (x_fac - 1.0) + p1.y * y_fac; T v1 = p1.x * (x_fac - 1) + p1.y * y_fac;
double v2 = p2.x * x_fac + p2.y * (y_fac - 1.0); T v2 = p2.x * x_fac + p2.y * (y_fac - 1);
double v3 = p3.x * (x_fac - 1.0) + p3.y * (y_fac - 1.0); T v3 = p3.x * (x_fac - 1) + p3.y * (y_fac - 1);
return L (L (v0, v1, x_fac), return L (L (v0, v1, x_fac),
L (v2, v3, x_fac), L (v2, v3, x_fac),
@ -196,45 +221,51 @@ gradient<L>::eval (double x, double y) const {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
namespace util { namespace util {
namespace noise { namespace noise {
template struct gradient<util::lerp::linear>; template struct gradient<float, lerp::linear>;
template struct gradient<util::lerp::cubic>; template struct gradient<float, lerp::cubic>;
template struct gradient<util::lerp::quintic>; template struct gradient<float, lerp::quintic>;
template struct gradient<double, lerp::linear>;
template struct gradient<double, lerp::cubic>;
template struct gradient<double, lerp::quintic>;
} }
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
cellular::cellular (seed_t _seed): template <typename T>
basis (_seed) cellular<T>::cellular (seed_t _seed):
basis<T> (_seed)
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
cellular::cellular () template <typename T>
cellular<T>::cellular ()
{ ; } { ; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
util::range<double> template <typename T>
cellular::bounds (void) const util::range<T>
cellular<T>::bounds (void) const
{ return { 0.0, 1.5 }; } { return { 0.0, 1.5 }; }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
double template <typename T>
cellular::eval (double x, double y) const { T
using util::point2d; cellular<T>::eval (T x, T y) const {
intmax_t x_int = static_cast<intmax_t> (x); intmax_t x_int = static_cast<intmax_t> (x);
intmax_t y_int = static_cast<intmax_t> (y); intmax_t y_int = static_cast<intmax_t> (y);
double x_fac = x - x_int; T x_fac = x - x_int;
double y_fac = y - y_int; T y_fac = y - y_int;
// Generate the four corner values. It's not strictly necessary to // Generate the four corner values. It's not strictly necessary to
// normalise the values, but we get a more consistent and visually // normalise the values, but we get a more consistent and visually
// appealing range of outputs with normalised values. // appealing range of outputs with normalised values.
if (x < 0) { x_fac = 1.0 + x_fac; x_int -= 1; } if (x < 0) { x_fac = 1 + x_fac; x_int -= 1; }
if (y < 0) { y_fac = 1.0 + y_fac; y_int -= 1; } if (y < 0) { y_fac = 1 + y_fac; y_int -= 1; }
// +---+---+---+ // +---+---+---+
// | 0 | 1 | 2 | // | 0 | 1 | 2 |
@ -244,19 +275,19 @@ cellular::eval (double x, double y) const {
// | 6 | 7 | 8 | // | 6 | 7 | 8 |
// +---+---+---+ // +---+---+---+
point2d centre = { x_fac, y_fac }; point<2,T> centre = { x_fac, y_fac };
double distances[9] = { std::numeric_limits<double>::quiet_NaN () }; T distances[9] = { std::numeric_limits<T>::quiet_NaN () };
double *cursor = distances; T *cursor = distances;
for (signed y_off = -1; y_off <= 1 ; ++y_off) for (signed y_off = -1; y_off <= 1 ; ++y_off)
for (signed x_off = -1; x_off <= 1; ++x_off) { for (signed x_off = -1; x_off <= 1; ++x_off) {
auto pos = point2d (double (x_off), double (y_off)); auto pos = point<2,T> (T (x_off), T (y_off));
auto off = generate<vector2d> (x_int + x_off, y_int + y_off, this->seed); auto off = generate<vector<2,T>> (x_int + x_off, y_int + y_off, this->seed);
off += 1.; off += T{1};
off /= 2.; off /= T{2};
CHECK (off.x >= 0 && off.x <= 1.0); CHECK (off.x >= 0 && off.x <= 1);
CHECK (off.y >= 0 && off.y <= 1.0); CHECK (off.y >= 0 && off.y <= 1);
pos += off; pos += off;
*cursor++ = pos.distance2 (centre); *cursor++ = pos.distance2 (centre);
@ -267,3 +298,10 @@ cellular::eval (double x, double y) const {
CHECK (bounds ().contains (distances[0])); CHECK (bounds ().contains (distances[0]));
return distances[0]; return distances[0];
} }
//-----------------------------------------------------------------------------
namespace util { namespace noise {
template struct cellular<float>;
template struct cellular<double>;
} }

View File

@ -25,6 +25,7 @@ namespace util {
namespace noise { namespace noise {
template <typename T> using lerp_t = T (*)(T,T,T); template <typename T> using lerp_t = T (*)(T,T,T);
template <typename T>
struct basis { struct basis {
typedef uint64_t seed_t; typedef uint64_t seed_t;
@ -34,40 +35,47 @@ namespace util {
seed_t seed; seed_t seed;
virtual range<double> bounds (void) const = 0; virtual range<T> bounds (void) const = 0;
virtual double eval (double x, double y) const = 0; virtual T eval (T x, T y) const = 0;
}; };
/// Perlin: single value per grid space /// Perlin: single value per grid space
template <lerp_t<double>> template <typename T, lerp_t<T>>
struct value : public basis { struct value : public basis<T> {
using seed_t = typename basis<T>::seed_t;
value (seed_t); value (seed_t);
value (); value ();
virtual range<double> bounds (void) const; virtual range<T> bounds (void) const override;
virtual double eval (double x, double y) const; virtual T eval (T x, T y) const override;
}; };
/// Perlin: interpolated value across each grid space /// Perlin: interpolated value across each grid space
template <lerp_t<double>L> template <typename T, lerp_t<T> L>
struct gradient : public basis { struct gradient : public basis<T> {
using seed_t = typename basis<T>::seed_t;
gradient (seed_t); gradient (seed_t);
gradient (); gradient ();
virtual range<double> bounds (void) const; virtual range<T> bounds (void) const override;
virtual double eval (double x, double y) const; virtual T eval (T x, T y) const override;
}; };
/// Cellular/Worley/Vornoi of order-1 /// Cellular/Worley/Vornoi of order-1
struct cellular : public basis { template <typename T>
struct cellular : public basis<T> {
using seed_t = typename basis<T>::seed_t;
cellular (seed_t); cellular (seed_t);
cellular (); cellular ();
virtual range<double> bounds (void) const; virtual range<T> bounds (void) const override;
virtual double eval (double x, double y) const; virtual T eval (T x, T y) const override;
}; };
} }
} }

View File

@ -55,7 +55,7 @@ template <typename B>
util::noise::fbm<B>::fbm (unsigned _octaves, util::noise::fbm<B>::fbm (unsigned _octaves,
double _frequency, double _frequency,
double _lacunarity, double _lacunarity,
basis::seed_t _seed): seed_t _seed):
fractal (_octaves, _frequency, _lacunarity), fractal (_octaves, _frequency, _lacunarity),
basis (_seed) basis (_seed)
{ ; } { ; }
@ -90,11 +90,11 @@ util::noise::fbm<B>::eval (double x, double y) const {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template struct util::noise::fbm<util::noise::cellular>; template struct util::noise::fbm<util::noise::cellular<double>>;
template struct util::noise::fbm<util::noise::gradient<util::lerp::linear>>; template struct util::noise::fbm<util::noise::gradient<double,util::lerp::linear>>;
template struct util::noise::fbm<util::noise::gradient<util::lerp::quintic>>; template struct util::noise::fbm<util::noise::gradient<double,util::lerp::quintic>>;
template struct util::noise::fbm<util::noise::value<util::lerp::linear>>; template struct util::noise::fbm<util::noise::value<double,util::lerp::linear>>;
template struct util::noise::fbm<util::noise::value<util::lerp::quintic>>; template struct util::noise::fbm<util::noise::value<double,util::lerp::quintic>>;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -102,7 +102,7 @@ template <typename B>
util::noise::musgrave<B>::musgrave (unsigned _octaves, util::noise::musgrave<B>::musgrave (unsigned _octaves,
double _frequency, double _frequency,
double _lacunarity, double _lacunarity,
basis::seed_t _seed): seed_t _seed):
fractal (_octaves, _frequency, _lacunarity), fractal (_octaves, _frequency, _lacunarity),
basis (_seed) basis (_seed)
{ ; } { ; }
@ -156,9 +156,9 @@ util::noise::musgrave<B>::eval (double x, double y) const {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template struct util::noise::musgrave<util::noise::cellular>; template struct util::noise::musgrave<util::noise::cellular<double>>;
template struct util::noise::musgrave<util::noise::gradient<util::lerp::linear>>; template struct util::noise::musgrave<util::noise::gradient<double,util::lerp::linear>>;
template struct util::noise::musgrave<util::noise::gradient<util::lerp::quintic>>; template struct util::noise::musgrave<util::noise::gradient<double,util::lerp::quintic>>;
template struct util::noise::musgrave<util::noise::value<util::lerp::linear>>; template struct util::noise::musgrave<util::noise::value<double,util::lerp::linear>>;
template struct util::noise::musgrave<util::noise::value<util::lerp::quintic>>; template struct util::noise::musgrave<util::noise::value<double,util::lerp::quintic>>;

View File

@ -41,10 +41,12 @@ namespace util {
/// Fractal Brownian Motion summation. /// Fractal Brownian Motion summation.
template <typename B> template <typename B>
struct fbm : public fractal { struct fbm : public fractal {
using seed_t = typename basis<double>::seed_t;
fbm (unsigned octaves, fbm (unsigned octaves,
double frequency, double frequency,
double lacunarity, double lacunarity,
basis::seed_t seed); seed_t seed);
fbm (); fbm ();
B basis; B basis;
@ -55,10 +57,12 @@ namespace util {
/// Rigid Multifractal noise summation. /// Rigid Multifractal noise summation.
template <typename B> template <typename B>
struct musgrave : public fractal { struct musgrave : public fractal {
using seed_t = typename basis<double>::seed_t;
musgrave (unsigned octaves, musgrave (unsigned octaves,
double frequency, double frequency,
double lacunarity, double lacunarity,
basis::seed_t seed); seed_t seed);
musgrave (); musgrave ();
B basis; B basis;

View File

@ -11,9 +11,9 @@ main (void)
//using basis_t = util::noise::gradient<lerp_t>; //using basis_t = util::noise::gradient<lerp_t>;
//using noise_t = util::noise::fbm<basis_t>; //using noise_t = util::noise::fbm<basis_t>;
//using noise_t = util::noise::fbm<util::noise::gradient<util::lerp::quintic>>; using noise_t = util::noise::fbm<util::noise::gradient<double,util::lerp::quintic>>;
//using noise_t = util::noise::musgrave<util::noise::gradient<util::lerp::quintic>>; //using noise_t = util::noise::musgrave<util::noise::gradient<double,util::lerp::quintic>>;
using noise_t = util::noise::fbm<util::noise::cellular>; //using noise_t = util::noise::fbm<util::noise::cellular<double>>;
util::noise::fill (img, noise_t {}); util::noise::fill (img, noise_t {});