Skip to content

Commit 5f79b6e

Browse files
authored
Add Perlin gradient noise (#61)
1 parent 932ede8 commit 5f79b6e

File tree

4 files changed

+234
-0
lines changed

4 files changed

+234
-0
lines changed

Examples/AMReX_Shapes/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ To select different geometries, set which_geom to one of the below.
4343
* `which_geom = 10` Spherical shell
4444
* `which_geom = 11` Death star
4545
* `which_geom = 12` Rounded box
46+
* `which_geom = 13` Kern Perlin's gradient noise SDF

Examples/AMReX_Shapes/main.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,11 @@ main(int argc, char* argv[])
163163

164164
func = std::make_shared<EBGeometry::RoundedBoxSDF<T>>(1.0 * Vec3::one(), 0.1);
165165
}
166+
else if (whichGeom == 13) { // Perlin Random noise function
167+
rb = RealBox({-1, -1, -1}, {1, 1, 1});
168+
169+
func = std::make_shared<EBGeometry::PerlinSDF<T>>(0.5, 2.0 * Vec3::one(), 0.5, 4);
170+
}
166171

167172
// AMReX uses the opposite sign.
168173
func = EBGeometry::Complement<T>(func);

Examples/EBGeometry_Shapes/main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ main()
2121
const EBGeometry::CapsuleSDF<T> capsule(Vec3::zero(), Vec3::one(), 0.1);
2222
const EBGeometry::InfiniteConeSDF<T> infiniteCone(Vec3::zero(), 45.0);
2323
const EBGeometry::ConeSDF<T> cone(Vec3::zero(), 1.0, 45.0);
24+
const EBGeometry::PerlinSDF<T> perlin(1.0, Vec3::one(), 0.5, 10);
25+
const EBGeometry::RoundedBoxSDF<T> roundBox(Vec3::one(), 0.1);
2426
}

Source/EBGeometry_AnalyticDistanceFunctions.hpp

+226
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
// Std includes
1919
#include <algorithm>
20+
#include <random>
2021

2122
// Our includes
2223
#include "EBGeometry_BoundingVolumes.hpp"
@@ -864,6 +865,231 @@ class RoundedBoxSDF : public SignedDistanceFunction<T>
864865
Vec3T<T> m_dimensions;
865866
};
866867

