Browse Source

Simplify noise function signatures and provide default parameter values

master
C. J. Howard 2 years ago
parent
commit
94d9142ebf
8 changed files with 159 additions and 108 deletions
  1. +17
    -22
      src/game/load.cpp
  2. +1
    -0
      src/math/hash/hash.hpp
  3. +68
    -0
      src/math/hash/make-uint.hpp
  4. +11
    -37
      src/math/hash/pcg.hpp
  5. +6
    -4
      src/math/noise/fbm.hpp
  6. +14
    -8
      src/math/noise/simplex.hpp
  7. +36
    -37
      src/math/noise/voronoi.hpp
  8. +6
    -0
      src/math/vector.hpp

+ 17
- 22
src/game/load.cpp View File

@ -48,6 +48,7 @@ void biome(game::context& ctx, const std::filesystem::path& path)
{
ctx.logger->push_task("Loading biome from \"" + path.string() + "\"");
/*
image img;
img.format(1, 1);
img.resize(1024, 1024);
@ -113,7 +114,7 @@ void biome(game::context& ctx, const std::filesystem::path& path)
// f2_displacement,
// f2_id
edge_sqr_distance
] = math::noise::voronoi::f1_edge<float, 1, std::uint32_t>({position[0]}, 1.0f, {0.0f}, &math::hash::pcg);
] = math::noise::voronoi::f1_edge<float, 2>(position);
float f1_distance = std::sqrt(f1_sqr_distance);
//float f2_distance = std::sqrt(f2_sqr_distance);
@ -126,7 +127,7 @@ void biome(game::context& ctx, const std::filesystem::path& path)
stbi_flip_vertically_on_write(1);
stbi_write_png((ctx.config_path / "gallery" / "noise.png").string().c_str(), img.get_width(), img.get_height(), img.get_channel_count(), img.data(), img.get_width() * img.get_channel_count());
*/
try
{
@ -194,38 +195,32 @@ void biome(game::context& ctx, const std::filesystem::path& path)
(
[](float x, float z) -> float
{
float angle = math::radians(30.0f);
float c = std::cos(angle);
float s = std::sin(angle);
x = x * c - z * s;
z = x * s + z * c;
float frequency = 0.01f;
std::size_t octaves = 4;
float lacunarity = 3.0f;
float gain = 0.5f;
auto noise = static_cast<float(*)(const math::vector<float, 2>&, decltype(hash))>(math::noise::simplex);
auto hash = static_cast<math::vector<std::uint32_t, 2>(*)(const math::vector<float, 2>&)>(math::hash::pcg);
float2 position = float2{x, z} * frequency;
/*
float n = math::noise::fbm
float fbm = math::noise::fbm
(
position,
octaves,
lacunarity,
gain,
noise,
hash
gain
);
*/
//float n = math::noise::voronoi::f1<float, std::uint32_t>(position, 1.0f, &math::noise::hash::pcg3d_3)[0];
float n = 0.0f;
return 10.0f * n;
auto
[
f1_sqr_distance,
f1_displacement,
f1_id
] = math::noise::voronoi::f1(position);
float f1_distance = std::sqrt(f1_sqr_distance);
float y = f1_distance * 5.0f + fbm * 0.5f;
return y;
}
);

+ 1
- 0
src/math/hash/hash.hpp View File

@ -27,6 +27,7 @@ namespace hash {}
} // namespace math
#include "math/hash/make-uint.hpp"
#include "math/hash/pcg.hpp"
#endif // ANTKEEPER_MATH_HASH_HPP

+ 68
- 0
src/math/hash/make-uint.hpp View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ANTKEEPER_MATH_HASH_MAKE_UINT_HPP
#define ANTKEEPER_MATH_HASH_MAKE_UINT_HPP
#include <cstdint>
#include <type_traits>
namespace math {
namespace hash {
/**
* Provides an unsigned integer type of equivalent size to type @p T.
*/
template <class T>
struct make_uint
{
static_assert(std::is_integral<T>::value);
/// Unsigned integer type of equivalent size to type @p T.
typedef typename std::make_unsigned<T>::type type;
};
/// Provides an unsigned integer type of equivalent size to `float`.
template<>
struct make_uint<float>
{
static_assert(sizeof(float) == sizeof(std::uint32_t));
/// Unsigned integer type of equivalent size to `float`.
typedef std::uint32_t type;
};
/// Provides an unsigned integer type of equivalent size to `double`.
template<>
struct make_uint<double>
{
static_assert(sizeof(double) == sizeof(std::uint64_t));
/// Unsigned integer type of equivalent size to `double`.
typedef std::uint64_t type;
};
/// Helper type for make_uint.
template <class T>
using make_uint_t = typename make_uint<T>::type;
} // namespace hash
} // namespace math
#endif // ANTKEEPER_MATH_HASH_MAKE_UINT_HPP

+ 11
- 37
src/math/hash/pcg.hpp View File

@ -21,42 +21,13 @@
#define ANTKEEPER_MATH_HASH_PCG_HPP
#include "math/vector.hpp"
#include "math/hash/make-uint.hpp"
#include <cstdint>
#include <type_traits>
namespace math {
namespace hash {
/**
* Provides an unsigned integer type of equivalent size to type @p T.
*/
template <class T>
struct pcg_make_uint
{
static_assert(std::is_integral<T>::value);
/// Unsigned integer type of equivalent size to type @p T.
typedef typename std::make_unsigned<T>::type type;
};
/// Provides an unsigned integer type of equivalent to `float`.
template<>
struct pcg_make_uint<float>
{
typedef std::uint32_t type;
};
/// Provides an unsigned integer type of equivalent to `double`.
template<>
struct pcg_make_uint<double>
{
typedef std::uint64_t type;
};
/// Helper type for pcg_make_uint.
template <class T>
using pcg_make_uint_t = typename pcg_make_uint<T>::type;
/// @private
template <class T>
constexpr T pcg_multiplier = 0;
@ -197,7 +168,10 @@ constexpr vector pcg_uvec4(vector x) noexcept
*
* @param x Input value.
*
* @return Pseudorandom output value.
* @return Unsigned pseudorandom output value.
*
* @warning Floating point and signed input values will be converted to unsigned integers via `static_cast`.
* @warning Vectors with more than 4 elements are not supported.
*
* @see https://en.wikipedia.org/wiki/Permuted_congruential_generator
* @see O'Neill, M.E. (2014). PCG : A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation.
@ -255,18 +229,18 @@ inline constexpr std::uint64_t pcg(double x) noexcept
}
template <class T, std::size_t N>
inline constexpr vector<pcg_make_uint_t<T>, N> pcg(const vector<T, N>& x) noexcept
inline constexpr vector<make_uint_t<T>, N> pcg(const vector<T, N>& x) noexcept
{
static_assert(N > 0 && N < 5, "Dimension not supported by PCG hash.");
static_assert(N > 0 && N < 5, "PCG hash only supports vectors with 1-4 elements.");
if constexpr (N == 1)
return pcg_uvec1<pcg_make_uint_t<T>>(vector<pcg_make_uint_t<T>, N>(x));
return pcg_uvec1<make_uint_t<T>>(vector<make_uint_t<T>, N>(x));
else if constexpr (N == 2)
return pcg_uvec2<pcg_make_uint_t<T>>(vector<pcg_make_uint_t<T>, N>(x));
return pcg_uvec2<make_uint_t<T>>(vector<make_uint_t<T>, N>(x));
else if constexpr (N == 3)
return pcg_uvec3<pcg_make_uint_t<T>>(vector<pcg_make_uint_t<T>, N>(x));
return pcg_uvec3<make_uint_t<T>>(vector<make_uint_t<T>, N>(x));
else
return pcg_uvec4<pcg_make_uint_t<T>>(vector<pcg_make_uint_t<T>, N>(x));
return pcg_uvec4<make_uint_t<T>>(vector<make_uint_t<T>, N>(x));
}
/// @}

+ 6
- 4
src/math/noise/fbm.hpp View File

@ -20,6 +20,9 @@
#ifndef ANTKEEPER_MATH_NOISE_FBM_HPP
#define ANTKEEPER_MATH_NOISE_FBM_HPP
#include "math/hash/make-uint.hpp"
#include "math/hash/pcg.hpp"
#include "math/noise/simplex.hpp"
#include "math/vector.hpp"
#include <cstdint>
@ -31,7 +34,6 @@ namespace noise {
*
* @tparam T Real type.
* @tparam N Number of dimensions.
* @tparam U Hash function return type.
*
* @param x n-dimensional input value.
* @param octaves Number of octaves.
@ -41,15 +43,15 @@ namespace noise {
* @param hash Hash function.
*
*/
template <class T, std::size_t N, class U>
template <class T, std::size_t N>
T fbm
(
vector<T, N> x,
std::size_t octaves,
T lacunarity,
T gain,
T (*noise)(const vector<T, N>&, vector<U, N> (*)(const vector<T, N>&)),
vector<U, N> (*hash)(const vector<T, N>&)
T (*noise)(const vector<T, N>&, vector<hash::make_uint_t<T>, N> (*)(const vector<T, N>&)) = &simplex<T, N>,
vector<hash::make_uint_t<T>, N> (*hash)(const vector<T, N>&) = &hash::pcg<T, N>
)
{
T amplitude{1};

+ 14
- 8
src/math/noise/simplex.hpp View File

@ -21,6 +21,8 @@
#define ANTKEEPER_MATH_NOISE_SIMPLEX_HPP
#include "math/vector.hpp"
#include "math/hash/make-uint.hpp"
#include "math/hash/pcg.hpp"
#include <algorithm>
#include <utility>
@ -109,8 +111,12 @@ constexpr auto simplex_edges = make_simplex_edges(std::make_index_sequence
* @see https://briansharpe.wordpress.com/2011/11/14/two-useful-interpolation-functions-for-noise-development/
* @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116
*/
template <class T, std::size_t N, class U>
T simplex(const vector<T, N>& x, vector<U, N> (*hash)(const vector<T, N>&))
template <class T, std::size_t N>
T simplex
(
const vector<T, N>& x,
vector<hash::make_uint_t<T>, N> (*hash)(const vector<T, N>&) = &hash::pcg<T, N>
)
{
// Skewing (F) and unskewing (G) factors
static const T f = (std::sqrt(static_cast<T>(N + 1)) - T{1}) / static_cast<T>(N);
@ -137,13 +143,13 @@ T simplex(const vector& x, vector (*hash)(const vector&))
static const T corner_normalization = T{1} / ((static_cast<T>(N) / std::sqrt(static_cast<T>(N + 1))) * falloff(static_cast<T>(N) / (T{4} * static_cast<T>(N + 1))));
// Adjust normalization factor for difference in length between corner gradient vectors and edge gradient vectors
static const T edge_normalization = corner_normalization * (std::sqrt(static_cast<T>(N)) / math::length(simplex_edges<T, N>[0]));
static const T edge_normalization = corner_normalization * (std::sqrt(static_cast<T>(N)) / length(simplex_edges<T, N>[0]));
// Skew input position to get the origin vertex of the unit hypercube cell to which they belong
const vector<T, N> origin_vertex = math::floor(x + math::sum(x) * f);
const vector<T, N> origin_vertex = floor(x + sum(x) * f);
// Displacement vector from origin vertex position to input position
const vector<T, N> dx = x - origin_vertex + math::sum(origin_vertex) * g;
const vector<T, N> dx = x - origin_vertex + sum(origin_vertex) * g;
// Find axis traversal order
vector<std::size_t, N> axis_order;
@ -170,13 +176,13 @@ T simplex(const vector& x, vector (*hash)(const vector&))
const vector<T, N> d = dx - (current_vertex - origin_vertex) + g * static_cast<T>(i);
// Calculate falloff
T t = falloff(math::length_squared(d));
T t = falloff(length_squared(d));
if (t > T{0})
{
const auto gradient_index = hash(current_vertex)[0] % simplex_edges<T, N>.size();
const hash::make_uint_t<T> gradient_index = hash(current_vertex)[0] % simplex_edges<T, N>.size();
const vector<T, N>& gradient = simplex_edges<T, N>[gradient_index];
n += math::dot(d, gradient) * t;
n += dot(d, gradient) * t;
}
}

+ 36
- 37
src/math/noise/voronoi.hpp View File

@ -21,6 +21,8 @@
#define ANTKEEPER_MATH_NOISE_VORONOI_HPP
#include "math/vector.hpp"
#include "math/hash/make-uint.hpp"
#include "math/hash/pcg.hpp"
#include <algorithm>
#include <array>
#include <cmath>
@ -96,7 +98,6 @@ constexpr auto kernel = generate_kernel(std::make_index_sequence
*
* @tparam T Real type.
* @tparam N Number of dimensions.
* @tparam U Hash function return type.
*
* @param position Input position.
* @param randomness Degree of randomness, on `[0, 1]`.
@ -105,7 +106,7 @@ constexpr auto kernel = generate_kernel(std::make_index_sequence
*
* @return Tuple containing the square Euclidean distance from @p position to the F1 cell, the displacement vector from the input position to the F1 cell center, and a hash value indicating the ID of the F1 cell.
*/
template <class T, std::size_t N, class U>
template <class T, std::size_t N>
std::tuple
<
// F1 square distance to center
@ -115,27 +116,27 @@ std::tuple
vector<T, N>,
// F1 hash
U
hash::make_uint_t<T>
>
f1
(
const vector<T, N>& position,
T randomness,
const vector<T, N>& tiling,
vector<U, N> (*hash)(const vector<T, N>&)
T randomness = T{1},
const vector<T, N>& tiling = vector<T, N>::zero(),
vector<hash::make_uint_t<T>, N> (*hash)(const vector<T, N>&) = &hash::pcg<T, N>
)
{
// Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness
T hash_scale = (T{1} / static_cast<T>(std::numeric_limits<U>::max())) * randomness;
T hash_scale = (T{1} / static_cast<T>(std::numeric_limits<hash::make_uint_t<T>>::max())) * randomness;
// Get integer and fractional parts
vector<T, N> position_i = math::floor(position - T{1.5});
vector<T, N> position_i = floor(position - T{1.5});
vector<T, N> position_f = position - position_i;
// Find the F1 cell
T f1_sqr_distance = std::numeric_limits<T>::infinity();
vector<T, N> f1_displacement;
U f1_hash;
hash::make_uint_t<T> f1_hash;
for (std::size_t i = 0; i < kernel_size<N>; ++i)
{
// Get kernel offset for current cell
@ -150,7 +151,7 @@ f1
}
// Calculate hash values for the hash position
vector<U, N> hash_i = hash(hash_position);
vector<hash::make_uint_t<T>, N> hash_i = hash(hash_position);
// Convert hash values to pseudorandom fractional offset
vector<T, N> offset_f = vector<T, N>(hash_i) * hash_scale;
@ -159,7 +160,7 @@ f1
vector<T, N> displacement = (offset_i + offset_f) - position_f;
// Calculate square distance to the current cell center
T sqr_distance = math::length_squared(displacement);
T sqr_distance = length_squared(displacement);
// Update F1 cell
if (sqr_distance < f1_sqr_distance)
@ -183,7 +184,6 @@ f1
*
* @tparam T Real type.
* @tparam N Number of dimensions.
* @tparam U Hash function return type.
*
* @param position Input position.
* @param randomness Degree of randomness, on `[0, 1]`.
@ -192,7 +192,7 @@ f1
*
* @return Tuple containing the square Euclidean distance from @p position to the F1 cell center, the displacement vector from the input position to the F1 cell center, a hash value indicating the ID of the F1 cell, and the square Euclidean distance from @p position to the nearest edge.
*/
template <class T, std::size_t N, class U>
template <class T, std::size_t N>
std::tuple
<
// F1 square distance to center
@ -202,7 +202,7 @@ std::tuple
vector<T, N>,
// F1 hash
U,
hash::make_uint_t<T>,
// Edge square distance
T
@ -210,23 +210,23 @@ std::tuple
f1_edge
(
const vector<T, N>& position,
T randomness,
const vector<T, N>& tiling,
vector<U, N> (*hash)(const vector<T, N>&)
T randomness = T{1},
const vector<T, N>& tiling = vector<T, N>::zero(),
vector<hash::make_uint_t<T>, N> (*hash)(const vector<T, N>&) = &hash::pcg<T, N>
)
{
// Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness
T hash_scale = (T{1} / static_cast<T>(std::numeric_limits<U>::max())) * randomness;
T hash_scale = (T{1} / static_cast<T>(std::numeric_limits<hash::make_uint_t<T>>::max())) * randomness;
// Get integer and fractional parts
vector<T, N> position_i = math::floor(position - T{1.5});
vector<T, N> position_i = floor(position - T{1.5});
vector<T, N> position_f = position - position_i;
// Find F1 cell
T f1_sqr_distance_center = std::numeric_limits<T>::infinity();
vector<T, N> displacement_cache[kernel_size<N>];
std::size_t f1_i = 0;
U f1_hash;
hash::make_uint_t<T> f1_hash;
for (std::size_t i = 0; i < kernel_size<N>; ++i)
{
// Get kernel offset for current cell
@ -241,7 +241,7 @@ f1_edge
}
// Calculate hash values for the hash position
vector<U, N> hash_i = hash(hash_position);
vector<hash::make_uint_t<T>, N> hash_i = hash(hash_position);
// Convert hash values to pseudorandom fractional offset
vector<T, N> offset_f = vector<T, N>(hash_i) * hash_scale;
@ -250,7 +250,7 @@ f1_edge
displacement_cache[i] = (offset_i + offset_f) - position_f;
// Calculate square distance to the current cell center
T sqr_distance = math::length_squared(displacement_cache[i]);
T sqr_distance = length_squared(displacement_cache[i]);
// Update F1 cell
if (sqr_distance < f1_sqr_distance_center)
@ -279,10 +279,10 @@ f1_edge
const vector<T, N> midpoint = (f1_displacement + displacement) * T{0.5};
// Calculate direction from the F1 cell to current cell
const vector<T, N> direction = math::normalize(displacement - f1_displacement);
const vector<T, N> direction = normalize(displacement - f1_displacement);
// Calculate square distance to the edge
const T sqr_distance = math::dot(midpoint, direction);
const T sqr_distance = dot(midpoint, direction);
// Update minimum edge distance if closer than the nearest edge
if (sqr_distance < edge_sqr_distance_edge)
@ -303,7 +303,6 @@ f1_edge
*
* @tparam T Real type.
* @tparam N Number of dimensions.
* @tparam U Hash function return type.
*
* @param position Input position.
* @param randomness Degree of randomness, on `[0, 1]`.
@ -312,7 +311,7 @@ f1_edge
*
* @return Tuple containing the square Euclidean distances, displacement vectors from the input position to the cell centers, and hash values indicating the cell IDs, for both the F1 and F2 cells.
*/
template <class T, std::size_t N, class U>
template <class T, std::size_t N>
std::tuple
<
// F1 square distance to center
@ -322,7 +321,7 @@ std::tuple
vector<T, N>,
// F1 hash
U,
hash::make_uint_t<T>,
// F2 square distance to center
T,
@ -331,30 +330,30 @@ std::tuple
vector<T, N>,
// F2 hash
U
hash::make_uint_t<T>
>
f1_f2
(
const vector<T, N>& position,
T randomness,
const vector<T, N>& tiling,
vector<U, N> (*hash)(const vector<T, N>&)
T randomness = T{1},
const vector<T, N>& tiling = vector<T, N>::zero(),
vector<hash::make_uint_t<T>, N> (*hash)(const vector<T, N>&) = &hash::pcg<T, N>
)
{
// Calculate factor which scales hash value onto `[0, 1]`, modulated by desired randomness
T hash_scale = (T{1} / static_cast<T>(std::numeric_limits<U>::max())) * randomness;
T hash_scale = (T{1} / static_cast<T>(std::numeric_limits<hash::make_uint_t<T>>::max())) * randomness;
// Get integer and fractional parts
vector<T, N> position_i = math::floor(position - T{1.5});
vector<T, N> position_i = floor(position - T{1.5});
vector<T, N> position_f = position - position_i;
// Find the F1 and F2 cells
T f1_sqr_distance_center = std::numeric_limits<T>::infinity();
vector<T, N> f1_displacement = {0, 0};
U f1_hash = 0;
hash::make_uint_t<T> f1_hash = 0;
T f2_sqr_distance_center = std::numeric_limits<T>::infinity();
vector<T, N> f2_displacement = {0, 0};
U f2_hash = 0;
hash::make_uint_t<T> f2_hash = 0;
for (std::size_t i = 0; i < kernel_size<N>; ++i)
{
// Get kernel offset for current cell
@ -369,7 +368,7 @@ f1_f2
}
// Calculate hash values for the hash position
vector<U, N> hash_i = hash(hash_position);
vector<hash::make_uint_t<T>, N> hash_i = hash(hash_position);
// Convert hash values to pseudorandom fractional offset
vector<T, N> offset_f = vector<T, N>(hash_i) * hash_scale;
@ -378,7 +377,7 @@ f1_f2
vector<T, N> displacement = (offset_i + offset_f) - position_f;
// Calculate square distance to the current cell center
T sqr_distance = math::length_squared(displacement);
T sqr_distance = length_squared(displacement);
// Update F1 and F2 cells
if (sqr_distance < f1_sqr_distance_center)

+ 6
- 0
src/math/vector.hpp View File

@ -254,6 +254,12 @@ struct vector
{
return size_cast<M>(std::make_index_sequence<M>{});
}
/// Returns a zero vector.
static constexpr inline vector zero() noexcept
{
return {};
}
};
// Ensure vector is a POD type

Loading…
Cancel
Save