Cycles: remove old Sobol pattern, simplify sampling dimensions

The multi-dimensional Sobol pattern required us to carefully use as low
dimensions as possible, as quality goes down in higher dimensions. Now that we
have two sampling patterns that are at least as good, there is no need to keep
it around and the implementation can be simplified.

Differential Revision: https://developer.blender.org/D15788
This commit is contained in:
Brecht Van Lommel 2022-08-26 13:14:57 +02:00
parent 50df9caef0
commit 60119daef5
20 changed files with 88 additions and 21504 deletions

View File

@ -81,9 +81,8 @@ enum_use_layer_samples = (
)
enum_sampling_pattern = (
('SOBOL', "Sobol", "Use Sobol random sampling pattern", 0),
('SOBOL', "Sobol-Burley", "Use Sobol-Burley random sampling pattern", 0),
('PROGRESSIVE_MULTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern", 1),
('SOBOL_BURLEY', "Sobol-Burley", "Use Sobol-Burley random sampling pattern", 2),
)
enum_volume_sampling = (
@ -382,7 +381,7 @@ class CyclesRenderSettings(bpy.types.PropertyGroup):
sampling_pattern: EnumProperty(
name="Sampling Pattern",
description="Random sampling pattern used by the integrator. When adaptive sampling is enabled, Progressive Multi-Jitter is always used instead of Sobol",
description="Random sampling pattern used by the integrator. When adaptive sampling is enabled, Progressive Multi-Jitter is always used instead of Sobol-Burley",
items=enum_sampling_pattern,
default='PROGRESSIVE_MULTI_JITTER',
)

View File

@ -343,7 +343,7 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background)
integrator->set_light_sampling_threshold(get_float(cscene, "light_sampling_threshold"));
SamplingPattern sampling_pattern = (SamplingPattern)get_enum(
cscene, "sampling_pattern", SAMPLING_NUM_PATTERNS, SAMPLING_PATTERN_SOBOL);
cscene, "sampling_pattern", SAMPLING_NUM_PATTERNS, SAMPLING_PATTERN_PMJ);
integrator->set_sampling_pattern(sampling_pattern);
int samples = 1;

View File

@ -70,7 +70,7 @@ KERNEL_DATA_ARRAY(KernelShader, shaders)
/* lookup tables */
KERNEL_DATA_ARRAY(float, lookup_table)
/* sobol */
/* PMJ sample pattern */
KERNEL_DATA_ARRAY(float, sample_pattern_lut)
/* image textures */

View File

@ -126,7 +126,7 @@ ccl_device bool integrator_init_from_bake(KernelGlobals kg,
filter_x = filter_y = 0.5f;
}
else {
path_rng_2D(kg, rng_hash, sample, PRNG_FILTER_U, &filter_x, &filter_y);
path_rng_2D(kg, rng_hash, sample, PRNG_FILTER, &filter_x, &filter_y);
}
/* Initialize path state for path integration. */

View File

@ -30,13 +30,13 @@ ccl_device_inline void integrate_camera_sample(KernelGlobals kg,
filter_v = 0.5f;
}
else {
path_rng_2D(kg, rng_hash, sample, PRNG_FILTER_U, &filter_u, &filter_v);
path_rng_2D(kg, rng_hash, sample, PRNG_FILTER, &filter_u, &filter_v);
}
/* Depth of field sampling. */
float lens_u = 0.0f, lens_v = 0.0f;
if (kernel_data.cam.aperturesize > 0.0f) {
path_rng_2D(kg, rng_hash, sample, PRNG_LENS_U, &lens_u, &lens_v);
path_rng_2D(kg, rng_hash, sample, PRNG_LENS, &lens_u, &lens_v);
}
/* Motion blur time sampling. */

View File

