lowdiscrepancy.h 9.0 KB
Newer Older
M
Matt Pharr 已提交
1 2 3 4 5 6 7 8 9 10 11
// pbrt is Copyright(c) 1998-2020 Matt Pharr, Wenzel Jakob, and Greg Humphreys.
// The pbrt source code is licensed under the Apache License, Version 2.0.
// SPDX: Apache-2.0

#ifndef PBRT_UTIL_LOWDISCREPANCY_H
#define PBRT_UTIL_LOWDISCREPANCY_H

#include <pbrt/pbrt.h>

#include <pbrt/util/check.h>
#include <pbrt/util/float.h>
12
#include <pbrt/util/hash.h>
13
#include <pbrt/util/math.h>
M
Matt Pharr 已提交
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
#include <pbrt/util/primes.h>
#include <pbrt/util/pstd.h>
#include <pbrt/util/sobolmatrices.h>
#include <pbrt/util/vecmath.h>

#include <algorithm>
#include <memory>
#include <string>

namespace pbrt {

// DigitPermutation Definition
class DigitPermutation {
  public:
    // DigitPermutation Public Methods
    DigitPermutation() = default;
    DigitPermutation(int base, uint32_t seed, Allocator alloc) : base(base) {
        CHECK_LT(base, 65536);  // uint16_t
        // Compute number of digits needed for _base_
        nDigits = 0;
M
Matt Pharr 已提交
34 35
        Float invBase = (Float)1 / (Float)base, invBaseM = 1;
        while (1 - invBaseM < 1) {
M
Matt Pharr 已提交
36
            ++nDigits;
M
Matt Pharr 已提交
37
            invBaseM *= invBase;
M
Matt Pharr 已提交
38 39 40
        }

        permutations = alloc.allocate_object<uint16_t>(nDigits * base);
M
Matt Pharr 已提交
41
        // Compute random permutations for all digits
M
Matt Pharr 已提交
42
        for (int digitIndex = 0; digitIndex < nDigits; ++digitIndex) {
M
Matt Pharr 已提交
43 44 45 46 47
            uint32_t digitSeed = MixBits(((base << 8) + digitIndex) ^ seed);
            for (int digitValue = 0; digitValue < base; ++digitValue) {
                int index = digitIndex * base + digitValue;
                permutations[index] = PermutationElement(digitValue, base, digitSeed);
            }
M
Matt Pharr 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60
        }
    }

    PBRT_CPU_GPU
    int Permute(int digitIndex, int digitValue) const {
        DCHECK_LT(digitIndex, nDigits);
        DCHECK_LT(digitValue, base);
        return permutations[digitIndex * base + digitValue];
    }

    std::string ToString() const;

