samplers_test.cpp 7.6 KB
Newer Older
M
Matt Pharr 已提交
1 2 3 4 5 6 7 8 9 10
// 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

#include <gtest/gtest.h>

#include <pbrt/pbrt.h>

#include <pbrt/samplers.h>

M
Matt Pharr 已提交
11 12
#include <set>

M
Matt Pharr 已提交
13 14 15 16 17 18 19 20 21
using namespace pbrt;

// Make sure all samplers give the same sample values if we go back to the
// same pixel / sample index.
TEST(Sampler, ConsistentValues) {
    constexpr int rootSpp = 4;
    constexpr int spp = rootSpp * rootSpp;
    Point2i resolution(100, 101);

22
    std::vector<Sampler> samplers;
M
Matt Pharr 已提交
23
    samplers.push_back(new HaltonSampler(spp, resolution));
M
Matt Pharr 已提交
24
    samplers.push_back(new IndependentSampler(spp));
M
Matt Pharr 已提交
25
    samplers.push_back(new PaddedSobolSampler(spp, RandomizeStrategy::None));
26
    samplers.push_back(new PaddedSobolSampler(spp, RandomizeStrategy::PermuteDigits));
27
    samplers.push_back(new PaddedSobolSampler(spp, RandomizeStrategy::FastOwen));
M
Matt Pharr 已提交
28
    samplers.push_back(new PaddedSobolSampler(spp, RandomizeStrategy::Owen));
M
Matt Pharr 已提交
29 30 31 32
    samplers.push_back(new ZSobolSampler(spp, resolution, RandomizeStrategy::None));
    samplers.push_back(new ZSobolSampler(spp, resolution, RandomizeStrategy::PermuteDigits));
    samplers.push_back(new ZSobolSampler(spp, resolution, RandomizeStrategy::FastOwen));
    samplers.push_back(new ZSobolSampler(spp, resolution, RandomizeStrategy::Owen));
M
Matt Pharr 已提交
33 34 35
    samplers.push_back(new PMJ02BNSampler(spp));
    samplers.push_back(new StratifiedSampler(rootSpp, rootSpp, true));
    samplers.push_back(new SobolSampler(spp, resolution, RandomizeStrategy::None));
36
    samplers.push_back(new SobolSampler(spp, resolution, RandomizeStrategy::PermuteDigits));
M
Matt Pharr 已提交
37
    samplers.push_back(new SobolSampler(spp, resolution, RandomizeStrategy::Owen));
38
    samplers.push_back(new SobolSampler(spp, resolution, RandomizeStrategy::FastOwen));
M
Matt Pharr 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78

    for (auto &sampler : samplers) {
        std::vector<Float> s1d[spp];
        std::vector<Point2f> s2d[spp];

        for (int s = 0; s < spp; ++s) {
            sampler.StartPixelSample({1, 5}, s);
            for (int i = 0; i < 10; ++i) {
                s2d[s].push_back(sampler.Get2D());
                s1d[s].push_back(sampler.Get1D());
            }
        }

        // Go somewhere else and generate some samples, just to make sure
        // things are shaken up.
        sampler.StartPixelSample({0, 6}, 10);
        sampler.Get2D();
        sampler.Get2D();
        sampler.Get1D();

        // Now go back and generate samples again, but enumerate them in a
        // different order to make sure the sampler is doing the right
        // thing.
        for (int s = spp - 1; s >= 0; --s) {
            sampler.StartPixelSample({1, 5}, s);
            for (int i = 0; i < s2d[s].size(); ++i) {
                EXPECT_EQ(s2d[s][i], sampler.Get2D());
                EXPECT_EQ(s1d[s][i], sampler.Get1D());
            }
        }
    }
}

static void checkElementary(const char *name, std::vector<Point2f> samples,
                            int logSamples) {
    for (int i = 0; i <= logSamples; ++i) {
        // Check one set of elementary intervals: number of intervals
        // in each dimension.
        int nx = 1 << i, ny = 1 << (logSamples - i);

M
Matt Pharr 已提交
79
        std::vector<int> count(size_t(1 << logSamples), 0);
M
Matt Pharr 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
        for (const Point2f &s : samples) {
            // Map the sample to an interval
            Float x = nx * s.x, y = ny * s.y;
            EXPECT_GE(x, 0);
            EXPECT_LT(x, nx);
            EXPECT_GE(y, 0);
            EXPECT_LT(y, ny);
            int index = (int)std::floor(y) * nx + (int)std::floor(x);
            EXPECT_GE(index, 0);
            EXPECT_LT(index, count.size());

            // This should be the first time a sample has landed in its
            // interval.
            EXPECT_EQ(0, count[index])
                << "Sampler " << name << " with interval " << nx << " x " << ny;
            ++count[index];
        }
    }
}