@ -1034,7 +1034,7 @@ ccl_device_forceinline int kernel_path_mnee_sample(KernelGlobals kg,
if (microfacet_bsdf->alpha_x > 0.f && microfacet_bsdf->alpha_y > 0.f) {
/* Sample transmissive microfacet bsdf. */
float bsdf_u, bsdf_v;
path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
path_state_rng_2D(kg, rng_state, PRNG_SURFACE_BSDF, &bsdf_u, &bsdf_v);
h = mnee_sample_bsdf_dh(
bsdf->type, microfacet_bsdf->alpha_x, microfacet_bsdf->alpha_y, bsdf_u, bsdf_v);
}

View File

@ -48,7 +48,7 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg,
INTEGRATOR_STATE_WRITE(state, path, volume_bounce) = 0;
INTEGRATOR_STATE_WRITE(state, path, volume_bounds_bounce) = 0;
INTEGRATOR_STATE_WRITE(state, path, rng_hash) = rng_hash;
INTEGRATOR_STATE_WRITE(state, path, rng_offset) = PRNG_BASE_NUM;
INTEGRATOR_STATE_WRITE(state, path, rng_offset) = PRNG_BOUNCE_NUM;
INTEGRATOR_STATE_WRITE(state, path, flag) = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP |
PATH_RAY_TRANSPARENT_BACKGROUND;
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = 0.0f;
@ -314,19 +314,6 @@ ccl_device_inline void path_state_rng_2D(KernelGlobals kg,
kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension, fx, fy);
}
ccl_device_inline float path_state_rng_1D_hash(KernelGlobals kg,
ccl_private const RNGState *rng_state,
uint hash)
{
/* Use a hash instead of dimension, this is not great but avoids adding
* more dimensions to each bounce which reduces quality of dimensions we
* are already using. */
return path_rng_1D(kg,
hash_wang_seeded_uint(rng_state->rng_hash, hash),
rng_state->sample,
rng_state->rng_offset);
}
ccl_device_inline float path_branched_rng_1D(KernelGlobals kg,
ccl_private const RNGState *rng_state,
int branch,

View File

@ -156,7 +156,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg,
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
float light_u, light_v;
path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v);
path_state_rng_2D(kg, rng_state, PRNG_LIGHT, &light_u, &light_v);
if (!light_distribution_sample_from_position(
kg, light_u, light_v, sd->time, sd->P, bounce, path_flag, &ls)) {
@ -348,7 +348,7 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce(
}
float bsdf_u, bsdf_v;
path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
path_state_rng_2D(kg, rng_state, PRNG_SURFACE_BSDF, &bsdf_u, &bsdf_v);
ccl_private const ShaderClosure *sc = shader_bsdf_bssrdf_pick(sd, &bsdf_u);
#ifdef __SUBSURFACE__
@ -457,7 +457,7 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg,
}
float bsdf_u, bsdf_v;
path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v);
path_state_rng_2D(kg, rng_state, PRNG_SURFACE_BSDF, &bsdf_u, &bsdf_v);
float3 ao_N;
const Spectrum ao_weight = shader_bsdf_ao(

View File

@ -144,11 +144,11 @@ ccl_device_forceinline void volume_step_init(KernelGlobals kg,
/* Perform shading at this offset within a step, to integrate over
* over the entire step segment. */
*step_shade_offset = path_state_rng_1D_hash(kg, rng_state, 0x1e31d8a4);
*step_shade_offset = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_SHADE_OFFSET);
/* Shift starting point of all segment by this random amount to avoid
* banding artifacts from the volume bounding shape. */
*steps_offset = path_state_rng_1D_hash(kg, rng_state, 0x3d22c7b3);
*steps_offset = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_OFFSET);
}
}
@ -549,8 +549,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
vstate.tmin = ray->tmin;
vstate.tmax = ray->tmin;
vstate.absorption_only = true;
vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_SCATTER_DISTANCE);
vstate.rphase = path_state_rng_1D(kg, rng_state, PRNG_PHASE_CHANNEL);
vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_SCATTER_DISTANCE);
vstate.rphase = path_state_rng_1D(kg, rng_state, PRNG_VOLUME_PHASE_CHANNEL);
/* Multiple importance sampling: pick between equiangular and distance sampling strategy. */
vstate.direct_sample_method = direct_sample_method;
@ -695,7 +695,7 @@ ccl_device_forceinline bool integrate_volume_sample_light(
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
float light_u, light_v;
path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v);
path_state_rng_2D(kg, rng_state, PRNG_LIGHT, &light_u, &light_v);
if (!light_distribution_sample_from_volume_segment(
kg, light_u, light_v, sd->time, sd->P, bounce, path_flag, ls)) {
@ -736,7 +736,7 @@ ccl_device_forceinline void integrate_volume_direct_light(
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
float light_u, light_v;
path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v);
path_state_rng_2D(kg, rng_state, PRNG_LIGHT, &light_u, &light_v);
if (!light_distribution_sample_from_position(
kg, light_u, light_v, sd->time, P, bounce, path_flag, ls)) {
@ -865,7 +865,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INDIRECT_LIGHT);
float phase_u, phase_v;
path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &phase_u, &phase_v);
path_state_rng_2D(kg, rng_state, PRNG_VOLUME_PHASE, &phase_u, &phase_v);
/* Phase closure, sample direction. */
float phase_pdf;