  private:
M
Matt Pharr 已提交
61 62
    // DigitPermutation Private Members
    int base, nDigits;
M
Matt Pharr 已提交
63 64 65 66
    uint16_t *permutations;
};

// Low Discrepancy Declarations
M
Matt Pharr 已提交
67 68
inline PBRT_CPU_GPU uint64_t SobolIntervalToIndex(uint32_t log2Scale,
                                                  uint64_t sampleIndex, Point2i p);
M
Matt Pharr 已提交
69

M
Matt Pharr 已提交
70 71
PBRT_CPU_GPU inline Float BlueNoiseSample(Point2i p, int instance);

M
Matt Pharr 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
PBRT_CPU_GPU
Float RadicalInverse(int baseIndex, uint64_t a);
pstd::vector<DigitPermutation> *ComputeRadicalInversePermutations(uint32_t seed,
                                                                  Allocator alloc = {});
PBRT_CPU_GPU
Float ScrambledRadicalInverse(int baseIndex, uint64_t a, const DigitPermutation &perm);

// NoRandomizer Definition
struct NoRandomizer {
    PBRT_CPU_GPU
    uint32_t operator()(uint32_t v) const { return v; }
};

// Low Discrepancy Inline Functions
PBRT_CPU_GPU inline Float RadicalInverse(int baseIndex, uint64_t a) {
    int base = Primes[baseIndex];
M
Matt Pharr 已提交
88
    Float invBase = (Float)1 / (Float)base, invBaseN = 1;
M
Matt Pharr 已提交
89 90
    uint64_t reversedDigits = 0;
    while (a) {
M
Matt Pharr 已提交
91
        // Extract least significant digit from _a_ and update _reversedDigits_
M
Matt Pharr 已提交
92 93 94 95 96 97 98 99 100
        uint64_t next = a / base;
        uint64_t digit = a - next * base;
        reversedDigits = reversedDigits * base + digit;
        invBaseN *= invBase;
        a = next;
    }
    return std::min(reversedDigits * invBaseN, OneMinusEpsilon);
}

M
Matt Pharr 已提交
101 102
PBRT_CPU_GPU inline uint64_t InverseRadicalInverse(uint64_t inverse, int base,
                                                   int nDigits) {
M
Matt Pharr 已提交
103 104 105 106 107 108 109 110 111 112 113 114
    uint64_t index = 0;
    for (int i = 0; i < nDigits; ++i) {
        uint64_t digit = inverse % base;
        inverse /= base;
        index = index * base + digit;
    }
    return index;
}

PBRT_CPU_GPU inline Float ScrambledRadicalInverse(int baseIndex, uint64_t a,
                                                  const DigitPermutation &perm) {
    int base = Primes[baseIndex];
M
Matt Pharr 已提交
115
    Float invBase = (Float)1 / (Float)base, invBaseM = 1;
M
Matt Pharr 已提交
116 117
    uint64_t reversedDigits = 0;
    int digitIndex = 0;
M
Matt Pharr 已提交
118
    while (1 - invBaseM < 1) {
M
Matt Pharr 已提交
119
        // Permute least significant digit from _a_ and update _reversedDigits_
M
Matt Pharr 已提交
120 121 122
        uint64_t next = a / base;
        int digitValue = a - next * base;
        reversedDigits = reversedDigits * base + perm.Permute(digitIndex, digitValue);
M
Matt Pharr 已提交
123
        invBaseM *= invBase;
M
Matt Pharr 已提交
124 125 126
        ++digitIndex;
        a = next;
    }
M
Matt Pharr 已提交
127
    return std::min(invBaseM * reversedDigits, OneMinusEpsilon);
M
Matt Pharr 已提交
128 129
}

130 131 132
PBRT_CPU_GPU inline Float OwenScrambledRadicalInverse(int baseIndex, uint64_t a,
                                                      uint32_t hash) {
    int base = Primes[baseIndex];
M
Matt Pharr 已提交
133
    Float invBase = (Float)1 / (Float)base, invBaseM = 1;
134 135
    uint64_t reversedDigits = 0;
    int digitIndex = 0;
M
Matt Pharr 已提交
136 137
    while (1 - invBaseM < 1) {
        // Compute Owen-scrambled digit for _digitIndex_
138 139 140 141 142
        uint64_t next = a / base;
        int digitValue = a - next * base;
        uint32_t digitHash = MixBits(hash ^ reversedDigits);
        digitValue = PermutationElement(digitValue, base, digitHash);
        reversedDigits = reversedDigits * base + digitValue;
M
Matt Pharr 已提交
143
        invBaseM *= invBase;
144 145 146
        ++digitIndex;
        a = next;
    }
M
Matt Pharr 已提交
147
    return std::min(invBaseM * reversedDigits, OneMinusEpsilon);
148 149
}

M
Matt Pharr 已提交
150 151 152 153 154 155 156 157 158
PBRT_CPU_GPU inline uint32_t MultiplyGenerator(pstd::span<const uint32_t> C, uint32_t a) {
    uint32_t v = 0;
    for (int i = 0; a != 0; ++i, a >>= 1)
        if (a & 1)
            v ^= C[i];
    return v;
}

template <typename R>
M
Matt Pharr 已提交
159
PBRT_CPU_GPU inline Float SobolSample(int64_t a, int dimension, R randomizer) {
M
Matt Pharr 已提交
160
    DCHECK_LT(dimension, NSobolDimensions);
M
Matt Pharr 已提交
161
    DCHECK(a >= 0 && a < (1ull << SobolMatrixSize));
M
Matt Pharr 已提交
162
    // Compute initial Sobol sample _v_ using generator matrices
M
Matt Pharr 已提交
163 164 165 166
    uint32_t v = 0;
    for (int i = dimension * SobolMatrixSize; a != 0; a >>= 1, i++)
        if (a & 1)
            v ^= SobolMatrices32[i];
M
Matt Pharr 已提交
167

M
Matt Pharr 已提交
168
    // Randomize Sobol sample and return floating-point value
M
Matt Pharr 已提交
169 170
    v = randomizer(v);
    return std::min(v * 0x1p-32f, FloatOneMinusEpsilon);
M
Matt Pharr 已提交
171 172
}

M
Matt Pharr 已提交
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
PBRT_CPU_GPU inline Float BlueNoiseSample(Point2i p, int instance) {
    auto HashPerm = [&](uint64_t index) -> int {
        return uint32_t(MixBits(index ^ (0x55555555 * instance)) >> 24) % 24;
    };

    int nBase4Digits = 8;  // Log2Int(256)
    p.x &= 255;
    p.y &= 255;
    uint64_t mortonIndex = EncodeMorton2(p.x, p.y);

    static const uint8_t permutations[24][4] = {
        {0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 2, 1},
        {0, 3, 1, 2}, {1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0},
        {1, 3, 2, 0}, {1, 3, 0, 2}, {2, 1, 0, 3}, {2, 1, 3, 0}, {2, 0, 1, 3},
        {2, 0, 3, 1}, {2, 3, 0, 1}, {2, 3, 1, 0}, {3, 1, 2, 0}, {3, 1, 0, 2},
        {3, 2, 1, 0}, {3, 2, 0, 1}, {3, 0, 2, 1}, {3, 0, 1, 2}};

    uint32_t sampleIndex = 0;
    for (int i = nBase4Digits - 1; i >= 0; --i) {
        int digitShift = 2 * i;
        int digit = (mortonIndex >> digitShift) & 3;
        int p = HashPerm(mortonIndex >> (digitShift + 2));
        digit = permutations[p][digit];
        sampleIndex |= digit << digitShift;
    }

    return ReverseBits32(sampleIndex) * 0x1p-32f;
}

202 203
// BinaryPermuteScrambler Definition
struct BinaryPermuteScrambler {
M
Matt Pharr 已提交
204
    PBRT_CPU_GPU
M
Matt Pharr 已提交
205
    BinaryPermuteScrambler(uint32_t perm) : permutation(perm) {}
M
Matt Pharr 已提交
206
    PBRT_CPU_GPU
M
Matt Pharr 已提交
207 208
    uint32_t operator()(uint32_t v) const { return permutation ^ v; }
    uint32_t permutation;
M
Matt Pharr 已提交
209 210
};

211 212 213 214
// FastOwenScrambler Definition
struct FastOwenScrambler {
    PBRT_CPU_GPU
    FastOwenScrambler(uint32_t seed) : seed(seed) {}
M
Matt Pharr 已提交
215
    // FastOwenScrambler Public Methods
216
    // Laine et al., Stratified Sampling for Stochastic Transparency, Sec 3.1...
217
    PBRT_CPU_GPU
M
Matt Pharr 已提交
218 219 220
    uint32_t operator()(uint32_t v) const {
        v = ReverseBits32(v);
        v += seed;
221 222 223 224
        v ^= v * 0x6c50b47cu;
        v ^= v * 0xb82f1e52u;
        v ^= v * 0xc7afe638u;
        v ^= v * 0x8d22f6e6u;
M
Matt Pharr 已提交
225 226 227
        return ReverseBits32(v);
    }

228 229 230
    uint32_t seed;
};

M
Matt Pharr 已提交
231 232 233 234
// OwenScrambler Definition
struct OwenScrambler {
    PBRT_CPU_GPU
    OwenScrambler(uint32_t seed) : seed(seed) {}
M
Matt Pharr 已提交
235
    // OwenScrambler Public Methods
M
Matt Pharr 已提交
236
    PBRT_CPU_GPU
M
Matt Pharr 已提交
237 238 239 240 241 242
    uint32_t operator()(uint32_t v) const {
        if (seed & 1)
            v ^= 1u << 31;
        for (int b = 1; b < 32; ++b) {
            // Apply Owen scrambling to binary digit _b_ in _v_
            uint32_t mask = (~0u) << (32 - b);
M
Matt Pharr 已提交
243
            if ((uint32_t)MixBits((v & mask) ^ seed) & (1u << b))
M
Matt Pharr 已提交
244 245 246 247 248
                v ^= 1u << (31 - b);
        }
        return v;
    }

M
Matt Pharr 已提交
249 250 251
    uint32_t seed;
};

M
Matt Pharr 已提交
252
// RandomizeStrategy Definition
253
enum class RandomizeStrategy { None, PermuteDigits, FastOwen, Owen };
M
Matt Pharr 已提交
254 255 256 257

std::string ToString(RandomizeStrategy r);

PBRT_CPU_GPU
M
Matt Pharr 已提交
258
inline uint64_t SobolIntervalToIndex(uint32_t m, uint64_t frame, Point2i p) {
M
Matt Pharr 已提交
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
    if (m == 0)
        return frame;

    const uint32_t m2 = m << 1;
    uint64_t index = uint64_t(frame) << m2;

    uint64_t delta = 0;
    for (int c = 0; frame; frame >>= 1, ++c)
        if (frame & 1)  // Add flipped column m + c + 1.
            delta ^= VdCSobolMatrices[m - 1][c];

    // flipped b
    uint64_t b = (((uint64_t)((uint32_t)p.x) << m) | ((uint32_t)p.y)) ^ delta;

    for (int c = 0; b; b >>= 1, ++c)
        if (b & 1)  // Add column 2 * m - c.
            index ^= VdCSobolMatricesInv[m - 1][c];

    return index;
}

}  // namespace pbrt

#endif  // PBRT_UTIL_LOWDISCREPANCY_H