Halton sequence added to AzCore/Math/Random.h with unit tests. This work is in support of ATOM-13988 for generating sub-pixel camera offsets for TAA jitter.
This commit is contained in:
@@ -86,4 +86,94 @@ namespace AZ
|
|||||||
Normal,
|
Normal,
|
||||||
UniformReal
|
UniformReal
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Halton sequences are deterministic, quasi-random sequences with low discrepancy. They
|
||||||
|
//! are useful for generating evenly distributed points.
|
||||||
|
//! See https://en.wikipedia.org/wiki/Halton_sequence for more information.
|
||||||
|
|
||||||
|
//! Returns a single halton number.
|
||||||
|
//! @param index The index of the number. Indices start at 1. Using index 0 will return 0.
|
||||||
|
//! @param base The numerical base of the halton number.
|
||||||
|
inline float GetHaltonNumber(uint32_t index, uint32_t base)
|
||||||
|
{
|
||||||
|
float fraction = 1.0f;
|
||||||
|
float result = 0.0f;
|
||||||
|
|
||||||
|
while (index > 0)
|
||||||
|
{
|
||||||
|
fraction = fraction / base;
|
||||||
|
result += fraction * (index % base);
|
||||||
|
index = floor(index / base);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! A helper class for generating arrays of Halton sequences in n dimensions.
|
||||||
|
//! The class holds the state of which bases to use, the starting offset
|
||||||
|
//! of each dimension and how much to increment between each index for each
|
||||||
|
//! dimension.
|
||||||
|
template <uint8_t Dimensions>
|
||||||
|
class HaltonSequence
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
//! Initializes a Halton sequence with some bases. By default there is no
|
||||||
|
//! offset and the index increments by one between each number.
|
||||||
|
HaltonSequence(AZStd::array<uint32_t, Dimensions> bases)
|
||||||
|
: m_bases(bases)
|
||||||
|
{
|
||||||
|
m_offsets.fill(1); // Halton sequences start at index 1.
|
||||||
|
m_increments.fill(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns a Halton sequence in an array of N length
|
||||||
|
template<uint32_t N>
|
||||||
|
AZStd::array<AZStd::array<float, Dimensions>, N> GetHaltonSequence()
|
||||||
|
{
|
||||||
|
AZStd::array<AZStd::array<float, Dimensions>, N> result;
|
||||||
|
|
||||||
|
AZStd::array<uint32_t, Dimensions> indices = m_offsets;
|
||||||
|
|
||||||
|
// Generator that returns the Halton number for all bases for a single entry.
|
||||||
|
auto f = [&] ()
|
||||||
|
{
|
||||||
|
AZStd::array<float, Dimensions> item;
|
||||||
|
for (auto d = 0; d < Dimensions; ++d)
|
||||||
|
{
|
||||||
|
item[d] = GetHaltonNumber(indices[d], m_bases[d]);
|
||||||
|
indices[d] += m_increments[d];
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
};
|
||||||
|
|
||||||
|
AZStd::generate(result.begin(), result.end(), f);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the offsets per dimension to start generating a sequence from.
|
||||||
|
//! By default, there is no offset (offset of 0 corresponds to starting at index 1)
|
||||||
|
void SetOffsets(AZStd::array<uint32_t, Dimensions> offsets)
|
||||||
|
{
|
||||||
|
m_offsets = offsets;
|
||||||
|
|
||||||
|
// Halton sequences start at index 1, so increment all the indices.
|
||||||
|
AZStd::for_each(m_offsets.begin(), m_offsets.end(), [](uint32_t &n){ n++; });
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Sets the increment between numbers in the halton sequence per dimension
|
||||||
|
//! By default this is 1, meaning that no numbers are skipped. Can be negative
|
||||||
|
//! to generate numbers in reverse order.
|
||||||
|
void SetIncrements(AZStd::array<int32_t, Dimensions> increments)
|
||||||
|
{
|
||||||
|
m_increments = increments;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
AZStd::array<uint32_t, Dimensions> m_bases;
|
||||||
|
AZStd::array<uint32_t, Dimensions> m_offsets;
|
||||||
|
AZStd::array<int32_t, Dimensions> m_increments;
|
||||||
|
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||||
|
* its licensors.
|
||||||
|
*
|
||||||
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
||||||
|
* distribution (the "License"). All use of this software is governed by the License,
|
||||||
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
||||||
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <AzCore/Math/Random.h>
|
||||||
|
#include <AzCore/UnitTest/TestTypes.h>
|
||||||
|
|
||||||
|
using namespace AZ;
|
||||||
|
|
||||||
|
namespace UnitTest
|
||||||
|
{
|
||||||
|
TEST(MATH_Random, GetHaltonNumber)
|
||||||
|
{
|
||||||
|
EXPECT_FLOAT_EQ(0.5, GetHaltonNumber(1, 2));
|
||||||
|
EXPECT_FLOAT_EQ(898.0f / 2187.0f, GetHaltonNumber(1234, 3));
|
||||||
|
EXPECT_FLOAT_EQ(5981.0f / 15625.0f, GetHaltonNumber(4321, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(MATH_Random, HaltonSequence)
|
||||||
|
{
|
||||||
|
HaltonSequence<3> sequence({ 2, 3, 5 });
|
||||||
|
auto regularSequence = sequence.GetHaltonSequence<5>();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 2.0f, regularSequence[0][0]);
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 3.0f, regularSequence[0][1]);
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 5.0f, regularSequence[0][2]);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 4.0f, regularSequence[1][0]);
|
||||||
|
EXPECT_FLOAT_EQ(2.0f / 3.0f, regularSequence[1][1]);
|
||||||
|
EXPECT_FLOAT_EQ(2.0f / 5.0f, regularSequence[1][2]);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(3.0f / 4.0f, regularSequence[2][0]);
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 9.0f, regularSequence[2][1]);
|
||||||
|
EXPECT_FLOAT_EQ(3.0f / 5.0f, regularSequence[2][2]);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 8.0f, regularSequence[3][0]);
|
||||||
|
EXPECT_FLOAT_EQ(4.0f / 9.0f, regularSequence[3][1]);
|
||||||
|
EXPECT_FLOAT_EQ(4.0f / 5.0f, regularSequence[3][2]);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(5.0f / 8.0f, regularSequence[4][0]);
|
||||||
|
EXPECT_FLOAT_EQ(7.0f / 9.0f, regularSequence[4][1]);
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 25.0f, regularSequence[4][2]);
|
||||||
|
|
||||||
|
sequence.SetOffsets({ 1, 2, 3 });
|
||||||
|
auto offsetSequence = sequence.GetHaltonSequence<2>();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 4.0f, offsetSequence[0][0]);
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 9.0f, offsetSequence[0][1]);
|
||||||
|
EXPECT_FLOAT_EQ(4.0f / 5.0f, offsetSequence[0][2]);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(3.0f / 4.0f, offsetSequence[1][0]);
|
||||||
|
EXPECT_FLOAT_EQ(4.0f / 9.0f, offsetSequence[1][1]);
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 25.0f, offsetSequence[1][2]);
|
||||||
|
|
||||||
|
sequence.SetIncrements({ 1, 2, 3 });
|
||||||
|
auto incrementedSequence = sequence.GetHaltonSequence<2>();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 4.0f, incrementedSequence[0][0]);
|
||||||
|
EXPECT_FLOAT_EQ(1.0f / 9.0f, incrementedSequence[0][1]);
|
||||||
|
EXPECT_FLOAT_EQ(4.0f / 5.0f, incrementedSequence[0][2]);
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(3.0f / 4.0f, incrementedSequence[1][0]);
|
||||||
|
EXPECT_FLOAT_EQ(7.0f / 9.0f, incrementedSequence[1][1]);
|
||||||
|
EXPECT_FLOAT_EQ(11.0f / 25.0f, incrementedSequence[1][2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -152,6 +152,7 @@ set(FILES
|
|||||||
Math/PlaneTests.cpp
|
Math/PlaneTests.cpp
|
||||||
Math/QuaternionPerformanceTests.cpp
|
Math/QuaternionPerformanceTests.cpp
|
||||||
Math/QuaternionTests.cpp
|
Math/QuaternionTests.cpp
|
||||||
|
Math/RandomTests.cpp
|
||||||
Math/ShapeIntersectionPerformanceTests.cpp
|
Math/ShapeIntersectionPerformanceTests.cpp
|
||||||
Math/ShapeIntersectionTests.cpp
|
Math/ShapeIntersectionTests.cpp
|
||||||
Math/SfmtTests.cpp
|
Math/SfmtTests.cpp
|
||||||
|
|||||||
Reference in New Issue
Block a user