View File

@ -26,7 +26,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
{
float disk_u, disk_v;
path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &disk_u, &disk_v);
path_state_rng_2D(kg, &rng_state, PRNG_SUBSURFACE_DISK, &disk_u, &disk_v);
/* Read shading point info from integrator state. */
const float3 P = INTEGRATOR_STATE(state, ray, P);
@ -163,7 +163,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg,
}
/* Use importance resampling, sampling one of the hits proportional to weight. */
const float r = lcg_step_float(&lcg_state) * sum_weights;
const float r = path_state_rng_1D(kg, &rng_state, PRNG_SUBSURFACE_DISK_RESAMPLE) * sum_weights;
float partial_sum = 0.0f;
for (int hit = 0; hit < num_eval_hits; hit++) {

View File

@ -166,7 +166,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
ccl_private LocalIntersection &ss_isect)
{
float bssrdf_u, bssrdf_v;
path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v);
path_state_rng_2D(kg, &rng_state, PRNG_SUBSURFACE_BSDF, &bssrdf_u, &bssrdf_v);
const float3 P = INTEGRATOR_STATE(state, ray, P);
const float3 N = INTEGRATOR_STATE(state, ray, D);
@ -272,11 +272,11 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
#endif
/* Sample color channel, use MIS with balance heuristic. */
float rphase = path_state_rng_1D(kg, &rng_state, PRNG_PHASE_CHANNEL);
float rphase = path_state_rng_1D(kg, &rng_state, PRNG_SUBSURFACE_PHASE_CHANNEL);
Spectrum channel_pdf;
int channel = volume_sample_channel(alpha, throughput, rphase, &channel_pdf);
float sample_sigma_t = volume_channel_get(sigma_t, channel);
float randt = path_state_rng_1D(kg, &rng_state, PRNG_SCATTER_DISTANCE);
float randt = path_state_rng_1D(kg, &rng_state, PRNG_SUBSURFACE_SCATTER_DISTANCE);
/* We need the result of the ray-cast to compute the full guided PDF, so just remember the
* relevant terms to avoid recomputing them later. */
@ -289,7 +289,8 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
/* For the initial ray, we already know the direction, so just do classic distance sampling. */
if (bounce > 0) {
/* Decide whether we should use guided or classic sampling. */
bool guided = (path_state_rng_1D(kg, &rng_state, PRNG_LIGHT_TERMINATE) < guided_fraction);
bool guided = (path_state_rng_1D(kg, &rng_state, PRNG_SUBSURFACE_GUIDE_STRATEGY) <
guided_fraction);
/* Determine if we want to sample away from the incoming interface.
* This only happens if we found a nearby opposite interface, and the probability for it
@ -303,12 +304,13 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
float x = clamp(dot(ray.P - P, -N), 0.0f, opposite_distance);
backward_fraction = 1.0f /
(1.0f + expf((opposite_distance - 2.0f * x) / diffusion_length));
guide_backward = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE) < backward_fraction;
guide_backward = path_state_rng_1D(kg, &rng_state, PRNG_SUBSURFACE_GUIDE_DIRECTION) <
backward_fraction;
}
/* Sample scattering direction. */
float scatter_u, scatter_v;
path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &scatter_u, &scatter_v);
path_state_rng_2D(kg, &rng_state, PRNG_SUBSURFACE_BSDF, &scatter_u, &scatter_v);
float cos_theta;
float hg_pdf;
if (guided) {

View File

@ -13,33 +13,6 @@ CCL_NAMESPACE_BEGIN
* this single threaded on a CPU for repeatable results. */
//#define __DEBUG_CORRELATION__
/* High Dimensional Sobol.
*
* Multidimensional sobol with generator matrices. Dimension 0 and 1 are equal
* to classic Van der Corput and Sobol sequences. */
#ifdef __SOBOL__
/* Skip initial numbers that for some dimensions have clear patterns that
* don't cover the entire sample space. Ideally we would have a better
* progressive pattern that doesn't suffer from this problem, because even
* with this offset some dimensions are quite poor.
*/
# define SOBOL_SKIP 64
ccl_device uint sobol_dimension(KernelGlobals kg, int index, int dimension)
{
uint result = 0;
uint i = index + SOBOL_SKIP;
for (int j = 0, x; (x = find_first_set(i)); i >>= x) {
j += x;
result ^= __float_as_uint(kernel_data_fetch(sample_pattern_lut, 32 * dimension + j - 1));
}
return result;
}
#endif /* __SOBOL__ */
ccl_device_forceinline float path_rng_1D(KernelGlobals kg,
uint rng_hash,
int sample,
@ -52,30 +25,9 @@ ccl_device_forceinline float path_rng_1D(KernelGlobals kg,
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_SOBOL_BURLEY) {
return sobol_burley_sample_1D(sample, dimension, rng_hash);
}
#ifdef __SOBOL__
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ)
#endif
{
else {
return pmj_sample_1D(kg, sample, rng_hash, dimension);
}
#ifdef __SOBOL__
/* Sobol sequence value using direction vectors. */
uint result = sobol_dimension(kg, sample, dimension);
float r = (float)result * (1.0f / (float)0xFFFFFFFF);
/* Cranly-Patterson rotation using rng seed */
float shift;
/* Hash rng with dimension to solve correlation issues.
* See T38710, T50116.
*/
uint tmp_rng = hash_wang_seeded_uint(dimension, rng_hash);
shift = tmp_rng * (kernel_data.integrator.scrambling_distance / (float)0xFFFFFFFF);
return r + shift - floorf(r + shift);
#endif
}
ccl_device_forceinline void path_rng_2D(KernelGlobals kg,
@ -93,23 +45,10 @@ ccl_device_forceinline void path_rng_2D(KernelGlobals kg,
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_SOBOL_BURLEY) {
sobol_burley_sample_2D(sample, dimension, rng_hash, fx, fy);
return;
}
#ifdef __SOBOL__
if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ)
#endif
{
else {
pmj_sample_2D(kg, sample, rng_hash, dimension, fx, fy);
return;
}
#ifdef __SOBOL__
/* Sobol. */
*fx = path_rng_1D(kg, rng_hash, sample, dimension);
*fy = path_rng_1D(kg, rng_hash, sample, dimension + 1);
#endif
}
/**
@ -164,7 +103,7 @@ ccl_device_inline bool sample_is_even(int pattern, int sample)
return popcount(uint(sample) & 0xaaaaaaaa) & 1;
}
else {
/* TODO(Stefan): Are there reliable ways of dividing CMJ and Sobol into two classes? */
/* TODO(Stefan): Are there reliable ways of dividing Sobol-Burley into two classes? */
return sample & 0x1;
}
}