100
static void checkElementarySampler(const char *name, Sampler sampler,
M
Matt Pharr 已提交
101
                                   int logSamples, int res = 1) {
M
Matt Pharr 已提交
102 103 104
    // Get all of the samples for a pixel.
    int spp = sampler.SamplesPerPixel();
    std::vector<Point2f> samples;
M
Matt Pharr 已提交
105 106 107 108 109 110
    for (Point2i p : Bounds2i(Point2i(0, 0), Point2i(res, res))) {
        samples.clear();
        for (int i = 0; i < spp; ++i) {
            sampler.StartPixelSample(p, i);
            samples.push_back(sampler.GetPixel2D());
        }
M
Matt Pharr 已提交
111

M
Matt Pharr 已提交
112 113
        checkElementary(name, samples, logSamples);
    }
M
Matt Pharr 已提交
114 115 116 117 118 119
}

// TODO: check Halton (where the elementary intervals are (2^i, 3^j)).

TEST(PaddedSobolSampler, ElementaryIntervals) {
    for (auto rand :
120
         {RandomizeStrategy::None, RandomizeStrategy::PermuteDigits})
M
Matt Pharr 已提交
121 122 123 124 125 126
        for (int logSamples = 2; logSamples <= 10; ++logSamples)
            checkElementarySampler("PaddedSobolSampler",
                                   new PaddedSobolSampler(1 << logSamples, rand),
                                   logSamples);
}

M
Matt Pharr 已提交
127
TEST(ZSobolSampler, ElementaryIntervals) {
128 129 130 131 132 133 134
    for (int seed : {0, 1, 5, 6, 10, 15})
        for (auto rand :
                 {RandomizeStrategy::None, RandomizeStrategy::PermuteDigits})
            for (int logSamples = 2; logSamples <= 8; ++logSamples)
                checkElementarySampler(StringPrintf("ZSobolSampler - %s - %d", rand, seed).c_str(),
                                       new ZSobolSampler(1 << logSamples, Point2i(10, 10), rand, seed),
                                       logSamples, 10);
M
Matt Pharr 已提交
135 136
}

M
Matt Pharr 已提交
137 138 139 140 141 142 143 144 145 146 147 148
TEST(SobolUnscrambledSampler, ElementaryIntervals) {
    for (int logSamples = 2; logSamples <= 10; ++logSamples)
        checkElementarySampler(
            "Sobol Unscrambled",
            new SobolSampler(1 << logSamples, Point2i(1, 1), RandomizeStrategy::None),
            logSamples);
}

TEST(SobolXORScrambledSampler, ElementaryIntervals) {
    for (int logSamples = 2; logSamples <= 10; ++logSamples)
        checkElementarySampler(
            "Sobol XOR Scrambled",
149
            new SobolSampler(1 << logSamples, Point2i(1, 1), RandomizeStrategy::PermuteDigits),
M
Matt Pharr 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
            logSamples);
}

TEST(SobolOwenScrambledSampler, ElementaryIntervals) {
    for (int logSamples = 2; logSamples <= 10; ++logSamples)
        checkElementarySampler(
            "Sobol Owen Scrambled",
            new SobolSampler(1 << logSamples, Point2i(1, 1), RandomizeStrategy::Owen),
            logSamples);
}

TEST(PMJ02BNSampler, ElementaryIntervals) {
    for (int logSamples = 2; logSamples <= 10; logSamples += 2)
        checkElementarySampler("PMJ02BNSampler", new PMJ02BNSampler(1 << logSamples),
                               logSamples);
}
M
Matt Pharr 已提交
166 167 168 169 170

TEST(ZSobolSampler, ValidIndices) {
    Point2i res(16, 9);
    for (int logSamples = 0; logSamples <= 10; ++logSamples) {
        int spp = 1 << logSamples;
171
        ZSobolSampler sampler(spp, res, RandomizeStrategy::PermuteDigits);
M
Matt Pharr 已提交
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195

        for (int dim = 0; dim < 7; dim += 3) {
            std::set<uint64_t> returnedIndices;
            for (Point2i p : Bounds2i(Point2i(0, 0), res)) {
                uint64_t pow2Base;
                for (int i = 0; i < spp; ++i) {
                    sampler.StartPixelSample(p, i, dim);
                    uint64_t index = sampler.GetSampleIndex();

                    // Make sure no index is repeated across multiple pixels
                    EXPECT_TRUE(returnedIndices.find(index) == returnedIndices.end());
                    returnedIndices.insert(index);

                    // Make sure that all samples for this pixel are within the
                    // same pow2 aligned and sized range of the sample indices.
                    if (i == 0)
                        pow2Base = index / spp;
                    else
                        EXPECT_EQ(index / spp, pow2Base);
                }
            }
        }
    }
}