#include "vf/NoiseAdv.h" #include #include #include namespace vf { namespace { constexpr float simplexF2 = 0.3660254037844386f; // (sqrt(3) - 1) / 2 constexpr float simplexG2 = 0.21132486540518713f; // (3 - sqrt(3)) / 6 constexpr float simplexF3 = 1.0f / 3.0f; constexpr float simplexG3 = 1.0f / 6.0f; constexpr float gradients2D[8][2] = { {1.0f, 0.0f}, {-1.0f, 0.0f}, {0.0f, 1.0f}, {0.0f, -1.0f}, {0.7071068f, 0.7071068f}, {-0.7071068f, 0.7071068f}, {0.7071068f, -0.7071068f}, {-0.7071068f, -0.7071068f}, }; constexpr float gradients3D[12][3] = { {1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0}, {1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1}, {0, 1, 1}, {0, -1, 1}, {0, 1, -1}, {0, -1, -1}, }; float quinticFade(float t) { return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); } float clampUnit(float v) { return std::max(-1.0f, std::min(1.0f, v)); } } // namespace void NoiseAdv::reseed(uint32_t seed) { seedValue = seed; std::array base{}; for (int i = 0; i < 256; ++i) base[i] = i; uint32_t state = seed ^ 0x9E3779B9u; if (state == 0) state = 0x1u; auto nextRandom = [&state]() { state ^= state << 13; state ^= state >> 17; state ^= state << 5; return state; }; for (int i = 255; i > 0; --i) { int j = static_cast(nextRandom() % static_cast(i + 1)); std::swap(base[i], base[j]); } for (int i = 0; i < 512; ++i) perm[static_cast(i)] = base[static_cast(i & 255)]; } float NoiseAdv::gradDot2D(int hashIndex, float x, float y) const { const float* g = gradients2D[hashIndex & 7]; return g[0] * x + g[1] * y; } float NoiseAdv::gradDot3D(int hashIndex, float x, float y, float z) const { const float* g = gradients3D[hashIndex % 12]; return g[0] * x + g[1] * y + g[2] * z; } uint32_t NoiseAdv::hashCell(int x, int y) const { uint32_t h = seedValue; h ^= static_cast(x) * 0x27D4EB2Fu; h ^= static_cast(y) * 0x165667B1u; h ^= h >> 15; h *= 0x85EBCA6Bu; h ^= h >> 13; h *= 0xC2B2AE35u; h ^= h >> 16; return h; } float NoiseAdv::latticeValue(int x, int y) const { uint32_t h = hashCell(x, y); return (static_cast(h & 0xFFFFFFu) / 16777215.0f) * 2.0f - 1.0f; } float NoiseAdv::simplex2D(float x, float y) const { float s = (x + y) * simplexF2; int i = static_cast(std::floor(x + s)); int j = static_cast(std::floor(y + s)); float t = static_cast(i + j) * simplexG2; float x0Origin = static_cast(i) - t; float y0Origin = static_cast(j) - t; float x0 = x - x0Origin; float y0 = y - y0Origin; int i1 = 0, j1 = 0; if (x0 > y0) { i1 = 1; j1 = 0; } else { i1 = 0; j1 = 1; } float x1 = x0 - static_cast(i1) + simplexG2; float y1 = y0 - static_cast(j1) + simplexG2; float x2 = x0 - 1.0f + 2.0f * simplexG2; float y2 = y0 - 1.0f + 2.0f * simplexG2; int ii = i & 255; int jj = j & 255; float n0 = 0.0f, n1 = 0.0f, n2 = 0.0f; float t0 = 0.5f - x0 * x0 - y0 * y0; if (t0 > 0.0f) { int hash0 = perm[static_cast(ii + perm[static_cast(jj)])]; t0 *= t0; n0 = t0 * t0 * gradDot2D(hash0, x0, y0); } float t1 = 0.5f - x1 * x1 - y1 * y1; if (t1 > 0.0f) { int hash1 = perm[static_cast(ii + i1 + perm[static_cast(jj + j1)])]; t1 *= t1; n1 = t1 * t1 * gradDot2D(hash1, x1, y1); } float t2 = 0.5f - x2 * x2 - y2 * y2; if (t2 > 0.0f) { int hash2 = perm[static_cast(ii + 1 + perm[static_cast(jj + 1)])]; t2 *= t2; n2 = t2 * t2 * gradDot2D(hash2, x2, y2); } return clampUnit(70.0f * (n0 + n1 + n2)); } float NoiseAdv::simplex3D(float x, float y, float z) const { float s = (x + y + z) * simplexF3; int i = static_cast(std::floor(x + s)); int j = static_cast(std::floor(y + s)); int k = static_cast(std::floor(z + s)); float t = static_cast(i + j + k) * simplexG3; float x0 = x - (static_cast(i) - t); float y0 = y - (static_cast(j) - t); float z0 = z - (static_cast(k) - t); int i1, j1, k1; int i2, j2, k2; if (x0 >= y0) { if (y0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } else if (x0 >= z0) { i1 = 1; j1 = 0; k1 = 0; i2 = 1; j2 = 0; k2 = 1; } else { i1 = 0; j1 = 0; k1 = 1; i2 = 1; j2 = 0; k2 = 1; } } else { if (y0 < z0) { i1 = 0; j1 = 0; k1 = 1; i2 = 0; j2 = 1; k2 = 1; } else if (x0 < z0) { i1 = 0; j1 = 1; k1 = 0; i2 = 0; j2 = 1; k2 = 1; } else { i1 = 0; j1 = 1; k1 = 0; i2 = 1; j2 = 1; k2 = 0; } } float x1 = x0 - static_cast(i1) + simplexG3; float y1 = y0 - static_cast(j1) + simplexG3; float z1 = z0 - static_cast(k1) + simplexG3; float x2 = x0 - static_cast(i2) + 2.0f * simplexG3; float y2 = y0 - static_cast(j2) + 2.0f * simplexG3; float z2 = z0 - static_cast(k2) + 2.0f * simplexG3; float x3 = x0 - 1.0f + 3.0f * simplexG3; float y3 = y0 - 1.0f + 3.0f * simplexG3; float z3 = z0 - 1.0f + 3.0f * simplexG3; int ii = i & 255; int jj = j & 255; int kk = k & 255; float n0 = 0.0f, n1 = 0.0f, n2 = 0.0f, n3 = 0.0f; float t0 = 0.6f - x0 * x0 - y0 * y0 - z0 * z0; if (t0 > 0.0f) { int hash0 = perm[static_cast(ii + perm[static_cast(jj + perm[static_cast(kk)])])]; t0 *= t0; n0 = t0 * t0 * gradDot3D(hash0, x0, y0, z0); } float t1 = 0.6f - x1 * x1 - y1 * y1 - z1 * z1; if (t1 > 0.0f) { int hash1 = perm[static_cast(ii + i1 + perm[static_cast(jj + j1 + perm[static_cast(kk + k1)])])]; t1 *= t1; n1 = t1 * t1 * gradDot3D(hash1, x1, y1, z1); } float t2 = 0.6f - x2 * x2 - y2 * y2 - z2 * z2; if (t2 > 0.0f) { int hash2 = perm[static_cast(ii + i2 + perm[static_cast(jj + j2 + perm[static_cast(kk + k2)])])]; t2 *= t2; n2 = t2 * t2 * gradDot3D(hash2, x2, y2, z2); } float t3 = 0.6f - x3 * x3 - y3 * y3 - z3 * z3; if (t3 > 0.0f) { int hash3 = perm[static_cast(ii + 1 + perm[static_cast(jj + 1 + perm[static_cast(kk + 1)])])]; t3 *= t3; n3 = t3 * t3 * gradDot3D(hash3, x3, y3, z3); } return clampUnit(32.0f * (n0 + n1 + n2 + n3)); } float NoiseAdv::value2D(float x, float y) const { int xi = static_cast(std::floor(x)); int yi = static_cast(std::floor(y)); float xf = x - static_cast(xi); float yf = y - static_cast(yi); float v00 = latticeValue(xi, yi); float v10 = latticeValue(xi + 1, yi); float v01 = latticeValue(xi, yi + 1); float v11 = latticeValue(xi + 1, yi + 1); float u = quinticFade(xf); float v = quinticFade(yf); float top = v00 + (v10 - v00) * u; float bottom = v01 + (v11 - v01) * u; return clampUnit(top + (bottom - top) * v); } WorleyResult NoiseAdv::worley2D(float x, float y, DistanceMetric metric) const { int cellX = static_cast(std::floor(x)); int cellY = static_cast(std::floor(y)); float best1 = std::numeric_limits::max(); float best2 = std::numeric_limits::max(); int bestCellX = cellX; int bestCellY = cellY; for (int dy = -1; dy <= 1; ++dy) { for (int dx = -1; dx <= 1; ++dx) { int neighborX = cellX + dx; int neighborY = cellY + dy; uint32_t h = hashCell(neighborX, neighborY); float jitterX = static_cast(h & 0xFFFFu) / 65535.0f; float jitterY = static_cast((h >> 16) & 0xFFFFu) / 65535.0f; float pointX = static_cast(neighborX) + jitterX; float pointY = static_cast(neighborY) + jitterY; float distX = x - pointX; float distY = y - pointY; float distance; switch (metric) { case DistanceMetric::Manhattan: distance = std::fabs(distX) + std::fabs(distY); break; case DistanceMetric::Chebyshev: distance = std::max(std::fabs(distX), std::fabs(distY)); break; default: distance = std::sqrt(distX * distX + distY * distY); break; } if (distance < best1) { best2 = best1; best1 = distance; bestCellX = neighborX; bestCellY = neighborY; } else if (distance < best2) { best2 = distance; } } } WorleyResult result; result.f1 = best1; result.f2 = best2; result.cellX = bestCellX; result.cellY = bestCellY; return result; } float NoiseAdv::ridged2D(float x, float y) const { return 1.0f - std::fabs(simplex2D(x, y)); } float NoiseAdv::ridgedFbm2D(float x, float y, const FbmParams& fbmParams) const { float sum = 0.0f; float freq = fbmParams.frequency; float amp = fbmParams.amplitude; float weight = 1.0f; float norm = 0.0f; for (int octave = 0; octave < fbmParams.octaves; ++octave) { float signal = 1.0f - std::fabs(simplex2D(x * freq, y * freq)); signal *= signal; signal *= weight; weight = std::max(0.0f, std::min(1.0f, signal)); sum += signal * amp; norm += amp; freq *= fbmParams.lacunarity; amp *= fbmParams.gain; } return norm > 0.0f ? sum / norm : 0.0f; } float NoiseAdv::fbm2D(float x, float y, const FbmParams& fbmParams) const { float sum = 0.0f; float freq = fbmParams.frequency; float amp = fbmParams.amplitude; float norm = 0.0f; for (int octave = 0; octave < fbmParams.octaves; ++octave) { sum += simplex2D(x * freq, y * freq) * amp; norm += amp; freq *= fbmParams.lacunarity; amp *= fbmParams.gain; } return norm > 0.0f ? clampUnit(sum / norm) : 0.0f; } float NoiseAdv::fbm3D(float x, float y, float z, const FbmParams& fbmParams) const { float sum = 0.0f; float freq = fbmParams.frequency; float amp = fbmParams.amplitude; float norm = 0.0f; for (int octave = 0; octave < fbmParams.octaves; ++octave) { sum += simplex3D(x * freq, y * freq, z * freq) * amp; norm += amp; freq *= fbmParams.lacunarity; amp *= fbmParams.gain; } return norm > 0.0f ? clampUnit(sum / norm) : 0.0f; } float NoiseAdv::warpedFbm2D(float x, float y, const FbmParams& fbmParams, float warpStrength) const { float warpX = simplex2D(x * 0.5f + 17.0f, y * 0.5f - 43.0f); float warpY = simplex2D(x * 0.5f - 89.0f, y * 0.5f + 5.0f); return fbm2D(x + warpX * warpStrength, y + warpY * warpStrength, fbmParams); } } // namespace vf