Cycles: Scrambling distance for the PMJ sampler

Adds scrambling distance to the PMJ sampler. This is based
on the work by Mathieu Menuet in D12318 who created the original
implementation for the Sobol sampler.

Reviewed By: brecht

Maniphest Tasks: T92181

Differential Revision: https://developer.blender.org/D12854
This commit is contained in:
William Leeson 2021-10-27 14:14:43 +02:00 committed by William Leeson
parent 7b1c5712f8
commit 82cf25dfbf
Notes: blender-bot 2023-02-14 08:07:50 +01:00
Referenced by issue #92181, Scrambling Distance and PMJ
4 changed files with 51 additions and 53 deletions

View File

@ -290,11 +290,11 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel):
col.active = not(cscene.use_adaptive_sampling)
col.prop(cscene, "sampling_pattern", text="Pattern")
col = layout.column(align=True)
col.active = cscene.sampling_pattern == 'SOBOL' and not cscene.use_adaptive_sampling
col.active = not cscene.use_adaptive_sampling
col.prop(cscene, "scrambling_distance", text="Scrambling Distance Strength")
col.prop(cscene, "adaptive_scrambling_distance", text="Adaptive Scrambling Distance")
col = layout.column(align=True)
col.active = ((cscene.scrambling_distance < 1.0) or cscene.adaptive_scrambling_distance) and cscene.sampling_pattern == 'SOBOL' and not cscene.use_adaptive_sampling
col.active = ((cscene.scrambling_distance < 1.0) or cscene.adaptive_scrambling_distance) and not cscene.use_adaptive_sampling
col.prop(cscene, "preview_scrambling_distance", text="Viewport Scrambling Distance")
layout.separator()

View File

@ -340,14 +340,16 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background)
cscene, "sampling_pattern", SAMPLING_NUM_PATTERNS, SAMPLING_PATTERN_SOBOL);
integrator->set_sampling_pattern(sampling_pattern);
bool use_adaptive_sampling = false;
if (preview) {
integrator->set_use_adaptive_sampling(
RNA_boolean_get(&cscene, "use_preview_adaptive_sampling"));
use_adaptive_sampling = RNA_boolean_get(&cscene, "use_preview_adaptive_sampling");
integrator->set_use_adaptive_sampling(use_adaptive_sampling);
integrator->set_adaptive_threshold(get_float(cscene, "preview_adaptive_threshold"));
integrator->set_adaptive_min_samples(get_int(cscene, "preview_adaptive_min_samples"));
}
else {
integrator->set_use_adaptive_sampling(RNA_boolean_get(&cscene, "use_adaptive_sampling"));
use_adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling");
integrator->set_use_adaptive_sampling(use_adaptive_sampling);
integrator->set_adaptive_threshold(get_float(cscene, "adaptive_threshold"));
integrator->set_adaptive_min_samples(get_int(cscene, "adaptive_min_samples"));
}
@ -361,7 +363,7 @@ void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background)
/* only use scrambling distance in the viewport if user wants to and disable with AS */
bool preview_scrambling_distance = get_boolean(cscene, "preview_scrambling_distance");
if ((preview && !preview_scrambling_distance) || sampling_pattern != SAMPLING_PATTERN_SOBOL)
if ((preview && !preview_scrambling_distance) || use_adaptive_sampling)
scrambling_distance = 1.0f;
VLOG(1) << "Used Scrambling Distance: " << scrambling_distance;

View File