View File

@ -50,7 +50,7 @@ ccl_device float svm_ao(
int unoccluded = 0;
for (int sample = 0; sample < num_samples; sample++) {
float disk_u, disk_v;
path_branched_rng_2D(kg, &rng_state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v);
path_branched_rng_2D(kg, &rng_state, sample, num_samples, PRNG_SURFACE_AO, &disk_u, &disk_v);
float2 d = concentric_sample_disk(disk_u, disk_v);
float3 D = make_float3(d.x, d.y, safe_sqrtf(1.0f - dot(d, d)));

View File

@ -129,7 +129,8 @@ ccl_device float3 svm_bevel(
for (int sample = 0; sample < num_samples; sample++) {
float disk_u, disk_v;
path_branched_rng_2D(kg, &rng_state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v);
path_branched_rng_2D(
kg, &rng_state, sample, num_samples, PRNG_SURFACE_BEVEL, &disk_u, &disk_v);
/* Pick random axis in local frame and point on disk. */
float3 disk_N, disk_T, disk_B;

View File

@ -54,7 +54,6 @@ CCL_NAMESPACE_BEGIN
#endif
/* Kernel features */
#define __SOBOL__
#define __DPDU__
#define __BACKGROUND__
#define __CAUSTICS_TRICKS__
@ -147,38 +146,49 @@ CCL_NAMESPACE_BEGIN
# define __BVH_LOCAL__
#endif
/* Path Tracing
* note we need to keep the u/v pairs at even values */
/* Sampling Patterns */
/* Unique numbers for sampling patterns in each bounce. */
enum PathTraceDimension {
PRNG_FILTER_U = 0,
PRNG_FILTER_V = 1,
PRNG_LENS_U = 2,
PRNG_LENS_V = 3,
PRNG_TIME = 4,
PRNG_UNUSED_0 = 5,
PRNG_UNUSED_1 = 6, /* for some reason (6, 7) is a bad sobol pattern */
PRNG_UNUSED_2 = 7, /* with a low number of samples (< 64) */
PRNG_BASE_NUM = 10,
/* Init bounce */
PRNG_FILTER = 0,
PRNG_LENS = 1,
PRNG_TIME = 2,
PRNG_BSDF_U = 0,
PRNG_BSDF_V = 1,
PRNG_LIGHT_U = 2,
PRNG_LIGHT_V = 3,
PRNG_LIGHT_TERMINATE = 4,
PRNG_TERMINATE = 5,
PRNG_PHASE_CHANNEL = 6,
PRNG_SCATTER_DISTANCE = 7,
PRNG_BOUNCE_NUM = 8,
/* Shade bounce */
PRNG_TERMINATE = 0,
PRNG_LIGHT = 1,
PRNG_LIGHT_TERMINATE = 2,
/* Surface */
PRNG_SURFACE_BSDF = 3,
PRNG_SURFACE_AO = 4,
PRNG_SURFACE_BEVEL = 5,
/* Volume */
PRNG_VOLUME_PHASE = 3,
PRNG_VOLUME_PHASE_CHANNEL = 4,
PRNG_VOLUME_SCATTER_DISTANCE = 5,
PRNG_VOLUME_OFFSET = 6,
PRNG_VOLUME_SHADE_OFFSET = 7,
PRNG_BEVEL_U = 6, /* reuse volume dimension, correlation won't harm */
PRNG_BEVEL_V = 7,
/* Subsurface random walk bounces */
PRNG_SUBSURFACE_BSDF = 0,
PRNG_SUBSURFACE_PHASE_CHANNEL = 1,
PRNG_SUBSURFACE_SCATTER_DISTANCE = 2,
PRNG_SUBSURFACE_GUIDE_STRATEGY = 3,
PRNG_SUBSURFACE_GUIDE_DIRECTION = 4,
/* Subsurface disk bounce */
PRNG_SUBSURFACE_DISK = 0,
PRNG_SUBSURFACE_DISK_RESAMPLE = 1,
/* High enough number so we don't need to change it when adding new dimenions,
* low enough so there is no uint16_t overflow with many bounces. */
PRNG_BOUNCE_NUM = 16,
};
enum SamplingPattern {
SAMPLING_PATTERN_SOBOL = 0,
SAMPLING_PATTERN_SOBOL_BURLEY = 0,
SAMPLING_PATTERN_PMJ = 1,
SAMPLING_PATTERN_SOBOL_BURLEY = 2,
SAMPLING_NUM_PATTERNS,
};

View File

@ -39,7 +39,6 @@ set(SRC
shader.cpp
shader_graph.cpp
shader_nodes.cpp
sobol.cpp
stats.cpp
svm.cpp
tables.cpp
@ -77,7 +76,6 @@ set(SRC_HEADERS
shader.h
shader_graph.h
shader_nodes.h
sobol.h
stats.h
svm.h
tables.h

View File

@ -13,7 +13,6 @@
#include "scene/object.h"
#include "scene/scene.h"
#include "scene/shader.h"
#include "scene/sobol.h"
#include "scene/stats.h"
#include "kernel/types.h"
@ -87,10 +86,9 @@ NODE_DEFINE(Integrator)
SOCKET_FLOAT(light_sampling_threshold, "Light Sampling Threshold", 0.01f);
static NodeEnum sampling_pattern_enum;
sampling_pattern_enum.insert("sobol", SAMPLING_PATTERN_SOBOL);
sampling_pattern_enum.insert("pmj", SAMPLING_PATTERN_PMJ);
sampling_pattern_enum.insert("sobol_burley", SAMPLING_PATTERN_SOBOL_BURLEY);
SOCKET_ENUM(sampling_pattern, "Sampling Pattern", sampling_pattern_enum, SAMPLING_PATTERN_SOBOL);
sampling_pattern_enum.insert("pmj", SAMPLING_PATTERN_PMJ);
SOCKET_ENUM(sampling_pattern, "Sampling Pattern", sampling_pattern_enum, SAMPLING_PATTERN_PMJ);
SOCKET_FLOAT(scrambling_distance, "Scrambling Distance", 1.0f);
static NodeEnum denoiser_type_enum;
@ -139,23 +137,6 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
KernelIntegrator *kintegrator = &dscene->data.integrator;
/* Adaptive sampling requires PMJ samples.
*
* This also makes detection of sampling pattern a bit more involved: can not rely on the changed
* state of socket, since its value might be different from the effective value used here. So
* instead compare with previous value in the KernelIntegrator. Only do it if the device was
* updated once (in which case the `sample_pattern_lut` will be allocated to a non-zero size). */
const SamplingPattern new_sampling_pattern = (use_adaptive_sampling) ? SAMPLING_PATTERN_PMJ :
sampling_pattern;
const bool need_update_lut = max_bounce_is_modified() || max_transmission_bounce_is_modified() ||
dscene->sample_pattern_lut.size() == 0 ||
kintegrator->sampling_pattern != new_sampling_pattern;
if (need_update_lut) {
dscene->sample_pattern_lut.tag_realloc();
}
device_free(device, dscene);
/* integrator parameters */
@ -236,7 +217,9 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
FLT_MAX :
sample_clamp_indirect * 3.0f;
kintegrator->sampling_pattern = new_sampling_pattern;
/* Adaptive sampling requires PMJ, see sample_is_even. */
kintegrator->sampling_pattern = (use_adaptive_sampling) ? SAMPLING_PATTERN_PMJ :
sampling_pattern;
kintegrator->scrambling_distance = scrambling_distance;
if (light_sampling_threshold > 0.0f) {
@ -246,36 +229,21 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene
kintegrator->light_inv_rr_threshold = 0.0f;
}
/* sobol directions table */
int max_samples = max_bounce + transparent_max_bounce + 3 + VOLUME_BOUNDS_MAX +
max(BSSRDF_MAX_HITS, BSSRDF_MAX_BOUNCES);
int dimensions = PRNG_BASE_NUM + max_samples * PRNG_BOUNCE_NUM;
dimensions = min(dimensions, SOBOL_MAX_DIMENSIONS);
if (need_update_lut) {
if (kintegrator->sampling_pattern == SAMPLING_PATTERN_SOBOL) {
uint *directions = (uint *)dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions);
sobol_generate_direction_vectors((uint(*)[SOBOL_BITS])directions, dimensions);
dscene->sample_pattern_lut.copy_to_device();
if (kintegrator->sampling_pattern == SAMPLING_PATTERN_PMJ &&
dscene->sample_pattern_lut.size() == 0) {
constexpr int sequence_size = NUM_PMJ_SAMPLES;
constexpr int num_sequences = NUM_PMJ_PATTERNS;
float2 *directions = (float2 *)dscene->sample_pattern_lut.alloc(sequence_size * num_sequences *
2);
TaskPool pool;
for (int j = 0; j < num_sequences; ++j) {
float2 *sequence = directions + j * sequence_size;
pool.push(
function_bind(&progressive_multi_jitter_02_generate_2D, sequence, sequence_size, j));
}
else if (kintegrator->sampling_pattern == SAMPLING_PATTERN_PMJ) {
constexpr int sequence_size = NUM_PMJ_SAMPLES;
constexpr int num_sequences = NUM_PMJ_PATTERNS;
float2 *directions = (float2 *)dscene->sample_pattern_lut.alloc(sequence_size *
num_sequences * 2);
TaskPool pool;
for (int j = 0; j < num_sequences; ++j) {
float2 *sequence = directions + j * sequence_size;
pool.push(
function_bind(&progressive_multi_jitter_02_generate_2D, sequence, sequence_size, j));
}
pool.wait_work();
pool.wait_work();
dscene->sample_pattern_lut.copy_to_device();
}
dscene->sample_pattern_lut.copy_to_device();
}
kintegrator->has_shadow_catcher = scene->has_shadow_catcher();

View File

@ -1,69 +0,0 @@
/* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2008, Frances Y. Kuo and Stephen Joe
* All rights reserved. */
/*
* Sobol sequence direction vectors.
*
* This file contains code to create direction vectors for generating sobol
* sequences in high dimensions. It is adapted from code on this webpage:
*
* http://web.maths.unsw.edu.au/~fkuo/sobol/
*
* From these papers:
*
* S. Joe and F. Y. Kuo, Remark on Algorithm 659: Implementing Sobol's quasirandom
* sequence generator, ACM Trans. Math. Softw. 29, 49-57 (2003)
*
* S. Joe and F. Y. Kuo, Constructing Sobol sequences with better two-dimensional
* projections, SIAM J. Sci. Comput. 30, 2635-2654 (2008)
*/
#include "util/types.h"
#include "scene/sobol.h"
CCL_NAMESPACE_BEGIN
#include "scene/sobol.tables"
void sobol_generate_direction_vectors(uint vectors[][SOBOL_BITS], int dimensions)
{
assert(dimensions <= SOBOL_MAX_DIMENSIONS);
const uint L = SOBOL_BITS;
/* first dimension is exception */
uint *v = vectors[0];
for (uint i = 0; i < L; i++)
v[i] = 1 << (31 - i); // all m's = 1
for (int dim = 1; dim < dimensions; dim++) {
const SobolDirectionNumbers *numbers = &SOBOL_NUMBERS[dim - 1];
const uint s = numbers->s;
const uint a = numbers->a;
const uint *m = numbers->m;
v = vectors[dim];
if (L <= s) {
for (uint i = 0; i < L; i++)
v[i] = m[i] << (31 - i);
}
else {
for (uint i = 0; i < s; i++)
v[i] = m[i] << (31 - i);
for (uint i = s; i < L; i++) {
v[i] = v[i - s] ^ (v[i - s] >> s);
for (uint k = 1; k < s; k++)
v[i] ^= (((a >> (s - 1 - k)) & 1) * v[i - k]);
}
}
}
}
CCL_NAMESPACE_END

View File

@ -1,18 +0,0 @@
/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
#ifndef __SOBOL_H__
#define __SOBOL_H__
#include "util/types.h"
CCL_NAMESPACE_BEGIN
#define SOBOL_BITS 32
#define SOBOL_MAX_DIMENSIONS 21201
void sobol_generate_direction_vectors(uint vectors[][SOBOL_BITS], int dimensions);
CCL_NAMESPACE_END
#endif /* __SOBOL_H__ */

File diff suppressed because it is too large Load Diff