Browse Source

Improve hash functions

master
C. J. Howard 2 years ago
parent
commit
a211967a45
8 changed files with 315 additions and 154 deletions
  1. +5
    -4
      src/game/load.cpp
  2. +32
    -0
      src/math/hash/hash.hpp
  3. +274
    -0
      src/math/hash/pcg.hpp
  4. +2
    -2
      src/math/noise/fbm.hpp
  5. +0
    -142
      src/math/noise/hash.hpp
  6. +0
    -1
      src/math/noise/noise.hpp
  7. +2
    -2
      src/math/noise/simplex.hpp
  8. +0
    -3
      src/math/noise/voronoi.hpp

+ 5
- 4
src/game/load.cpp View File

@ -31,6 +31,7 @@
#include "game/system/terrain.hpp" #include "game/system/terrain.hpp"
#include "math/random.hpp" #include "math/random.hpp"
#include "math/noise/noise.hpp" #include "math/noise/noise.hpp"
#include "math/hash/hash.hpp"
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <stb/stb_image_write.h> #include <stb/stb_image_write.h>
@ -55,7 +56,7 @@ void biome(game::context& ctx, const std::filesystem::path& path)
std::size_t octaves = 4; std::size_t octaves = 4;
float lacunarity = 2.0f; float lacunarity = 2.0f;
float gain = 0.5f; float gain = 0.5f;
auto hash = static_cast<std::uint32_t(*)(const math::vector<float, 2>&)>(math::noise::hash::pcg3d_1);
auto hash = static_cast<math::vector<std::uint32_t, 2>(*)(const math::vector<float, 2>&)>(math::hash::pcg);
auto noise = static_cast<float(*)(const math::vector<float, 2>&, decltype(hash))>(math::noise::simplex); auto noise = static_cast<float(*)(const math::vector<float, 2>&, decltype(hash))>(math::noise::simplex);
auto fbm = [&](const float2& x) auto fbm = [&](const float2& x)
@ -112,13 +113,13 @@ void biome(game::context& ctx, const std::filesystem::path& path)
// f2_displacement, // f2_displacement,
// f2_id // f2_id
edge_sqr_distance edge_sqr_distance
] = math::noise::voronoi::f1_edge<float, 3, std::uint32_t>({position[0], position[1], 0.5f}, 1.0f, {0.0f, 0.0f, 0.0f}, &math::noise::hash::pcg3d_3);
] = math::noise::voronoi::f1_edge<float, 1, std::uint32_t>({position[0]}, 1.0f, {0.0f}, &math::hash::pcg);
float f1_distance = std::sqrt(f1_sqr_distance); float f1_distance = std::sqrt(f1_sqr_distance);
//float f2_distance = std::sqrt(f2_sqr_distance); //float f2_distance = std::sqrt(f2_sqr_distance);
float edge_distance = std::sqrt(edge_sqr_distance); float edge_distance = std::sqrt(edge_sqr_distance);
pixel = static_cast<unsigned char>(std::min(255.0f, edge_distance * 255.0f));
pixel = static_cast<unsigned char>(std::min(255.0f, f1_distance * 255.0f));
//pixel = static_cast<unsigned char>(id % 255); //pixel = static_cast<unsigned char>(id % 255);
} }
); );
@ -207,7 +208,7 @@ void biome(game::context& ctx, const std::filesystem::path& path)
float lacunarity = 3.0f; float lacunarity = 3.0f;
float gain = 0.5f; float gain = 0.5f;
auto noise = static_cast<float(*)(const math::vector<float, 2>&, decltype(hash))>(math::noise::simplex); auto noise = static_cast<float(*)(const math::vector<float, 2>&, decltype(hash))>(math::noise::simplex);
auto hash = static_cast<std::uint32_t(*)(const math::vector<float, 2>&)>(math::noise::hash::pcg3d_1);
auto hash = static_cast<math::vector<std::uint32_t, 2>(*)(const math::vector<float, 2>&)>(math::hash::pcg);
float2 position = float2{x, z} * frequency; float2 position = float2{x, z} * frequency;

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

