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>
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;
@ -75,31 +75,36 @@ template <typename T, util::noise::lerp_t<T> L>
T
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_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
// for the lerp. It's better to do this than abs the fractional portion so
// we don't get reflections along the origin.
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 corner positions
auto p0 = p_int + util::vector<2,intmax_t> { 0, 0 };
auto p1 = p_int + util::vector<2,intmax_t> { 1, 0 };
auto p2 = p_int + util::vector<2,intmax_t> { 0, 1 };
auto p3 = p_int + util::vector<2,intmax_t> { 1, 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<T> (p_int + util::vector<2,T> { 0, 0 }, this->seed).normalise ();
auto p1 = generate<T> (p_int + util::vector<2,T> { 1, 0 }, this->seed).normalise ();
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 ();
// generate the corner gradients
auto g0 = generate<T> (p0, this->seed);
auto g1 = generate<T> (p1, this->seed);
auto g2 = generate<T> (p2, this->seed);
auto g3 = generate<T> (p3, this->seed);
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);
// compute the dot products
T v0 = dot (g0, p - p0);
T v1 = dot (g1, p - p1);
T v2 = dot (g2, p - p2);
T v3 = dot (g3, p - p3);
// interpolate the results
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_;
}
@ -109,10 +114,14 @@ perlin<T,L>::operator() (util::point<2,T> p) const
namespace util { namespace noise { namespace basis {
template struct perlin<float, lerp::linear>;
template struct perlin<float, lerp::cosine>;
template struct perlin<float, lerp::cubic>;
template struct perlin<float, lerp::quintic>;
template struct perlin<float, lerp::trunc>;
template struct perlin<double, lerp::linear>;
template struct perlin<double, lerp::cosine>;
template struct perlin<double, lerp::cubic>;
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]
template <typename 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;
T v = mix (seed, mix (uint64_t (p.y), uint64_t (p.x))) & 0xffff;
v = v / T{0xffff} * 2 - 1;
T v = mix (
seed,
mix (
uint64_t (p.y),
uint64_t (p.x)
)
) & 0xffff;
CHECK_GE (v, T{0});
CHECK_LE (v, T{1});
v = v / T{0xffff} * 2 - 1;
return v;
}
@ -67,25 +71,31 @@ template <typename T, util::noise::lerp_t<T> L>
T
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_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
// for the lerp. It's better to do this than abs the fractional portion so
// we don't get reflections along the origin.
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 corner points
auto p0 = p_int + util::vector<2,intmax_t> { 0, 0 };
auto p1 = p_int + util::vector<2,intmax_t> { 1, 0 };
auto p2 = p_int + util::vector<2,intmax_t> { 0, 1 };
auto p3 = p_int + util::vector<2,intmax_t> { 1, 1 };
// Generate the four corner values
T p0 = generate<T> (p_int + util::vector<2,T>{ 0, 0 }, this->seed);
T p1 = generate<T> (p_int + util::vector<2,T>{ 1, 0 }, this->seed);
T p2 = generate<T> (p_int + util::vector<2,T>{ 0, 0 }, this->seed);
T p3 = generate<T> (p_int + util::vector<2,T>{ 1, 1 }, this->seed);
T g0 = generate<T> (p0, this->seed);
T g1 = generate<T> (p1, this->seed);
T g2 = generate<T> (p2, this->seed);
T g3 = generate<T> (p3, this->seed);
// Interpolate on one dimension, then the other.
return L (L (p0, p1, p_rem.x),
L (p2, p3, p_rem.x),
p_rem.y);
auto l0 = L (g0, g1, p_rem.x);
auto l1 = L (g2, g3, p_rem.x);
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 {
template struct value<float, lerp::trunc>;
template struct value<float, lerp::cosine>;
template struct value<float, lerp::linear>;
template struct value<float, lerp::cubic>;
template struct value<float, lerp::quintic>;

View File

@ -71,14 +71,12 @@ template <typename T>
T
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_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 (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; }
if (p.x < 0) p_int.x -= 1;
if (p.y < 0) p_int.y -= 1;
auto p_rem = abs (p - p_int);
// +---+---+---+
// | 0 | 1 | 2 |
@ -102,7 +100,7 @@ worley<T>::operator() (util::point<2,T> p) const
CHECK (off.y >= 0 && off.y <= 1);
pos += off;
*cursor++ = pos.difference2 (p_rem);
*cursor++ = pos.difference2 (p_rem.template as<util::vector> ());
}
std::sort (std::begin (distances), std::end (distances));

View File

@ -16,6 +16,7 @@
#include "fractal.hpp"
#include "basis/constant.hpp"
#include "basis/value.hpp"
#include "basis/perlin.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::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::cubic>>;
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::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<double, util::noise::basis::worley<double>>;