n/basis: fix integer position extract when -ve

This commit is contained in:
Danny Robson 2015-05-29 15:54:17 +10:00
parent 3a8179dfff
commit b4ddd287bf
4 changed files with 68 additions and 44 deletions

View File

@ -24,7 +24,7 @@ using util::noise::basis::perlin;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
template <typename T> template <typename T>
util::vector<2,T> util::vector<2,T>
generate (util::point<2,T> p, uint64_t seed) generate (util::point<2,intmax_t> p, uint64_t seed)
{ {
using util::hash::murmur2::mix; using util::hash::murmur2::mix;
@ -75,31 +75,36 @@ template <typename T, util::noise::lerp_t<T> L>
T T
perlin<T,L>::operator() (util::point<2,T> p) const perlin<T,L>::operator() (util::point<2,T> p) const
{ {
// extract integer and fractional parts. be careful to always round down
// (particularly with negatives) and avoid rounding errors.
auto p_int = p.template cast<intmax_t> (); auto p_int = p.template cast<intmax_t> ();
auto p_rem = p - p_int; if (p.x < 0) p_int.x -= 1;
if (p.y < 0) p_int.y -= 1;
auto p_rem = abs (p - p_int);
// Shift the coordinate system down a little to ensure we get unit weights // generate the corner positions
// for the lerp. It's better to do this than abs the fractional portion so auto p0 = p_int + util::vector<2,intmax_t> { 0, 0 };
// we don't get reflections along the origin. auto p1 = p_int + util::vector<2,intmax_t> { 1, 0 };
if (p.x < 0) { p_rem.x = 1 + p_rem.x; p_int.x -= 1; } auto p2 = p_int + util::vector<2,intmax_t> { 0, 1 };
if (p.y < 0) { p_rem.y = 1 + p_rem.y; p_int.y -= 1; } auto p3 = p_int + util::vector<2,intmax_t> { 1, 1 };
// Generate the four corner values. It's not strictly necessary to // generate the corner gradients
// normalise the values, but we get a more consistent and visually auto g0 = generate<T> (p0, this->seed);
// appealing range of outputs with normalised values. auto g1 = generate<T> (p1, this->seed);
auto p0 = generate<T> (p_int + util::vector<2,T> { 0, 0 }, this->seed).normalise (); auto g2 = generate<T> (p2, this->seed);
auto p1 = generate<T> (p_int + util::vector<2,T> { 1, 0 }, this->seed).normalise (); auto g3 = generate<T> (p3, this->seed);
auto p2 = generate<T> (p_int + util::vector<2,T> { 0, 1 }, this->seed).normalise ();
auto p3 = generate<T> (p_int + util::vector<2,T> { 1, 1 }, this->seed).normalise ();
T v0 = p0.x * p_rem.x + p0.y * p_rem.y; // compute the dot products
T v1 = p1.x * (p_rem.x - 1) + p1.y * p_rem.y; T v0 = dot (g0, p - p0);
T v2 = p2.x * p_rem.x + p2.y * (p_rem.y - 1); T v1 = dot (g1, p - p1);
T v3 = p3.x * (p_rem.x - 1) + p3.y * (p_rem.y - 1); T v2 = dot (g2, p - p2);
T v3 = dot (g3, p - p3);
// interpolate the results
auto L0 = L (v0, v1, p_rem.x); auto L0 = L (v0, v1, p_rem.x);
auto L1 = L (v2, v3, p_rem.x); auto L1 = L (v2, v3, p_rem.x);
auto L_ = L (L0, L1, p_rem.y); auto L_ = L (L0, L1, p_rem.y);
return L_; return L_;
} }
@ -109,10 +114,14 @@ perlin<T,L>::operator() (util::point<2,T> p) const
namespace util { namespace noise { namespace basis { namespace util { namespace noise { namespace basis {
template struct perlin<float, lerp::linear>; template struct perlin<float, lerp::linear>;
template struct perlin<float, lerp::cosine>;
template struct perlin<float, lerp::cubic>; template struct perlin<float, lerp::cubic>;
template struct perlin<float, lerp::quintic>; template struct perlin<float, lerp::quintic>;
template struct perlin<float, lerp::trunc>;
template struct perlin<double, lerp::linear>; template struct perlin<double, lerp::linear>;
template struct perlin<double, lerp::cosine>;
template struct perlin<double, lerp::cubic>; template struct perlin<double, lerp::cubic>;
template struct perlin<double, lerp::quintic>; template struct perlin<double, lerp::quintic>;
template struct perlin<double, lerp::trunc>;
} } } } } }

View File

@ -25,15 +25,19 @@ using util::noise::basis::value;
// Generate a type from [-UNIT..UNIT] // Generate a type from [-UNIT..UNIT]
template <typename T> template <typename T>
T T
generate (util::point<2,T> p, uint64_t seed) generate (util::point<2,intmax_t> p, uint64_t seed)
{ {
using util::hash::murmur2::mix; using util::hash::murmur2::mix;
T v = mix (seed, mix (uint64_t (p.y), uint64_t (p.x))) & 0xffff; T v = mix (
v = v / T{0xffff} * 2 - 1; seed,
mix (
uint64_t (p.y),
uint64_t (p.x)
)
) & 0xffff;
CHECK_GE (v, T{0}); v = v / T{0xffff} * 2 - 1;
CHECK_LE (v, T{1});
return v; return v;
} }
@ -67,25 +71,31 @@ template <typename T, util::noise::lerp_t<T> L>
T T
value<T,L>::operator() (util::point<2,T> p) const value<T,L>::operator() (util::point<2,T> p) const
{ {
// extract integer and fractional parts. be careful to always round down
// (particularly with negatives) and avoid rounding errors.
auto p_int = p.template cast<intmax_t> (); auto p_int = p.template cast<intmax_t> ();
auto p_rem = p - p_int; if (p.x < 0) p_int.x -= 1;
if (p.y < 0) p_int.y -= 1;
auto p_rem = abs (p - p_int);
// Shift the coordinate system down a little to ensure we get unit weights // generate the corner points
// for the lerp. It's better to do this than abs the fractional portion so auto p0 = p_int + util::vector<2,intmax_t> { 0, 0 };
// we don't get reflections along the origin. auto p1 = p_int + util::vector<2,intmax_t> { 1, 0 };
if (p.x < 0) { p_rem.x = 1 + p_rem.x; p_int.x -= 1; } auto p2 = p_int + util::vector<2,intmax_t> { 0, 1 };
if (p.y < 0) { p_rem.y = 1 + p_rem.y; p_int.y -= 1; } auto p3 = p_int + util::vector<2,intmax_t> { 1, 1 };
// Generate the four corner values // Generate the four corner values
T p0 = generate<T> (p_int + util::vector<2,T>{ 0, 0 }, this->seed); T g0 = generate<T> (p0, this->seed);
T p1 = generate<T> (p_int + util::vector<2,T>{ 1, 0 }, this->seed); T g1 = generate<T> (p1, this->seed);
T p2 = generate<T> (p_int + util::vector<2,T>{ 0, 0 }, this->seed); T g2 = generate<T> (p2, this->seed);
T p3 = generate<T> (p_int + util::vector<2,T>{ 1, 1 }, this->seed); T g3 = generate<T> (p3, this->seed);
// Interpolate on one dimension, then the other. // Interpolate on one dimension, then the other.
return L (L (p0, p1, p_rem.x), auto l0 = L (g0, g1, p_rem.x);
L (p2, p3, p_rem.x), auto l1 = L (g2, g3, p_rem.x);
p_rem.y); auto l_ = L (l0, l1, p_rem.y);
return l_;
} }
@ -94,6 +104,7 @@ value<T,L>::operator() (util::point<2,T> p) const
namespace util { namespace noise { namespace basis { namespace util { namespace noise { namespace basis {
template struct value<float, lerp::trunc>; template struct value<float, lerp::trunc>;
template struct value<float, lerp::cosine>;
template struct value<float, lerp::linear>; template struct value<float, lerp::linear>;
template struct value<float, lerp::cubic>; template struct value<float, lerp::cubic>;
template struct value<float, lerp::quintic>; template struct value<float, lerp::quintic>;

View File

@ -71,14 +71,12 @@ template <typename T>
T T
worley<T>::operator() (util::point<2,T> p) const worley<T>::operator() (util::point<2,T> p) const
{ {
// extract integer and fractional parts. be careful to always round down
// (particularly with negatives) and avoid rounding errors.
auto p_int = p.template cast<intmax_t> (); auto p_int = p.template cast<intmax_t> ();
auto p_rem = p - p_int; if (p.x < 0) p_int.x -= 1;
if (p.y < 0) p_int.y -= 1;
// Generate the four corner values. It's not strictly necessary to auto p_rem = abs (p - p_int);
// normalise the values, but we get a more consistent and visually
// appealing range of outputs with normalised values.
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 | // | 0 | 1 | 2 |
@ -102,7 +100,7 @@ worley<T>::operator() (util::point<2,T> p) const
CHECK (off.y >= 0 && off.y <= 1); CHECK (off.y >= 0 && off.y <= 1);
pos += off; pos += off;
*cursor++ = pos.difference2 (p_rem); *cursor++ = pos.difference2 (p_rem.template as<util::vector> ());
} }
std::sort (std::begin (distances), std::end (distances)); std::sort (std::begin (distances), std::end (distances));

View File

@ -16,6 +16,7 @@
#include "fractal.hpp" #include "fractal.hpp"
#include "basis/constant.hpp"
#include "basis/value.hpp" #include "basis/value.hpp"
#include "basis/perlin.hpp" #include "basis/perlin.hpp"
#include "basis/worley.hpp" #include "basis/worley.hpp"
@ -125,10 +126,15 @@ fbm<T,B>::operator() (util::point<2,T> p) const {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
template struct util::noise::fbm<float, util::noise::basis::worley<float>>; template struct util::noise::fbm<float, util::noise::basis::worley<float>>;
template struct util::noise::fbm<float, util::noise::basis::constant<float>>;
template struct util::noise::fbm<float, util::noise::basis::perlin<float,util::lerp::trunc>>;
template struct util::noise::fbm<float, util::noise::basis::perlin<float,util::lerp::linear>>; template struct util::noise::fbm<float, util::noise::basis::perlin<float,util::lerp::linear>>;
template struct util::noise::fbm<float, util::noise::basis::perlin<float,util::lerp::cubic>>;
template struct util::noise::fbm<float, util::noise::basis::perlin<float,util::lerp::quintic>>; template struct util::noise::fbm<float, util::noise::basis::perlin<float,util::lerp::quintic>>;
template struct util::noise::fbm<float, util::noise::basis::value<float,util::lerp::trunc>>; template struct util::noise::fbm<float, util::noise::basis::value<float,util::lerp::trunc>>;
template struct util::noise::fbm<float, util::noise::basis::value<float,util::lerp::linear>>; template struct util::noise::fbm<float, util::noise::basis::value<float,util::lerp::linear>>;
template struct util::noise::fbm<float, util::noise::basis::value<float,util::lerp::cosine>>;
template struct util::noise::fbm<float, util::noise::basis::value<float,util::lerp::cubic>>;
template struct util::noise::fbm<float, util::noise::basis::value<float,util::lerp::quintic>>; template struct util::noise::fbm<float, util::noise::basis::value<float,util::lerp::quintic>>;
template struct util::noise::fbm<double, util::noise::basis::worley<double>>; template struct util::noise::fbm<double, util::noise::basis::worley<double>>;