This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/intern/cycles/scene/tabulated_sobol.cpp
Nathan Vegdahl b0cc8e8dde Cycles: switch from pretabulated 2D PMJ02 to pretabulated 4D Sobol
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
2022-12-14 17:39:13 +01:00

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