868+
/*!
869+
@brief Ken Perlins gradient noise function
870+
*/
871+
template <class T>
872+
class PerlinSDF : public SignedDistanceFunction<T>
873+
{
874+
public:
875+
/*!
876+
@brief Full constructor.
877+
@param[in] a_noiseAmplitude Noise amplitude
878+
@param[in] a_noiseFrequency Spatial noise frequency along the three Cartesian axes
879+
@param[in] a_noisePersistence Noise amplitude drop-off and frequency increase for octave noise. Should be < 1
880+
@param[in] a_noiseOctaves Number of octaves. Should be > 0
881+
*/
882+
PerlinSDF(const T a_noiseAmplitude,
883+
const Vec3T<T> a_noiseFrequency,
884+
const T a_noisePersistence,
885+
const unsigned int a_noiseOctaves) noexcept
886+
{
887+
m_noiseAmplitude = a_noiseAmplitude;
888+
m_noiseFrequency = a_noiseFrequency;
889+
m_noisePersistence = std::min(1.0, a_noisePersistence);
890+
m_noiseOctaves = std::max((unsigned int)1, a_noiseOctaves);
891+
892+
// By default, use Ken Perlin's original permutation table
893+
for (int i = 0; i < 256; i++) {
894+
m_permutationTable[i] = s_permutationTable[i];
895+
m_permutationTable[i + 256] = s_permutationTable[i];
896+
}
897+
898+
#if 1 // Development code
899+
std::random_device rd;
900+
std::mt19937 g(0);
901+
902+
this->shuffle(g);
903+
#endif
904+
}
905+
906+
/*!
907+
@brief Destructor (does nothing)
908+
*/
909+
virtual ~PerlinSDF() noexcept
910+
{}
911+
912+
/*!
913+
@brief Signed distance function.
914+
@param[in] a_point Input point
915+
*/
916+
virtual T
917+
signedDistance(const Vec3T<T>& a_point) const noexcept override
918+
{
919+
T ret = 0.0;
920+
921+
Vec3T<T> curFreq = m_noiseFrequency;
922+
T curAmp = 1.0;
923+
924+
for (unsigned int curOctave = 0; curOctave < m_noiseOctaves; curOctave++) {
925+
ret += this->noise(a_point * curFreq) * curAmp;
926+
927+
curFreq = curFreq / m_noisePersistence;
928+
curAmp = curAmp * m_noisePersistence;
929+
}
930+
931+
return ret * m_noiseAmplitude;
932+
};
933+
934+
/*!
935+
@brief Shuffle the permutation with the input RNG.
936+
@details URNG should be a uniform random number generator, e.g.
937+
@param[in] g Uniform random number generator (e.g., std::mt19937)
938+
@note When using parallel calculations it is exceptionally important that the input RNG is the same across all threads/ranks. Otherwise, the
939+
user must manually ensure that the permutation table is the same. Failure to do so implies that each thread/rank generates it's own gradient noise
940+
and there is correspondingly no single geometry.
941+
*/
942+
template <class URNG>
943+
void
944+
shuffle(URNG& g) noexcept
945+
{
946+
947+
for (unsigned int i = 0; i < 256; i++) {
948+
m_permutationTable[i] = i;
949+
}
950+
951+
std::shuffle(m_permutationTable.begin(), m_permutationTable.begin() + 256, g);
952+
953+
for (int i = 0; i < 256; i++) {
954+
m_permutationTable[i + 256] = m_permutationTable[i];
955+
}
956+
}
957+
958+
/*!
959+
@brief Get the internal permutation table
960+
@return m_permutationTable.
961+
*/
962+
std::array<int, 512>&
963+
getPermutationTable() noexcept
964+
{
965+
return m_permutationTable;
966+
}
967+
968+
protected:
969+
/*!
970+
@brief Ken Perlin's original permutation array
971+
*/
972+
constexpr static std::array<int, 256> s_permutationTable = {
973+
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142,
974+
8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203,
975+
117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165,
976+
71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41,
977+
55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89,
978+
18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250,
979+
124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189,
980+
28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
981+
129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34,
982+
242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31,
983+
181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114,
984+
67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180};
985+
986+
/*!
987+
@brief Noise frequency
988+
*/
989+
Vec3T<T> m_noiseFrequency;
990+
991+
/*!
992+
@brief Noise amplitude
993+
*/
994+
T m_noiseAmplitude;
995+
996+
/*!
997+
@brief Noise persistence
998+
*/
999+
T m_noisePersistence;
1000+
1001+
/*!
1002+
@brief Permutation table
1003+
*/
1004+
std::array<T, 512> m_permutationTable;
1005+
1006+
/*!
1007+
@brief Number of noise octaves
1008+
*/
1009+
unsigned int m_noiseOctaves;
1010+
1011+
/*!
1012+
@brief Ken Perlin's lerp function
1013+
@param[in] t Input parameter
1014+
@param[in] a Input parameter
1015+
@param[in] b Input parameter
1016+
*/
1017+
virtual T
1018+
lerp(const T t, const T a, const T b) const noexcept
1019+
{
1020+
return a + t * (b - a);
1021+
};
1022+
1023+
/*!
1024+
@brief Ken Perlin's fade function
1025+
@param[in] t Input parameter
1026+
*/
1027+
virtual T
1028+
fade(const T t) const noexcept
1029+
{
1030+
return t * t * t * (t * (t * 6 - 15) + 10);
1031+
};
1032+
1033+
/*!
1034+
@brief Ken Perlins grad function
1035+
@param[in] hash Input parameter
1036+
@param[in] x Input parameter
1037+
@param[in] y Input parameter
1038+
@param[in] z Input parameter
1039+
*/
1040+
T
1041+
grad(const int hash, const T x, const T y, const T z) const noexcept
1042+
{
1043+
const int h = hash & 15;
1044+
const T u = h < 8 ? x : y;
1045+
const T v = h < 4 ? y : h == 12 || h == 14 ? x : y;
1046+
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
1047+
}
1048+
1049+
/*!
1050+
@brief Octave noise function
1051+
@param[in] a_point Input point
1052+
*/
1053+
T
1054+
noise(const Vec3T<T>& a_point) const noexcept
1055+
{
1056+
// Lower cube corner
1057+
const int X = (int)std::floor(a_point[0]) & 255;
1058+
const int Y = (int)std::floor(a_point[1]) & 255;
1059+
const int Z = (int)std::floor(a_point[2]) & 255;
1060+
1061+
// Relative distance wrt lower cube corner
1062+
const double x = a_point[0] - std::floor(a_point[0]);
1063+
const double y = a_point[1] - std::floor(a_point[1]);
1064+
const double z = a_point[2] - std::floor(a_point[2]);
1065+
1066+
// Fade curves
1067+
const double u = fade(x);
1068+
const double v = fade(y);
1069+
const double w = fade(z);
1070+
1071+
// Hash coordinates of 8 cube corners
1072+
const int A = m_permutationTable[X] + Y;
1073+
const int AA = m_permutationTable[A] + Z;
1074+
const int AB = m_permutationTable[A + 1] + Z;
1075+
const int B = m_permutationTable[X + 1] + Y;
1076+
const int BA = m_permutationTable[B] + Z;
1077+
const int BB = m_permutationTable[B + 1] + Z;
1078+
1079+
// Add blended results from 8 corners of cube
1080+
return lerp(
1081+
w,
1082+
lerp(v,
1083+
lerp(u, grad(m_permutationTable[AA], x, y, z), grad(m_permutationTable[BA], x - 1, y, z)),
1084+
lerp(u, grad(m_permutationTable[AB], x, y - 1, z), grad(m_permutationTable[BB], x - 1, y - 1, z))),
1085+
lerp(v,
1086+
lerp(u, grad(m_permutationTable[AA + 1], x, y, z - 1), grad(m_permutationTable[BA + 1], x - 1, y, z - 1)),
1087+
lerp(u,
1088+
grad(m_permutationTable[AB + 1], x, y - 1, z - 1),
1089+
grad(m_permutationTable[BB + 1], x - 1, y - 1, z - 1))));
1090+
};
1091+
};
1092+
8671093
#include "EBGeometry_NamespaceFooter.hpp"
8681094

8691095
#endif

0 commit comments

Comments
 (0)