Revision control
++ // Copyright (C) 2026 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ // 3D noise volume for the volumetric cloud raymarch, generated at startup.
++ // Uses Schneider's recipe from "Real-time Volumetric Cloudscapes of Horizon
++ // Zero Dawn" (GPU Pro 7), as adopted by Wicked Engine, Frostbite, Decima:
++ //
++ // R channel: Perlin-Worley — Perlin FBM remapped over an inverted Worley
++ // FBM, giving high-contrast cumulus base shape.
++ // G/B/A : Multi-octave inverted Worley FBM at increasing frequencies,
++ // providing fluffy cauliflower detail at three scales.
++ //
++ // All channels are tileable in X, Y, Z.
++
++ #include "cloudnoisetexturedata.h"
++
++ #include <QtCore/QByteArray>
++ #include <QtCore/QSize>
++ #include <QtCore/qfloat16.h>
++ #include <QtCore/qmath.h>
++ #include <QtConcurrent/QtConcurrent>
++
++ #include <algorithm>
++ #include <cmath>
++ #include <cstdint>
++ #include <numeric>
++
++ namespace {
++
++ constexpr int kSize = 64;
++
++ inline uint32_t hashCell(int x, int y, int z, uint32_t salt = 0)
++ {
++ uint32_t h = uint32_t(x) * 73856093u ^ uint32_t(y) * 19349663u ^ uint32_t(z) * 83492791u ^ salt;
++ h ^= h >> 13;
++ h *= 0x5bd1e995u;
++ h ^= h >> 15;
++ return h;
++ }
++
++ inline float hashCellFloat(int x, int y, int z, uint32_t salt = 0)
++ {
++ return (hashCell(x, y, z, salt) & 0xFFFFFFu) / float(0xFFFFFFu);
++ }
++
++ inline int wrap(int v, int period)
++ {
++ int r = v % period;
++ return r < 0 ? r + period : r;
++ }
++
++ // Tileable 3D value noise (used as the building block of Perlin-style FBM).
++ // `period` is the grid resolution that the noise wraps over the [0,1] domain.
++ float valueNoise3T(float u, float v, float w, int period)
++ {
++ const float x = u * period;
++ const float y = v * period;
++ const float z = w * period;
++
++ const int x0 = wrap(int(std::floor(x)), period);
++ const int y0 = wrap(int(std::floor(y)), period);
++ const int z0 = wrap(int(std::floor(z)), period);
++ const int x1 = wrap(x0 + 1, period);
++ const int y1 = wrap(y0 + 1, period);
++ const int z1 = wrap(z0 + 1, period);
++
++ const float fx = x - std::floor(x);
++ const float fy = y - std::floor(y);
++ const float fz = z - std::floor(z);
++
++ const float sx = fx * fx * (3.0f - 2.0f * fx);
++ const float sy = fy * fy * (3.0f - 2.0f * fy);
++ const float sz = fz * fz * (3.0f - 2.0f * fz);
++
++ const float n000 = hashCellFloat(x0, y0, z0);
++ const float n100 = hashCellFloat(x1, y0, z0);
++ const float n010 = hashCellFloat(x0, y1, z0);
++ const float n110 = hashCellFloat(x1, y1, z0);
++ const float n001 = hashCellFloat(x0, y0, z1);
++ const float n101 = hashCellFloat(x1, y0, z1);
++ const float n011 = hashCellFloat(x0, y1, z1);
++ const float n111 = hashCellFloat(x1, y1, z1);
++
++ const auto lerp1 = [](float a, float b, float t) { return a + (b - a) * t; };
++ return lerp1(lerp1(lerp1(n000, n100, sx), lerp1(n010, n110, sx), sy), lerp1(lerp1(n001, n101, sx), lerp1(n011, n111, sx), sy), sz);
++ }
++
++ float perlinFbm3T(float u, float v, float w, int basePeriod, int octaves)
++ {
++ float value = 0.0f;
++ float amp = 0.5f;
++ float total = 0.0f;
++ int period = basePeriod;
++ for (int i = 0; i < octaves; ++i) {
++ value += amp * valueNoise3T(u, v, w, period);
++ total += amp;
++ period *= 2;
++ amp *= 0.5f;
++ }
++ return value / total;
++ }
++
++ // Tileable 3D Worley (F1) — returns distance to nearest feature point,
++ // scaled into ~[0,1].
++ float worley3T(float u, float v, float w, int gridSize, uint32_t salt = 0)
++ {
++ const float x = u * gridSize;
++ const float y = v * gridSize;
++ const float z = w * gridSize;
++ const int cx = int(std::floor(x));
++ const int cy = int(std::floor(y));
++ const int cz = int(std::floor(z));
++ const float fx = x - cx;
++ const float fy = y - cy;
++ const float fz = z - cz;
++
++ float minDistSq = 3.0f;
++ for (int dz = -1; dz <= 1; ++dz) {
++ for (int dy = -1; dy <= 1; ++dy) {
++ for (int dx = -1; dx <= 1; ++dx) {
++ const int nx = wrap(cx + dx, gridSize);
++ const int ny = wrap(cy + dy, gridSize);
++ const int nz = wrap(cz + dz, gridSize);
++ const float fpx = hashCellFloat(nx, ny, nz, salt + 0xA1u);
++ const float fpy = hashCellFloat(nx, ny, nz, salt + 0xB2u);
++ const float fpz = hashCellFloat(nx, ny, nz, salt + 0xC3u);
++ const float diffx = float(dx) + fpx - fx;
++ const float diffy = float(dy) + fpy - fy;
++ const float diffz = float(dz) + fpz - fz;
++ const float distSq = diffx * diffx + diffy * diffy + diffz * diffz;
++ if (distSq < minDistSq)
++ minDistSq = distSq;
++ }
++ }
++ }
++ return std::min(std::sqrt(minDistSq) / 0.7f, 1.0f);
++ }
++
++ // Three-octave inverted Worley FBM. Inverted so high values are at cell
++ // centers (the "cloudy" interior), low values between cells.
++ // Octaves at base, 2× base, 4× base with weights 0.625/0.25/0.125 (sum=1).
++ // gridBase × 4 must be ≤ kSize to avoid aliasing.
++ float invWorleyFbm(float u, float v, float w, int gridBase, uint32_t salt)
++ {
++ const float w1 = 1.0f - worley3T(u, v, w, gridBase, salt + 0x010u);
++ const float w2 = 1.0f - worley3T(u, v, w, gridBase * 2, salt + 0x020u);
++ const float w3 = 1.0f - worley3T(u, v, w, gridBase * 4, salt + 0x030u);
++ return w1 * 0.625f + w2 * 0.25f + w3 * 0.125f;
++ }
++
++ inline float remap(float v, float oldMin, float oldMax, float newMin, float newMax)
++ {
++ return newMin + (v - oldMin) / (oldMax - oldMin) * (newMax - newMin);
++ }
++
++ } // namespace
++
++ CloudNoiseTextureData::CloudNoiseTextureData(QQuick3DObject *parent) : QQuick3DTextureData(parent)
++ {
++ setSize(QSize(kSize, kSize));
++ setDepth(kSize);
++ setFormat(QQuick3DTextureData::RGBA16F);
++
++ QByteArray data;
++ data.resize(qsizetype(kSize) * kSize * kSize * 4 * qsizetype(sizeof(qfloat16)));
++ qfloat16 *out = reinterpret_cast<qfloat16 *>(data.data());
++
++ // Worley grid bases for each channel. The 4× highest octave must stay
++ // within kSize (64) to avoid Worley aliasing — so base ≤ 16.
++ constexpr int gridR = 6; // base Worley used inside Perlin-Worley remap
++ constexpr int gridG = 6; // mid cumulus puff frequency
++ constexpr int gridB = 10; // higher detail
++ constexpr int gridA = 16; // finest detail (16 × 4 = 64 = kSize limit)
++
++ // Generate the volume one z-slice per thread. Slices write into disjoint
++ // regions of the output buffer, so no synchronization is needed.
++ std::vector<int> sliceIndices(kSize);
++ std::iota(sliceIndices.begin(), sliceIndices.end(), 0);
++
++ QtConcurrent::blockingMap(sliceIndices, [&](int z) {
++ const float w = float(z) / float(kSize);
++ qfloat16 *sliceOut = out + qsizetype(z) * kSize * kSize * 4;
++ for (int y = 0; y < kSize; ++y) {
++ const float v = float(y) / float(kSize);
++ for (int x = 0; x < kSize; ++x) {
++ const float u = float(x) / float(kSize);
++
++ // R: Schneider's Perlin-Worley. Perlin FBM is "folded" via
++ // abs(p * 2 - 1) so both Perlin extremes map to high values,
++ // then remapped over an inverted Worley FBM. The result has
++ // smooth Perlin-style variation modulated by Worley cells —
++ // the canonical "cloud base shape" used by AAA renderers.
++ const float perlin = perlinFbm3T(u, v, w, 4, 4);
++ const float pfbm = std::abs(perlin * 2.0f - 1.0f);
++ const float wfbm = invWorleyFbm(u, v, w, gridR, 0x100u);
++ float r = remap(pfbm, 0.0f, 1.0f, wfbm, 1.0f);
++
++ // G/B/A: pure Worley FBM at three increasing frequencies for
++ // cauliflower detail at three scales of carving.
++ float g = invWorleyFbm(u, v, w, gridG, 0x200u);
++ float b = invWorleyFbm(u, v, w, gridB, 0x300u);
++ float a = invWorleyFbm(u, v, w, gridA, 0x400u);
++
++ r = std::clamp(r, 0.0f, 1.0f);
++ g = std::clamp(g, 0.0f, 1.0f);
++ b = std::clamp(b, 0.0f, 1.0f);
++ a = std::clamp(a, 0.0f, 1.0f);
++
++ sliceOut[0] = qfloat16(r);
++ sliceOut[1] = qfloat16(g);
++ sliceOut[2] = qfloat16(b);
++ sliceOut[3] = qfloat16(a);
++ sliceOut += 4;
++ }
++ }
++ });
++
++ setTextureData(data);
++ }