@ -0,0 +1,32 @@
/*
* 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_HPP
#define ANTKEEPER_MATH_HASH_HPP
namespace math {
/// Hash functions.
namespace hash {}
} // namespace math
#include "math/hash/pcg.hpp"
#endif // ANTKEEPER_MATH_HASH_HPP

+ 274
- 0
src/math/hash/pcg.hpp View File

@ -0,0 +1,274 @@
/*
* 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_PCG_HPP
#define ANTKEEPER_MATH_HASH_PCG_HPP
#include "math/vector.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;
};
template<>
struct pcg_make_uint<float>
{
typedef std::uint32_t type;
};
template<>
struct pcg_make_uint<double>
{
typedef std::uint64_t type;
};
template <class T>
using pcg_make_uint_t = typename pcg_make_uint<T>::type;
/// @}
/// @private
/// @{
template <class T>
constexpr T pcg_multiplier = 0;
template <class T>
constexpr T pcg_increment = 0;
template <class T>
constexpr T mcg_multiplier = 0;
template <>
constexpr std::uint8_t pcg_multiplier<std::uint8_t> = 141U;
template <>
constexpr std::uint8_t pcg_increment<std::uint8_t> = 77U;
template <>
constexpr std::uint8_t mcg_multiplier<std::uint8_t> = 217U;
template <>
constexpr std::uint16_t pcg_multiplier<std::uint16_t> = 12829U;
template <>
constexpr std::uint16_t pcg_increment<std::uint16_t> = 47989U;
template <>
constexpr std::uint16_t mcg_multiplier<std::uint16_t> = 62169U;
template <>
constexpr std::uint32_t pcg_multiplier<std::uint32_t> = 747796405UL;
template <>
constexpr std::uint32_t pcg_increment<std::uint32_t> = 2891336453UL;
template <>
constexpr std::uint32_t mcg_multiplier<std::uint32_t> = 277803737UL;
template <>
constexpr std::uint64_t pcg_multiplier<std::uint64_t> = 6364136223846793005ULL;
template <>
constexpr std::uint64_t pcg_increment<std::uint64_t> = 1442695040888963407ULL;
template <>
constexpr std::uint64_t mcg_multiplier<std::uint64_t> = 12605985483714917081ULL;
template <class T>
constexpr T pcg_uint(T x) noexcept
{
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
static_assert(sizeof(T) <= 8);
x = x * pcg_multiplier<T> + pcg_increment<T>;
x = (x ^ (x >> ((x >> ((sizeof(T) * 8) - (sizeof(T) + 1))) + (sizeof(T) + 1)))) * mcg_multiplier<T>;
return x ^ (x >> ((sizeof(T) * 16 + 2) / 3));
}
template <class T>
inline constexpr vector<T, 1> pcg_uvec1(vector<T, 1> x) noexcept
{
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
static_assert(sizeof(T) <= 8);
x[0] = T(x[0]);
return x;
}
template <class T>
constexpr vector<T, 2> pcg_uvec2(vector<T, 2> x) noexcept
{
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
static_assert(sizeof(T) <= 8);
x = x * pcg_multiplier<T> + pcg_increment<T>;
x[0] += x[1] * pcg_multiplier<T>;
x[1] += x[0] * pcg_multiplier<T>;
x[0] ^= x[0] >> (sizeof(T) * 4);
x[1] ^= x[1] >> (sizeof(T) * 4);
x[0] += x[1] * pcg_multiplier<T>;
x[1] += x[0] * pcg_multiplier<T>;
x[0] ^= x[0] >> (sizeof(T) * 4);
x[1] ^= x[1] >> (sizeof(T) * 4);
return x;
}
template <class T>
constexpr vector<T, 3> pcg_uvec3(vector<T, 3> x) noexcept
{
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
static_assert(sizeof(T) <= 8);
x = x * pcg_multiplier<T> + pcg_increment<T>;
x[0] += x[1] * x[2];
x[1] += x[2] * x[0];
x[2] += x[0] * x[1];
x[0] ^= x[0] >> (sizeof(T) * 4);
x[1] ^= x[1] >> (sizeof(T) * 4);
x[2] ^= x[2] >> (sizeof(T) * 4);
x[0] += x[1] * x[2];
x[1] += x[2] * x[0];
x[2] += x[0] * x[1];
return x;
}
template <class T>
constexpr vector<T, 4> pcg_uvec4(vector<T, 4> x) noexcept
{
static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value);
static_assert(sizeof(T) <= 8);
x = x * pcg_multiplier<T> + pcg_increment<T>;
x[0] += x[1] * x[3];
x[1] += x[2] * x[0];
x[2] += x[0] * x[1];
x[3] += x[1] * x[2];
x[0] ^= x[0] >> (sizeof(T) * 4);
x[1] ^= x[1] >> (sizeof(T) * 4);
x[2] ^= x[2] >> (sizeof(T) * 4);
x[3] ^= x[3] >> (sizeof(T) * 4);
x[0] += x[1] * x[3];
x[1] += x[2] * x[0];
x[2] += x[0] * x[1];
x[3] += x[1] * x[2];
return x;
}
/// @}
/**
* PCG hash functions.
*
* @param x Input value.
*
* @return Pseudorandom output value.
*
* @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.
* @see Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer Graphics Techniques (JCGT), vol. 9, no. 3, 21-38, 2020.
*/
/// @{
inline constexpr std::uint8_t pcg(std::uint8_t x) noexcept
{
return pcg_uint<std::uint8_t>(x);
}
inline constexpr std::uint16_t pcg(std::uint16_t x) noexcept
{
return pcg_uint<std::uint16_t>(x);
}
inline constexpr std::uint32_t pcg(std::uint32_t x) noexcept
{
return pcg_uint<std::uint32_t>(x);
}
inline constexpr std::uint64_t pcg(std::uint64_t x) noexcept
{
return pcg_uint<std::uint64_t>(x);
}
inline constexpr std::uint8_t pcg(std::int8_t x) noexcept
{
return pcg_uint<std::uint8_t>(static_cast<std::uint8_t>(x));
}
inline constexpr std::uint16_t pcg(std::int16_t x) noexcept
{
return pcg_uint<std::uint16_t>(static_cast<std::uint16_t>(x));
}
inline constexpr std::uint32_t pcg(std::int32_t x) noexcept
{
return pcg_uint<std::uint32_t>(static_cast<std::uint32_t>(x));
}
inline constexpr std::uint64_t pcg(std::int64_t x) noexcept
{
return pcg_uint<std::uint64_t>(static_cast<std::uint64_t>(x));
}
inline constexpr std::uint32_t pcg(float x) noexcept
{
return pcg_uint<std::uint32_t>(static_cast<std::uint32_t>(x));
}
inline constexpr std::uint64_t pcg(double x) noexcept
{
return pcg_uint<std::uint64_t>(static_cast<std::uint64_t>(x));
}
template <class T, std::size_t N>
inline constexpr vector<pcg_make_uint_t<T>, N> pcg(const vector<T, N>& x) noexcept
{
static_assert(N > 0 && N < 5, "Dimension not supported by PCG hash.");
if constexpr (N == 1)
return pcg_uvec1<pcg_make_uint_t<T>>(vector<pcg_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));
else if constexpr (N == 3)
return pcg_uvec3<pcg_make_uint_t<T>>(vector<pcg_make_uint_t<T>, N>(x));
else
return pcg_uvec4<pcg_make_uint_t<T>>(vector<pcg_make_uint_t<T>, N>(x));
}
/// @}
} // namespace hash
} // namespace math
#endif // ANTKEEPER_MATH_HASH_PCG_HPP

