The first two dimensions of scrambled, shuffled Sobol and shuffled PMJ02 are equivalent, so this makes no real difference for the first two dimensions. But Sobol allows us to naturally extend to more dimensions. Pretabulated Sobol is now always used, and the sampling pattern settings is now only available as a debug option. This in turn allows the following two things (also implemented): * Use proper 3D samples for combined lens + motion blur sampling. This notably reduces the noise on objects that are simultaneously out-of-focus and motion blurred. * Use proper 3D samples for combined light selection + light sampling. Cycles was already doing something clever here with 2D samples, but using 3D samples is more straightforward and avoids overloading one of the dimensions. In the future this will also allow for proper sampling of e.g. volumetric light sources and other things that may need three or four dimensions. Differential Revision: https://developer.blender.org/D16443
72 lines
3.5 KiB
C++
72 lines
3.5 KiB
C++
/* SPDX-License-Identifier: Apache-2.0
|
|
* Copyright 2019-2022 Blender Foundation */
|
|
|
|
/* This file is based on the paper "Stochastic Generation of (t, s)
|
|
* Sample Sequences" by Helmer, Christensen, and Kensler.
|
|
*/
|
|
|
|
#include "scene/tabulated_sobol.h"
|
|
#include "util/hash.h"
|
|
|
|
#include <math.h>
|
|
#include <vector>
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
void tabulated_sobol_generate_4D(float4 points[], int size, int rng_seed)
|
|
{
|
|
/* Xor values for generating the (4D) Owen-scrambled Sobol sequence.
|
|
* These permute the order we visit the strata in, which is what
|
|
* makes the code below produce the scrambled Sobol sequence. Other
|
|
* choices are also possible, but result in different sequences. */
|
|
static uint xors[4][32] = {
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
{0x00000000, 0x00000001, 0x00000001, 0x00000007, 0x00000001, 0x00000013, 0x00000015,
|
|
0x0000007f, 0x00000001, 0x00000103, 0x00000105, 0x0000070f, 0x00000111, 0x00001333,
|
|
0x00001555, 0x00007fff, 0x00000001, 0x00010003, 0x00010005, 0x0007000f, 0x00010011,
|
|
0x00130033, 0x00150055, 0x007f00ff, 0x00010101, 0x01030303, 0x01050505, 0x070f0f0f,
|
|
0x01111111, 0x13333333, 0x15555555, 0x7fffffff},
|
|
{0x00000000, 0x00000001, 0x00000003, 0x00000001, 0x00000005, 0x0000001f, 0x0000002b,
|
|
0x0000003d, 0x00000011, 0x00000133, 0x00000377, 0x00000199, 0x00000445, 0x00001ccf,
|
|
0x00002ddb, 0x0000366d, 0x00000101, 0x00010303, 0x00030707, 0x00010909, 0x00051515,
|
|
0x001f3f3f, 0x002b6b6b, 0x003dbdbd, 0x00101011, 0x01303033, 0x03707077, 0x01909099,
|
|
0x04515145, 0x1cf3f3cf, 0x2db6b6db, 0x36dbdb6d},
|
|
{0x00000000, 0x00000001, 0x00000000, 0x00000003, 0x0000000d, 0x0000000c, 0x00000005,
|
|
0x0000004f, 0x00000014, 0x000000e7, 0x00000329, 0x0000039c, 0x00000011, 0x00001033,
|
|
0x00000044, 0x000030bb, 0x0000d1cd, 0x0000c2ec, 0x00005415, 0x0004fc3f, 0x00015054,
|
|
0x000e5c97, 0x0032e5b9, 0x0039725c, 0x00000101, 0x01000303, 0x00000404, 0x03000b0b,
|
|
0x0d001d1d, 0x0c002c2c, 0x05004545, 0x4f00cfcf},
|
|
};
|
|
|
|
/* Randomize the seed, in case it's incrementing. The constant is just a
|
|
* random number, and has no other significance. */
|
|
uint rng_i = hash_hp_seeded_uint(rng_seed, 0x44605a73);
|
|
|
|
points[0].x = hash_hp_float(rng_i++);
|
|
points[0].y = hash_hp_float(rng_i++);
|
|
points[0].z = hash_hp_float(rng_i++);
|
|
points[0].w = hash_hp_float(rng_i++);
|
|
|
|
/* Subdivide the domain into smaller and smaller strata, filling in new
|
|
* points as we go. */
|
|
for (int log_N = 0, N = 1; N < size; log_N++, N *= 2) {
|
|
float strata_count = (float)(N * 2);
|
|
for (int i = 0; i < N && (N + i) < size; i++) {
|
|
/* Find the strata that are already occupied in this cell. */
|
|
uint occupied_x_stratum = (uint)(points[i ^ xors[0][log_N]].x * strata_count);
|
|
uint occupied_y_stratum = (uint)(points[i ^ xors[1][log_N]].y * strata_count);
|
|
uint occupied_z_stratum = (uint)(points[i ^ xors[2][log_N]].z * strata_count);
|
|
uint occupied_w_stratum = (uint)(points[i ^ xors[3][log_N]].w * strata_count);
|
|
|
|
/* Generate a new point in the unoccupied strata. */
|
|
points[N + i].x = ((float)(occupied_x_stratum ^ 1) + hash_hp_float(rng_i++)) / strata_count;
|
|
points[N + i].y = ((float)(occupied_y_stratum ^ 1) + hash_hp_float(rng_i++)) / strata_count;
|
|
points[N + i].z = ((float)(occupied_z_stratum ^ 1) + hash_hp_float(rng_i++)) / strata_count;
|
|
points[N + i].w = ((float)(occupied_w_stratum ^ 1) + hash_hp_float(rng_i++)) / strata_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
CCL_NAMESPACE_END
|