From a211967a45ca7eeb9a7886cde24a4c2019aac12f Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Wed, 19 Oct 2022 14:03:44 +0800 Subject: [PATCH] Improve hash functions --- src/game/load.cpp | 9 +- src/math/hash/hash.hpp | 32 +++++ src/math/hash/pcg.hpp | 274 +++++++++++++++++++++++++++++++++++++ src/math/noise/fbm.hpp | 4 +- src/math/noise/hash.hpp | 142 ------------------- src/math/noise/noise.hpp | 1 - src/math/noise/simplex.hpp | 4 +- src/math/noise/voronoi.hpp | 3 - 8 files changed, 315 insertions(+), 154 deletions(-) create mode 100644 src/math/hash/hash.hpp create mode 100644 src/math/hash/pcg.hpp delete mode 100644 src/math/noise/hash.hpp diff --git a/src/game/load.cpp b/src/game/load.cpp index 4a1c916..fca8694 100644 --- a/src/game/load.cpp +++ b/src/game/load.cpp @@ -31,6 +31,7 @@ #include "game/system/terrain.hpp" #include "math/random.hpp" #include "math/noise/noise.hpp" +#include "math/hash/hash.hpp" #include #include #include @@ -55,7 +56,7 @@ void biome(game::context& ctx, const std::filesystem::path& path) std::size_t octaves = 4; float lacunarity = 2.0f; float gain = 0.5f; - auto hash = static_cast&)>(math::noise::hash::pcg3d_1); + auto hash = static_cast(*)(const math::vector&)>(math::hash::pcg); auto noise = static_cast&, decltype(hash))>(math::noise::simplex); auto fbm = [&](const float2& x) @@ -112,13 +113,13 @@ void biome(game::context& ctx, const std::filesystem::path& path) // f2_displacement, // f2_id edge_sqr_distance - ] = math::noise::voronoi::f1_edge({position[0], position[1], 0.5f}, 1.0f, {0.0f, 0.0f, 0.0f}, &math::noise::hash::pcg3d_3); + ] = math::noise::voronoi::f1_edge({position[0]}, 1.0f, {0.0f}, &math::hash::pcg); float f1_distance = std::sqrt(f1_sqr_distance); //float f2_distance = std::sqrt(f2_sqr_distance); float edge_distance = std::sqrt(edge_sqr_distance); - pixel = static_cast(std::min(255.0f, edge_distance * 255.0f)); + pixel = static_cast(std::min(255.0f, f1_distance * 255.0f)); //pixel = static_cast(id % 255); } ); @@ -207,7 +208,7 @@ void biome(game::context& ctx, const std::filesystem::path& path) float lacunarity = 3.0f; float gain = 0.5f; auto noise = static_cast&, decltype(hash))>(math::noise::simplex); - auto hash = static_cast&)>(math::noise::hash::pcg3d_1); + auto hash = static_cast(*)(const math::vector&)>(math::hash::pcg); float2 position = float2{x, z} * frequency; diff --git a/src/math/hash/hash.hpp b/src/math/hash/hash.hpp new file mode 100644 index 0000000..5bb7214 --- /dev/null +++ b/src/math/hash/hash.hpp @@ -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 . + */ + +#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 diff --git a/src/math/hash/pcg.hpp b/src/math/hash/pcg.hpp new file mode 100644 index 0000000..2cc2880 --- /dev/null +++ b/src/math/hash/pcg.hpp @@ -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 . + */ + +#ifndef ANTKEEPER_MATH_HASH_PCG_HPP +#define ANTKEEPER_MATH_HASH_PCG_HPP + +#include "math/vector.hpp" +#include +#include + +namespace math { +namespace hash { + +/** + * Provides an unsigned integer type of equivalent size to type @p T. + */ +/// @{ +template +struct pcg_make_uint +{ + static_assert(std::is_integral::value); + + /// Unsigned integer type of equivalent size to type @p T. + typedef typename std::make_unsigned::type type; +}; + +template<> +struct pcg_make_uint +{ + typedef std::uint32_t type; +}; + +template<> +struct pcg_make_uint +{ + typedef std::uint64_t type; +}; + +template +using pcg_make_uint_t = typename pcg_make_uint::type; +/// @} + +/// @private +/// @{ + +template +constexpr T pcg_multiplier = 0; +template +constexpr T pcg_increment = 0; +template +constexpr T mcg_multiplier = 0; + +template <> +constexpr std::uint8_t pcg_multiplier = 141U; +template <> +constexpr std::uint8_t pcg_increment = 77U; +template <> +constexpr std::uint8_t mcg_multiplier = 217U; + +template <> +constexpr std::uint16_t pcg_multiplier = 12829U; +template <> +constexpr std::uint16_t pcg_increment = 47989U; +template <> +constexpr std::uint16_t mcg_multiplier = 62169U; + +template <> +constexpr std::uint32_t pcg_multiplier = 747796405UL; +template <> +constexpr std::uint32_t pcg_increment = 2891336453UL; +template <> +constexpr std::uint32_t mcg_multiplier = 277803737UL; + +template <> +constexpr std::uint64_t pcg_multiplier = 6364136223846793005ULL; +template <> +constexpr std::uint64_t pcg_increment = 1442695040888963407ULL; +template <> +constexpr std::uint64_t mcg_multiplier = 12605985483714917081ULL; + +template +constexpr T pcg_uint(T x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x = x * pcg_multiplier + pcg_increment; + x = (x ^ (x >> ((x >> ((sizeof(T) * 8) - (sizeof(T) + 1))) + (sizeof(T) + 1)))) * mcg_multiplier; + return x ^ (x >> ((sizeof(T) * 16 + 2) / 3)); +} + +template +inline constexpr vector pcg_uvec1(vector x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x[0] = T(x[0]); + + return x; +} + +template +constexpr vector pcg_uvec2(vector x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x = x * pcg_multiplier + pcg_increment; + + x[0] += x[1] * pcg_multiplier; + x[1] += x[0] * pcg_multiplier; + + x[0] ^= x[0] >> (sizeof(T) * 4); + x[1] ^= x[1] >> (sizeof(T) * 4); + + x[0] += x[1] * pcg_multiplier; + x[1] += x[0] * pcg_multiplier; + + x[0] ^= x[0] >> (sizeof(T) * 4); + x[1] ^= x[1] >> (sizeof(T) * 4); + + return x; +} + +template +constexpr vector pcg_uvec3(vector x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x = x * pcg_multiplier + pcg_increment; + + 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 +constexpr vector pcg_uvec4(vector x) noexcept +{ + static_assert(std::is_integral::value && std::is_unsigned::value); + static_assert(sizeof(T) <= 8); + + x = x * pcg_multiplier + pcg_increment; + + 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(x); +} + +inline constexpr std::uint16_t pcg(std::uint16_t x) noexcept +{ + return pcg_uint(x); +} + +inline constexpr std::uint32_t pcg(std::uint32_t x) noexcept +{ + return pcg_uint(x); +} + +inline constexpr std::uint64_t pcg(std::uint64_t x) noexcept +{ + return pcg_uint(x); +} + +inline constexpr std::uint8_t pcg(std::int8_t x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +inline constexpr std::uint16_t pcg(std::int16_t x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +inline constexpr std::uint32_t pcg(std::int32_t x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +inline constexpr std::uint64_t pcg(std::int64_t x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +inline constexpr std::uint32_t pcg(float x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +inline constexpr std::uint64_t pcg(double x) noexcept +{ + return pcg_uint(static_cast(x)); +} + +template +inline constexpr vector, N> pcg(const vector& x) noexcept +{ + static_assert(N > 0 && N < 5, "Dimension not supported by PCG hash."); + + if constexpr (N == 1) + return pcg_uvec1>(vector, N>(x)); + else if constexpr (N == 2) + return pcg_uvec2>(vector, N>(x)); + else if constexpr (N == 3) + return pcg_uvec3>(vector, N>(x)); + else + return pcg_uvec4>(vector, N>(x)); +} +/// @} + +} // namespace hash +} // namespace math + +#endif // ANTKEEPER_MATH_HASH_PCG_HPP diff --git a/src/math/noise/fbm.hpp b/src/math/noise/fbm.hpp index c7a416a..29268c5 100644 --- a/src/math/noise/fbm.hpp +++ b/src/math/noise/fbm.hpp @@ -48,8 +48,8 @@ T fbm std::size_t octaves, T lacunarity, T gain, - T (*noise)(const vector&, U (*)(const vector&)), - U (*hash)(const vector&) + T (*noise)(const vector&, vector (*)(const vector&)), + vector (*hash)(const vector&) ) { T amplitude{1}; diff --git a/src/math/noise/hash.hpp b/src/math/noise/hash.hpp deleted file mode 100644 index bee41e0..0000000 --- a/src/math/noise/hash.hpp +++ /dev/null @@ -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 . - */ - -#ifndef ANTKEEPER_MATH_NOISE_HASH_HPP -#define ANTKEEPER_MATH_NOISE_HASH_HPP - -#include "math/vector.hpp" -#include - -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 -math::vector pcg3d_3(const math::vector& x) -{ - math::vector u = math::vector(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 -math::vector pcg3d_3(const math::vector& x) -{ - return pcg3d_3({x[0], x[1], T{1}}); -} - -template -math::vector pcg3d_3(const math::vector& x) -{ - return pcg3d_3({x[0], T{1}, T{1}}); -} - -template -math::vector pcg3d_3(T x) -{ - return pcg3d_3({x, T{1}, T{1}}); -} - -template -std::uint32_t pcg3d_1(const math::vector& x) -{ - return pcg3d_3(x)[0]; -} - -template -std::uint32_t pcg3d_1(const math::vector& x) -{ - return pcg3d_3({x[0], x[1], T{1}})[0]; -} - -template -std::uint32_t pcg3d_1(const math::vector& x) -{ - return pcg3d_3({x[0], T{1}, T{1}})[0]; -} - -template -std::uint32_t pcg3d_1(T x) -{ - return pcg3d_3({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 -math::vector pcg4d_4(const math::vector& x) -{ - math::vector u = math::vector(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 -std::uint32_t pcg4d_1(const math::vector& x) -{ - return pcg4d_4(x)[0]; -} -/// @} - -} // namespace hash - -} // namespace noise -} // namespace math - -#endif // ANTKEEPER_MATH_NOISE_HASH_HPP diff --git a/src/math/noise/noise.hpp b/src/math/noise/noise.hpp index 8f076ff..7f54eb4 100644 --- a/src/math/noise/noise.hpp +++ b/src/math/noise/noise.hpp @@ -28,7 +28,6 @@ namespace noise {} } // namespace math #include "math/noise/fbm.hpp" -#include "math/noise/hash.hpp" #include "math/noise/simplex.hpp" #include "math/noise/voronoi.hpp" diff --git a/src/math/noise/simplex.hpp b/src/math/noise/simplex.hpp index 9919352..fb617ca 100644 --- a/src/math/noise/simplex.hpp +++ b/src/math/noise/simplex.hpp @@ -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 */ template -T simplex(const vector& x, U (*hash)(const vector&)) +T simplex(const vector& x, vector (*hash)(const vector&)) { // Skewing (F) and unskewing (G) factors static const T f = (std::sqrt(static_cast(N + 1)) - T{1}) / static_cast(N); @@ -173,7 +173,7 @@ T simplex(const vector& x, U (*hash)(const vector&)) T t = falloff(math::length_squared(d)); if (t > T{0}) { - const auto gradient_index = hash(current_vertex) % simplex_edges.size(); + const auto gradient_index = hash(current_vertex)[0] % simplex_edges.size(); const vector& gradient = simplex_edges[gradient_index]; n += math::dot(d, gradient) * t; diff --git a/src/math/noise/voronoi.hpp b/src/math/noise/voronoi.hpp index 1f5d739..065d86d 100644 --- a/src/math/noise/voronoi.hpp +++ b/src/math/noise/voronoi.hpp @@ -136,7 +136,6 @@ f1 T f1_sqr_distance = std::numeric_limits::infinity(); vector f1_displacement; U f1_hash; - for (std::size_t i = 0; i < kernel_size; ++i) { // Get kernel offset for current cell @@ -228,7 +227,6 @@ f1_edge vector displacement_cache[kernel_size]; std::size_t f1_i = 0; U f1_hash; - for (std::size_t i = 0; i < kernel_size; ++i) { // Get kernel offset for current cell @@ -357,7 +355,6 @@ f1_f2 T f2_sqr_distance_center = std::numeric_limits::infinity(); vector f2_displacement = {0, 0}; U f2_hash = 0; - for (std::size_t i = 0; i < kernel_size; ++i) { // Get kernel offset for current cell