+ 2
- 2
src/math/noise/fbm.hpp View File

@ -48,8 +48,8 @@ T fbm
std::size_t octaves, std::size_t octaves,
T lacunarity, T lacunarity,
T gain, T gain,
T (*noise)(const vector<T, N>&, U (*)(const vector<T, N>&)),
U (*hash)(const vector<T, N>&)
T (*noise)(const vector<T, N>&, vector<U, N> (*)(const vector<T, N>&)),
vector<U, N> (*hash)(const vector<T, N>&)
) )
{ {
T amplitude{1}; T amplitude{1};

+ 0
- 142
src/math/noise/hash.hpp View File

@ -1,142 +0,0 @@
/*
* 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_NOISE_HASH_HPP
#define ANTKEEPER_MATH_NOISE_HASH_HPP
#include "math/vector.hpp"
#include <cstdint>
namespace math {
namespace noise {
/// Hash functions.
namespace hash {
/**
* PCG3D hash.
*
* @see Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer Graphics Techniques (JCGT), vol. 9, no. 3, 21-38, 2020.
*/
/// @{
template <class T>
math::vector<std::uint32_t, 3> pcg3d_3(const math::vector<T, 3>& x)
{
math::vector<std::uint32_t, 3> u = math::vector<std::uint32_t, 3>(x) * 1664525U + 1013904223U;
u[0] += u[1] * u[2];
u[1] += u[2] * u[0];
u[2] += u[0] * u[1];
u[0] ^= u[0] >> 16U;
u[1] ^= u[1] >> 16U;
u[2] ^= u[2] >> 16U;
u[0] += u[1] * u[2];
u[1] += u[2] * u[0];
u[2] += u[0] * u[1];
return u;
}
template <class T>
math::vector<std::uint32_t, 3> pcg3d_3(const math::vector<T, 2>& x)
{
return pcg3d_3<T>({x[0], x[1], T{1}});
}
template <class T>
math::vector<std::uint32_t, 3> pcg3d_3(const math::vector<T, 1>& x)
{
return pcg3d_3<T>({x[0], T{1}, T{1}});
}
template <class T>
math::vector<std::uint32_t, 3> pcg3d_3(T x)
{
return pcg3d_3<T>({x, T{1}, T{1}});
}
template <class T>
std::uint32_t pcg3d_1(const math::vector<T, 3>& x)
{
return pcg3d_3<T>(x)[0];
}
template <class T>
std::uint32_t pcg3d_1(const math::vector<T, 2>& x)
{
return pcg3d_3<T>({x[0], x[1], T{1}})[0];
}
template <class T>
std::uint32_t pcg3d_1(const math::vector<T, 1>& x)
{
return pcg3d_3<T>({x[0], T{1}, T{1}})[0];
}
template <class T>
std::uint32_t pcg3d_1(T x)
{
return pcg3d_3<T>({x, T{1}, T{1}})[0];
}
/// @}
/**
* PCG4D hash.
*
* @see Mark Jarzynski and Marc Olano, Hash Functions for GPU Rendering, Journal of Computer Graphics Techniques (JCGT), vol. 9, no. 3, 21-38, 2020.
*/
/// @{
template <class T>
math::vector<std::uint32_t, 4> pcg4d_4(const math::vector<T, 4>& x)
{
math::vector<std::uint32_t, 4> u = math::vector<std::uint32_t, 4>(x) * 1664525U + 1013904223U;
u[0] += u[1] * u[3];
u[1] += u[2] * u[0];
u[2] += u[0] * u[1];
u[3] += u[1] * u[2];
u[0] ^= u[0] >> 16U;
u[1] ^= u[1] >> 16U;
u[2] ^= u[2] >> 16U;
u[3] ^= u[3] >> 16U;
u[0] += u[1] * u[3];
u[1] += u[2] * u[0];
u[2] += u[0] * u[1];
u[3] += u[1] * u[2];
return u;
}
template <class T>
std::uint32_t pcg4d_1(const math::vector<T, 4>& x)
{
return pcg4d_4<T>(x)[0];
}
/// @}
} // namespace hash
} // namespace noise
} // namespace math
#endif // ANTKEEPER_MATH_NOISE_HASH_HPP

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

