noise/worley.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /// @file | ||
| 2 | /// @brief Worley cellular noise in 2D and 3D: feature-point distances and Voronoi cell values. | ||
| 3 | #pragma once | ||
| 4 | |||
| 5 | #include "terraingen/noise/hash.h" | ||
| 6 | #include "terraingen/noise/lattice.h" | ||
| 7 | |||
| 8 | namespace terraingen { | ||
| 9 | |||
| 10 | /// Distances from a point to its nearest and second-nearest feature points, and the random value | ||
| 11 | /// of the nearest point's Voronoi cell. | ||
| 12 | struct WorleyDistances { | ||
| 13 | float f1 = 0.0f; ///< Distance to the nearest feature point (F1), in lattice units. | ||
| 14 | float f2 = 0.0f; ///< Distance to the second-nearest feature point (F2), in lattice units. | ||
| 15 | float value = 0.0f; ///< Value of the nearest point's cell, uniform in [-1, 1). | ||
| 16 | }; | ||
| 17 | |||
| 18 | namespace detail { | ||
| 19 | |||
| 20 | /// The squared gap along one axis between a fraction `f` of the centre cell and the cell `d` | ||
| 21 | /// cells away. | ||
| 22 | 5775630 | inline float CellGapSquared(int32_t d, float f) { | |
| 23 | 5775630 | float gap = 0.0f; | |
| 24 | 5775630 | if (d > 0) { | |
| 25 | 2416335 | gap = float(d) - f; | |
| 26 | 3359295 | } else if (d < 0) { | |
| 27 | 2416335 | gap = f - float(d + 1); | |
| 28 | } | ||
| 29 | 5775630 | return gap * gap; | |
| 30 | } | ||
| 31 | |||
| 32 | } // namespace detail | ||
| 33 | |||
| 34 | /// Worley cellular distances at a lattice cell plus fraction, one feature point per cell, | ||
| 35 | /// searching the 5 x 5 cells around it: F1 and the cell value are exact, F2 exact but for rare | ||
| 36 | /// configurations. | ||
| 37 | /// @param fx Fraction in x, in [0, 1). | ||
| 38 | /// @param fy Fraction in y, in [0, 1). | ||
| 39 | /// @return F1 in [0, sqrt(2)] and F2 >= F1. | ||
| 40 | 4110453 | inline WorleyDistances Worley2(int32_t ix, int32_t iy, float fx, float fy, uint32_t seed) { | |
| 41 | 4110453 | float d1 = 1e30f; | |
| 42 | 4110453 | float d2 = 1e30f; | |
| 43 | 4110453 | uint32_t nearest = 0u; | |
| 44 | 24662718 | for (int32_t dy = -2; dy <= 2; ++dy) { | |
| 45 | 123313590 | for (int32_t dx = -2; dx <= 2; ++dx) { | |
| 46 | const UVec3 h = | ||
| 47 | 102761325 | Pcg3dHash(detail::SeededLatticeKey2(uint32_t(ix + dx), uint32_t(iy + dy), seed)); | |
| 48 | 102761325 | const float px = float(dx) + HashToUnitFloat(h.y) - fx; | |
| 49 | 102761325 | const float py = float(dy) + HashToUnitFloat(h.z) - fy; | |
| 50 | 102761325 | const float d = px * px + py * py; | |
| 51 | 102761325 | if (d < d1) { | |
| 52 | 25858586 | d2 = d1; | |
| 53 | 25858586 | d1 = d; | |
| 54 | 25858586 | nearest = h.x; | |
| 55 | 76902739 | } else if (d < d2) { | |
| 56 | 10874536 | d2 = d; | |
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | 4110453 | const WorleyDistances distances = {sqrt(d1), sqrt(d2), 2.0f * HashToUnitFloat(nearest) - 1.0f}; | |
| 61 | 4110453 | return distances; | |
| 62 | } | ||
| 63 | |||
| 64 | /// Worley cellular distances at a point in lattice units. | ||
| 65 | /// @return F1 in [0, sqrt(2)] and F2 >= F1. | ||
| 66 | 4110453 | inline WorleyDistances Worley2(float x, float y, uint32_t seed) { | |
| 67 | 4110453 | const LatticePoint2 point = SplitLattice2(x, y); | |
| 68 | 4110453 | return Worley2(point.ix, point.iy, point.fx, point.fy, seed); | |
| 69 | } | ||
| 70 | |||
| 71 | /// Worley cellular distances in 3D, one feature point per cell: the 3 x 3 x 3 cells around the | ||
| 72 | /// point, then the outer shell of the 5 x 5 x 5 cells where a cell could still be nearer than F2. | ||
| 73 | /// F1 and the cell value are exact, F2 exact but for rare configurations. | ||
| 74 | /// @return F1 in [0, sqrt(3)] and F2 >= F1. | ||
| 75 | 19645 | inline WorleyDistances Worley3(LatticePoint3 point, uint32_t seed) { | |
| 76 | 19645 | const uint32_t seedKey = PcgHash(seed); | |
| 77 | 19645 | float d1 = 1e30f; | |
| 78 | 19645 | float d2 = 1e30f; | |
| 79 | 19645 | uint32_t nearest = 0u; | |
| 80 | 58935 | for (int32_t shell = 0; shell < 2; ++shell) { | |
| 81 | 235740 | for (int32_t dz = -2; dz <= 2; ++dz) { | |
| 82 | 1178700 | for (int32_t dy = -2; dy <= 2; ++dy) { | |
| 83 | 5893500 | for (int32_t dx = -2; dx <= 2; ++dx) { | |
| 84 | 4911250 | const bool inner = abs(dx) <= 1 && abs(dy) <= 1 && abs(dz) <= 1; | |
| 85 | 4911250 | if (inner != (shell == 0)) { | |
| 86 | 4380647 | continue; | |
| 87 | } | ||
| 88 | 4380835 | if (shell == 1 && detail::CellGapSquared(dx, point.fx) + | |
| 89 | 3850420 | detail::CellGapSquared(dy, point.fy) + | |
| 90 | 1925210 | detail::CellGapSquared(dz, point.fz) >= | |
| 91 | d2) { | ||
| 92 | 1925022 | continue; | |
| 93 | } | ||
| 94 | 530603 | const UVec3 h = Pcg3dHash(detail::CellularLatticeKey3( | |
| 95 | 530603 | uint32_t(point.ix + dx), uint32_t(point.iy + dy), uint32_t(point.iz + dz), | |
| 96 | seedKey)); | ||
| 97 | 530603 | const float px = float(dx) + HashToUnitFloat(h.x) - point.fx; | |
| 98 | 530603 | const float py = float(dy) + HashToUnitFloat(h.y) - point.fy; | |
| 99 | 530603 | const float pz = float(dz) + HashToUnitFloat(h.z) - point.fz; | |
| 100 | 530603 | const float d = px * px + py * py + pz * pz; | |
| 101 | 530603 | if (d < d1) { | |
| 102 | 96606 | d2 = d1; | |
| 103 | 96606 | d1 = d; | |
| 104 | 96606 | nearest = PcgHash(h.x ^ h.y ^ h.z); | |
| 105 | 433997 | } else if (d < d2) { | |
| 106 | 55895 | d2 = d; | |
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | 19645 | const WorleyDistances distances = {sqrt(d1), sqrt(d2), 2.0f * HashToUnitFloat(nearest) - 1.0f}; | |
| 113 | 19645 | return distances; | |
| 114 | } | ||
| 115 | |||
| 116 | /// Worley cellular distances in 3D at a point in lattice units. | ||
| 117 | /// @return F1 in [0, sqrt(3)] and F2 >= F1. | ||
| 118 | 7400 | inline WorleyDistances Worley3(float x, float y, float z, uint32_t seed) { | |
| 119 | 7400 | return Worley3(SplitLattice3(x, y, z), seed); | |
| 120 | } | ||
| 121 | |||
| 122 | } // namespace terraingen | ||
| 123 |