noise: use permutation random generator

much faster for common operations
This commit is contained in:
Danny Robson 2015-10-06 21:19:17 +11:00
parent e80e445645
commit b464f089a5
15 changed files with 239 additions and 46 deletions

View File

@ -186,6 +186,10 @@ UTIL_FILES = \
noise/midpoint.hpp \
noise/rand.hpp \
noise/rand.ipp \
noise/rand/hash.hpp \
noise/rand/hash.ipp \
noise/rand/permute.cpp \
noise/rand/permute.hpp \
noise/turbulence.hpp \
noise/turbulence.ipp \
pascal.cpp \

View File

@ -38,7 +38,7 @@ namespace util { namespace noise { namespace basis {
vector<S,T>
expgrad<S,T,L>::generate (pointi<S> p) const
{
auto t = (noise::rand<float> (this->seed (), p) + 1) / 2;
auto t = rand::scalar<float> (this->seed (), p);
auto factor = std::pow (m_base, -t * m_exponent);
return factor * gradient<S,T,L>::generate (p);
}

View File

@ -52,7 +52,7 @@ namespace util { namespace noise { namespace basis {
vector<S,T>
gradient<S,T,L>::generate (pointi<S> p) const
{
return noise::rand<vector,T> (m_seed, p);
return rand::coord<vector,T> (m_seed, p) * 2 - 1;
}
} } }

View File

@ -61,7 +61,7 @@ namespace util { namespace noise { namespace basis {
std::transform (std::begin (this->OFFSETS),
std::end (this->OFFSETS),
std::begin (centres),
[this,p_int] (auto i) { return (noise::rand<point,T> (m_seed, p_int + i) + 1) / 2 + i; });
[this,p_int] (auto i) { return rand::coord<point,T> (m_seed, p_int + i) + i; });
T distances[COUNT];
std::transform (std::begin (centres),
@ -96,7 +96,7 @@ namespace util { namespace noise { namespace basis {
// to blend.
for (size_t i = 0; i < hi_off; ++i)
{
auto v = util::noise::rand<T> (
auto v = rand::scalar<T> (
m_seed,
p_int + this->OFFSETS[indices[i]]
);

View File

@ -19,7 +19,6 @@
#endif
#define __UTIL_NOISE_BASIS_PERLIN_IPP
#include "../rand.hpp"
#include "../../types.hpp"

View File

@ -77,7 +77,7 @@ namespace util { namespace noise { namespace basis {
std::array<T,pow(2,S)> g_;
std::transform (std::begin (p_), std::end (p_),
std::begin (g_),
[this] (auto i) { return noise::rand<float> (m_seed, i); });
[this] (auto i) { return rand::scalar<float> (m_seed, i) * 2 - 1; });
// Interpolate on one dimension, then the other.
T l_[pow(2,S)];

View File

@ -91,6 +91,6 @@ namespace util { namespace noise { namespace basis {
point<S,T>
worley<S,T,F>::generate (point<S,intmax_t> p) const
{
return (noise::rand<util::point,T> (m_seed, p) + 1) / 2;
return rand::coord<util::point,T> (m_seed, p);
}
} } }

View File

@ -60,10 +60,10 @@ fill (util::image::buffer<T> &img,
// do the centre
{
const auto avg = (v0 + v1 + v2 + v3) / 4;
const auto val = avg + scale * util::noise::rand<T> (seed, target.centre ());
const auto val = avg + scale * util::noise::rand::scalar<T> (seed, target.centre ());
const auto pos = target.p + target.e / 2;
img[pos] = val;
img[pos] = val * 2 - 1;
}
// average the sides
@ -73,15 +73,15 @@ fill (util::image::buffer<T> &img,
const auto p32 = target.p + util::vector2u{w/2, h-1};
const auto p20 = target.p + util::vector2u{0, h/2};
const auto v01 = (v0 + v1) / 2 + sides * scale * util::noise::rand<T> (seed, p01);
const auto v13 = (v1 + v3) / 2 + sides * scale * util::noise::rand<T> (seed, p13);
const auto v32 = (v3 + v2) / 2 + sides * scale * util::noise::rand<T> (seed, p32);
const auto v20 = (v2 + v0) / 2 + sides * scale * util::noise::rand<T> (seed, p20);
const auto v01 = (v0 + v1) / 2 + sides * scale * util::noise::rand::scalar<T> (seed, p01);
const auto v13 = (v1 + v3) / 2 + sides * scale * util::noise::rand::scalar<T> (seed, p13);
const auto v32 = (v3 + v2) / 2 + sides * scale * util::noise::rand::scalar<T> (seed, p32);
const auto v20 = (v2 + v0) / 2 + sides * scale * util::noise::rand::scalar<T> (seed, p20);
img[p01] = v01;
img[p13] = v13;
img[p32] = v32;
img[p20] = v20;
img[p01] = v01 * 2 - 1;
img[p13] = v13 * 2 - 1;
img[p32] = v32 * 2 - 1;
img[p20] = v20 * 2 - 1;
}
// recurse
@ -109,7 +109,7 @@ util::noise::midpoint (image::buffer<T> &img, uint64_t seed, float persistence,
};
for (auto i: CORNERS)
img[i] = util::noise::rand<T> (seed, i);
img[i] = util::noise::rand::scalar<T> (seed, i) * 2 - 1;
fill (img, seed, { { 0, 0 }, img.extent () }, 1.f, persistence, sides);
}

View File

@ -17,8 +17,11 @@
#ifndef __UTIL_NOISE_RAND_HPP
#define __UTIL_NOISE_RAND_HPP
namespace util { namespace noise {
/// generate a uniform random floating point in the range [-1, 1] from a seed and vector
#include "./rand/permute.hpp"
#include "./rand/hash.hpp"
namespace util { namespace noise { namespace rand {
/// generate a uniform random floating point in the range [0, 1] from a seed and vector
template <
typename U,
size_t S,
@ -26,8 +29,16 @@ namespace util { namespace noise {
template <size_t,typename> class Q
>
U
rand (uint64_t seed, Q<S,T> value);
scalar (uint64_t seed, Q<S,T> value)
{
#if 1
return permute::scalar<U> (seed, value);
#else
return hash::scalar<U> (seed, value);
#endif
}
/// generate a coordinate type with uniform random components in the range [0, 1]
template <
template <size_t,typename> class R,
typename U,
@ -36,9 +47,14 @@ namespace util { namespace noise {
template <size_t,typename> class Q
>
R<S,U>
rand (uint64_t seed, Q<S,T> value);
} }
#include "rand.ipp"
coord (uint64_t seed, Q<S,T> value)
{
#if 1
return permute::coord<R,U,S,T,Q> (seed, value);
#else
return hash::coord<R,U,S,T,Q> (seed, value);
#endif
}
} } }
#endif

49
noise/rand/hash.hpp Normal file
View File

@ -0,0 +1,49 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_NOISE_RAND_HASH_HPP
#define __UTIL_NOISE_RAND_HASH_HPP
///////////////////////////////////////////////////////////////////////////////
namespace util { namespace noise { namespace rand {
struct hash {
/// generate a uniform random floating point in the range [0, 1] from a seed and vector
template <
typename U,
size_t S,
typename T,
template <size_t,typename> class Q
>
static U
scalar (uint64_t seed, Q<S,T> value);
/// generate a coordinate type with uniform random components in the range [0, 1]
template <
template <size_t,typename> class R,
typename U,
size_t S,
typename T,
template <size_t,typename> class Q
>
static R<S,U>
coord (uint64_t seed, Q<S,T> value);
};
} } }
#include "./hash.hpp"
#endif

View File

@ -15,15 +15,13 @@
*/
#ifdef __UTIL_NOISE_RAND_IPP
#ifdef __UTIL_NOISE_RAND_HASH_IPP
#error
#endif
#define __UTIL_NOISE_RAND_IPP
#include "../hash/murmur/murmur2.hpp"
#define __UTIL_NOISE_RAND_HASH_IPP
namespace util { namespace noise {
namespace util { namespace noise { namespace rand {
//-------------------------------------------------------------------------
template <
typename U,
@ -32,21 +30,17 @@ namespace util { namespace noise {
template <size_t,typename> class Q
>
U
rand (uint64_t seed, Q<S,T> query)
hash::scalar (uint64_t seed, Q<S,T> query)
{
constexpr decltype(seed) BITS = 0xFFFF;
static_assert (std::is_integral<T>::value,
"mixing only works on integral types");
uint64_t accum = seed;
for (auto i: query)
accum = hash::murmur2::mix (accum, i);
accum = util::hash::murmur2::mix (accum, i);
U result = (accum & 0xFFFF) / U{0xFFFF};
result *= 2;
result -= 1;
return result;
return (accum & BITS) / U{BITS};
}
@ -59,18 +53,22 @@ namespace util { namespace noise {
template <size_t,typename> class Q
>
R<S,U>
rand (uint64_t seed, Q<S,T> query)
hash::coord (uint64_t seed, Q<S,T> query)
{
uint64_t accum = seed;
constexpr decltype(seed) PERTURB = 0x96c39996c36c36c3;
constexpr decltype(seed) BITS = 0xFFFF;
auto accum = seed ^ PERTURB;
for (auto i: query)
accum = hash::murmur2::mix (accum, i);
accum = util::hash::murmur2::mix (accum, i);
R<S,U> result;
for (auto &i: result) {
i = (accum & 0xFFFF) / U{0xFFFF};
accum = hash::murmur2::mix (accum, seed);
i = (accum & BITS);
i /= BITS;
accum = util::hash::murmur2::mix (accum, seed);
}
return result * 2 - 1;
return result;
}
} }
} } }

25
noise/rand/permute.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "./permute.hpp"
using util::noise::rand::permute;
// static permutation offsets. consists of numbers [0,255] in a randomised
// pattern. the exact arrangement is irrelevant.
const std::array<uint8_t,256> permute::PERMUTE = {
148, 94, 173, 74, 199, 189, 102, 184, 149, 18, 177, 211, 16, 221, 252, 232,
82, 104, 164, 196, 78, 4, 247, 11, 206, 176, 137, 45, 30, 118, 72, 59,
39, 198, 158, 220, 213, 230, 197, 209, 145, 172, 188, 10, 214, 144, 241, 242,
84, 128, 168, 229, 64, 32, 20, 249, 236, 194, 253, 183, 132, 6, 43, 96,
181, 27, 127, 106, 63, 21, 134, 120, 170, 166, 93, 191, 70, 240, 46, 85,
23, 56, 228, 28, 160, 67, 175, 161, 36, 61, 204, 71, 140, 103, 190, 66,
100, 130, 192, 251, 174, 201, 79, 169, 231, 53, 48, 50, 119, 178, 87, 205,
243, 165, 207, 13, 116, 235, 24, 224, 47, 81, 195, 218, 97, 83, 238, 227,
113, 180, 33, 111, 126, 200, 153, 5, 146, 112, 179, 255, 250, 19, 92, 80,
135, 222, 95, 22, 142, 40, 15, 107, 217, 41, 77, 110, 210, 246, 185, 54,
163, 105, 114, 193, 215, 73, 150, 129, 208, 155, 171, 154, 115, 0, 8, 233,
162, 138, 109, 157, 248, 26, 25, 244, 31, 62, 75, 35, 29, 60, 182, 156,
55, 223, 125, 51, 76, 88, 9, 151, 117, 122, 38, 159, 124, 49, 44, 254,
202, 147, 225, 219, 90, 234, 133, 212, 14, 7, 167, 2, 91, 68, 187, 216,
65, 42, 69, 245, 123, 152, 89, 52, 37, 121, 143, 1, 57, 226, 3, 239,
99, 98, 108, 186, 17, 139, 203, 58, 86, 131, 141, 136, 34, 101, 12, 237
};

55
noise/rand/permute.hpp Normal file
View File

@ -0,0 +1,55 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_NOISE_RAND_PERMUTE_HPP
#define __UTIL_NOISE_RAND_PERMUTE_HPP
#include <array>
///////////////////////////////////////////////////////////////////////////////
namespace util { namespace noise { namespace rand {
struct permute {
/// generate a uniform random floating point in the range [0, 1] from a seed and vector
template <
typename U,
size_t S,
typename T,
template <size_t,typename> class Q
>
static U
scalar (uint64_t seed, Q<S,T> value);
/// generate a coordinate type with uniform random components in the range [0, 1]
template <
template <size_t,typename> class R,
typename U,
size_t S,
typename T,
template <size_t,typename> class Q
>
static R<S,U>
coord (uint64_t seed, Q<S,T> value);
private:
static const std::array<uint8_t, 256> PERMUTE;
};
} } }
#include "./permute.ipp"
#endif

47
noise/rand/permute.ipp Normal file
View File

@ -0,0 +1,47 @@
///////////////////////////////////////////////////////////////////////////////
namespace util { namespace noise { namespace rand {
template <
typename U,
size_t S,
typename T,
template <size_t,typename> class Q
>
U
permute::scalar (uint64_t seed, Q<S,T> query)
{
size_t idx = PERMUTE[seed&0xff];
for (auto i: query)
idx = PERMUTE[(idx+i)&0xff];
return PERMUTE[idx] / U(0xff);
}
//-------------------------------------------------------------------------
template <
template <size_t,typename> class R,
typename U,
size_t S,
typename T,
template <size_t,typename> class Q
>
R<S,U>
permute::coord (uint64_t seed, Q<S,T> query)
{
auto accum = seed;
for (auto q: query) {
size_t idx = q + accum;
accum = PERMUTE[idx & 0xff];
}
R<S,U> res;
for (size_t i = 0; i < S; ++i) {
res[i] = PERMUTE[accum];
res[i] /= 0xff;
accum = PERMUTE[(accum + i) & 0xff];
}
return res;
}
} } }

View File

@ -25,7 +25,7 @@
#include "region.hpp"
constexpr size_t S = 3;
constexpr size_t S = 2;
///////////////////////////////////////////////////////////////////////////////
template struct util::noise::fractal::fbm<S,float, util::noise::basis::perlin<S,float,util::lerp::cubic>>;