@ -28,7 +28,6 @@ namespace noise {}
} // namespace math } // namespace math
#include "math/noise/fbm.hpp" #include "math/noise/fbm.hpp"
#include "math/noise/hash.hpp"
#include "math/noise/simplex.hpp" #include "math/noise/simplex.hpp"
#include "math/noise/voronoi.hpp" #include "math/noise/voronoi.hpp"

+ 2
- 2
src/math/noise/simplex.hpp View File

@ -110,7 +110,7 @@ constexpr auto simplex_edges = make_simplex_edges(std::make_index_sequence
* @see https://math.stackexchange.com/questions/474638/radius-and-amplitude-of-kernel-for-simplex-noise/1901116 * @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> template <class T, std::size_t N, class U>
T simplex(const vector<T, N>& x, U (*hash)(const vector<T, N>&))
T simplex(const vector<T, N>& x, vector<U, N> (*hash)(const vector<T, N>&))
{ {
// Skewing (F) and unskewing (G) factors // Skewing (F) and unskewing (G) factors
static const T f = (std::sqrt(static_cast<T>(N + 1)) - T{1}) / static_cast<T>(N); static const T f = (std::sqrt(static_cast<T>(N + 1)) - T{1}) / static_cast<T>(N);
@ -173,7 +173,7 @@ T simplex(const vector& x, U (*hash)(const vector&))
T t = falloff(math::length_squared(d)); T t = falloff(math::length_squared(d));
if (t > T{0}) if (t > T{0})
{ {
const auto gradient_index = hash(current_vertex) % simplex_edges<T, N>.size();
const auto gradient_index = hash(current_vertex)[0] % simplex_edges<T, N>.size();
const vector<T, N>& gradient = simplex_edges<T, N>[gradient_index]; const vector<T, N>& gradient = simplex_edges<T, N>[gradient_index];
n += math::dot(d, gradient) * t; n += math::dot(d, gradient) * t;

+ 0
- 3
src/math/noise/voronoi.hpp View File

@ -136,7 +136,6 @@ f1
T f1_sqr_distance = std::numeric_limits<T>::infinity(); T f1_sqr_distance = std::numeric_limits<T>::infinity();
vector<T, N> f1_displacement; vector<T, N> f1_displacement;
U f1_hash; U f1_hash;
for (std::size_t i = 0; i < kernel_size<N>; ++i) for (std::size_t i = 0; i < kernel_size<N>; ++i)
{ {
// Get kernel offset for current cell // Get kernel offset for current cell
@ -228,7 +227,6 @@ f1_edge
vector<T, N> displacement_cache[kernel_size<N>]; vector<T, N> displacement_cache[kernel_size<N>];
std::size_t f1_i = 0; std::size_t f1_i = 0;
U f1_hash; U f1_hash;
for (std::size_t i = 0; i < kernel_size<N>; ++i) for (std::size_t i = 0; i < kernel_size<N>; ++i)
{ {
// Get kernel offset for current cell // Get kernel offset for current cell
@ -357,7 +355,6 @@ f1_f2
T f2_sqr_distance_center = std::numeric_limits<T>::infinity(); T f2_sqr_distance_center = std::numeric_limits<T>::infinity();
vector<T, N> f2_displacement = {0, 0}; vector<T, N> f2_displacement = {0, 0};
U f2_hash = 0; U f2_hash = 0;
for (std::size_t i = 0; i < kernel_size<N>; ++i) for (std::size_t i = 0; i < kernel_size<N>; ++i)
{ {
// Get kernel offset for current cell // Get kernel offset for current cell

Loading…
Cancel
Save