7.6 Maximized Minimal Distance Sampler

The left-parenthesis 0 comma 2 right-parenthesis -sequence sampler is more effective than the stratified sampler, thanks to being stratified over all elementary intervals. However, it still sometimes generates sample points that are close together. An alternative is to use a different pair of generator matrices that not only generate left-parenthesis 0 comma 2 right-parenthesis -sequences but that are also specially designed to maximize the distance between samples; this approach is implemented by the MaxMinDistSampler. (See the “Further Reading” section for more details about the origin of these generator matrices.)

<<MaxMinDistSampler Declarations>>= 
class MaxMinDistSampler : public PixelSampler { public: <<MaxMinDistSampler Public Methods>> 
void StartPixel(const Point2i &); std::unique_ptr<Sampler> Clone(int seed); int RoundCount(int count) const { return RoundUpPow2(count); } MaxMinDistSampler(int64_t samplesPerPixel, int nSampledDimensions) : PixelSampler(RoundUpPow2(samplesPerPixel), nSampledDimensions) { CPixel = CMaxMinDist[Log2Int(samplesPerPixel)]; }
private: <<MaxMinDistSampler Private Data>> 
const uint32_t *CPixel;

There are 17 of these specialized matrices, one for each power-of-two number of samples up to 2 Superscript 17 samples; a pointer to the appropriate one is stored in CPixel in the constructor.

<<MaxMinDistSampler Public Methods>>= 
MaxMinDistSampler(int64_t samplesPerPixel, int nSampledDimensions) : PixelSampler(RoundUpPow2(samplesPerPixel), nSampledDimensions) { CPixel = CMaxMinDist[Log2Int(samplesPerPixel)]; }

<<MaxMinDistSampler Private Data>>= 
const uint32_t *CPixel;

Figure 7.32 shows a few of these matrices.

Figure 7.32: Generator matrices for n equals 8 , 16 , and 64 sample patterns for the MaxMinDistSampler. As before, all matrix elements are either 0 or 1, and 1 elements are shown as filled squares here.

Figure 7.33 shows the points that one of the matrices generates. Note that the same sampling pattern is used in each of the 2 times 2 pixels shown there; when the matrices were found, distance between sample points was evaluated using toroidal topology—as if the unit square was rolled into a torus—to allow for high-quality sample tiling.

Figure 7.33: A grid of 2 times 2 pixels, each sampled with 16 samples from the MaxMinDistSampler. Though the same sample points are used in each pixel, their placement has been optimized so that not only are they well distributed within each pixel, but when they are tiled across pixels, sample points also aren’t too close to those in neighboring pixels.

<<Low Discrepancy Declarations>>+=  
extern uint32_t CMaxMinDist[17][32];

The MaxMinDistSampler uses the generator matrix to compute the pixel samples. The first 2D sample dimension’s value is set by uniformly stepping in the first dimension and the second comes from the generator matrix.

<<MaxMinDistSampler Method Definitions>>= 
void MaxMinDistSampler::StartPixel(const Point2i &p) { Float invSPP = (Float)1 / samplesPerPixel; for (int i = 0; i < samplesPerPixel; ++i) samples2D[0][i] = Point2f(i * invSPP, SampleGeneratorMatrix(CPixel, i)); Shuffle(&samples2D[0][0], samplesPerPixel, 1, rng); <<Generate remaining samples for MaxMinDistSampler>> 
for (size_t i = 0; i < samples1D.size(); ++i) VanDerCorput(1, samplesPerPixel, &samples1D[i][0], rng); for (size_t i = 1; i < samples2D.size(); ++i) Sobol2D(1, samplesPerPixel, &samples2D[i][0], rng); for (size_t i = 0; i < samples1DArraySizes.size(); ++i) { int count = samples1DArraySizes[i]; VanDerCorput(count, samplesPerPixel, &sampleArray1D[i][0], rng); } for (size_t i = 0; i < samples2DArraySizes.size(); ++i) { int count = samples2DArraySizes[i]; Sobol2D(count, samplesPerPixel, &sampleArray2D[i][0], rng); }
PixelSampler::StartPixel(p); }

The remaining dimensions are sampled using the first two Sobol prime matrices, like the ZeroTwoSequenceSampler. We have found slightly better results with this approach (versus using the CMaxMinDist matrices) for samples in non-image dimensions of the sample vector. Therefore, the corresponding fragment <<Generate remaining samples for MaxMinDistSampler>> isn’t included here.