@ -74,39 +74,9 @@ TileSize tile_calculate_best_size(const int2 &image_size,
TileSize tile_size;
const int num_path_states_per_sample = max_num_path_states / num_samples;
if (scrambling_distance < 0.9f) {
/* Prefer large tiles for scrambling distance. */
if (image_size.x * image_size.y <= num_path_states_per_sample) {
tile_size.width = image_size.x;
tile_size.height = image_size.y;
}
else {
/* Pick the option with the biggest tile size */
int heightOption = num_path_states_per_sample / image_size.x;
int widthOption = num_path_states_per_sample / image_size.y;
// Check if these options are possible
if ((heightOption > 0) || (widthOption > 0)) {
int area1 = image_size.x * heightOption;
int area2 = widthOption * image_size.y;
/* The option with the biggest pixel area */
if (area1 >= area2) {
tile_size.width = image_size.x;
tile_size.height = heightOption;
}
else {
tile_size.width = widthOption;
tile_size.height = image_size.y;
}
}
else { // Large tiles are not an option so use square tiles
if (num_path_states_per_sample != 0) {
tile_size.width = round_down_to_power_of_two(lround(sqrt(num_path_states_per_sample)));
tile_size.height = tile_size.width;
}
else {
tile_size.width = tile_size.height = 1;
}
}
}
/* Prefer large tiles for scrambling distance, bounded by max num path states. */
tile_size.width = min(image_size.x, max_num_path_states);
tile_size.height = min(image_size.y, max(max_num_path_states / tile_size.width, 1));
}
else {
/* Calculate tile size as if it is the most possible one to fit an entire range of samples.

View File

@ -72,13 +72,27 @@ ccl_device_inline float cmj_randfloat_simple(uint i, uint p)
return cmj_hash_simple(i, p) * (1.0f / (float)0xFFFFFFFF);
}
ccl_device_inline float cmj_randfloat_simple_dist(uint i, uint p, float d)
{
return cmj_hash_simple(i, p) * (d / (float)0xFFFFFFFF);
}
ccl_device float pmj_sample_1D(KernelGlobals kg, uint sample, uint rng_hash, uint dimension)
{
uint hash = rng_hash;
float jitter_x = 0.0f;
if (kernel_data.integrator.scrambling_distance < 1.0f) {
hash = kernel_data.integrator.seed;
jitter_x = cmj_randfloat_simple_dist(
dimension, rng_hash, kernel_data.integrator.scrambling_distance);
}
/* Perform Owen shuffle of the sample number to reorder the samples. */
#ifdef _SIMPLE_HASH_
const uint rv = cmj_hash_simple(dimension, rng_hash);
const uint rv = cmj_hash_simple(dimension, hash);
#else /* Use a _REGULAR_HASH_. */
const uint rv = cmj_hash(dimension, rng_hash);
const uint rv = cmj_hash(dimension, hash);
#endif
#ifdef _XOR_SHUFFLE_
# warning "Using XOR shuffle."
@ -101,12 +115,12 @@ ccl_device float pmj_sample_1D(KernelGlobals kg, uint sample, uint rng_hash, uin
#ifndef _NO_CRANLEY_PATTERSON_ROTATION_
/* Use Cranley-Patterson rotation to displace the sample pattern. */
# ifdef _SIMPLE_HASH_
float dx = cmj_randfloat_simple(d, rng_hash);
float dx = cmj_randfloat_simple(d, hash);
# else
float dx = cmj_randfloat(d, rng_hash);
float dx = cmj_randfloat(d, hash);
# endif
/* Jitter sample locations and map back into [0 1]. */
fx = fx + dx;
fx = fx + dx + jitter_x;
fx = fx - floorf(fx);
#else
# warning "Not using Cranley-Patterson Rotation."
@ -122,11 +136,23 @@ ccl_device void pmj_sample_2D(KernelGlobals kg,
ccl_private float *x,
ccl_private float *y)
{
uint hash = rng_hash;
float jitter_x = 0.0f;
float jitter_y = 0.0f;
if (kernel_data.integrator.scrambling_distance < 1.0f) {
hash = kernel_data.integrator.seed;
jitter_x = cmj_randfloat_simple_dist(
dimension, rng_hash, kernel_data.integrator.scrambling_distance);
jitter_y = cmj_randfloat_simple_dist(
dimension + 1, rng_hash, kernel_data.integrator.scrambling_distance);
}
/* Perform a shuffle on the sample number to reorder the samples. */
#ifdef _SIMPLE_HASH_
const uint rv = cmj_hash_simple(dimension, rng_hash);
const uint rv = cmj_hash_simple(dimension, hash);
#else /* Use a _REGULAR_HASH_. */
const uint rv = cmj_hash(dimension, rng_hash);
const uint rv = cmj_hash(dimension, hash);
#endif
#ifdef _XOR_SHUFFLE_
# warning "Using XOR shuffle."
@ -137,7 +163,7 @@ ccl_device void pmj_sample_2D(KernelGlobals kg,
/* Based on the sample number a sample pattern is selected and offset by the dimension. */
const uint sample_set = s / NUM_PMJ_SAMPLES;
const uint d = (dimension + sample_set);
const uint d = dimension + sample_set;
uint dim = d % NUM_PMJ_PATTERNS;
int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES));
@ -147,15 +173,15 @@ ccl_device void pmj_sample_2D(KernelGlobals kg,
#ifndef _NO_CRANLEY_PATTERSON_ROTATION_
/* Use Cranley-Patterson rotation to displace the sample pattern. */
# ifdef _SIMPLE_HASH_
float dx = cmj_randfloat_simple(d, rng_hash);
float dy = cmj_randfloat_simple(d + 1, rng_hash);
float dx = cmj_randfloat_simple(d, hash);
float dy = cmj_randfloat_simple(d + 1, hash);
# else
float dx = cmj_randfloat(d, rng_hash);
float dy = cmj_randfloat(d + 1, rng_hash);
float dx = cmj_randfloat(d, hash);
float dy = cmj_randfloat(d + 1, hash);
# endif
/* Jitter sample locations and map back to the unit square [0 1]x[0 1]. */
float sx = fx + dx;
float sy = fy + dy;
float sx = fx + dx + jitter_x;
float sy = fy + dy + jitter_y;
sx = sx - floorf(sx);
sy = sy - floorf(sy);
#else