[Bf-blender-cvs] [a06c9b5ca83] master: Cycles: add Sobol-Burley sampling pattern

Nathan Vegdahl noreply at git.blender.org
Fri Aug 19 16:27:31 CEST 2022


Commit: a06c9b5ca8364f95bbfa6c3bedd23307e6817437
Author: Nathan Vegdahl
Date:   Thu Aug 18 20:45:09 2022 +0200
Branches: master
https://developer.blender.org/rBa06c9b5ca8364f95bbfa6c3bedd23307e6817437

Cycles: add Sobol-Burley sampling pattern

Based on the paper "Practical Hash-based Owen Scrambling" by Brent Burley,
2020, Journal of Computer Graphics Techniques.

It is distinct from the existing Sobol sampler in two important ways:
* It is Owen scrambled, which gives it a much better convergence rate in many
  situations.
* It uses padding for higher dimensions, rather than using higher Sobol
  dimensions directly. In practice this is advantagous because high-dimensional
  Sobol sequences have holes in their sampling patterns that don't resolve
  until an unreasonable number of samples are taken. (See Burley's paper for
  details.)

The pattern reduces noise in some benchmark scenes, however it is also slower,
particularly on the CPU. So for now Progressive Multi-Jittered sampling remains
the default.

Differential Revision: https://developer.blender.org/D15679

===================================================================

M	intern/cycles/blender/addon/properties.py
M	intern/cycles/kernel/CMakeLists.txt
M	intern/cycles/kernel/integrator/path_state.h
M	intern/cycles/kernel/integrator/subsurface_random_walk.h
M	intern/cycles/kernel/sample/jitter.h
M	intern/cycles/kernel/sample/pattern.h
A	intern/cycles/kernel/sample/sobol_burley.h
A	intern/cycles/kernel/sample/util.h
M	intern/cycles/kernel/tables.h
M	intern/cycles/kernel/types.h
M	intern/cycles/scene/integrator.cpp
M	intern/cycles/util/hash.h

===================================================================

diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py
index 2c926893f9d..859560c8062 100644
--- a/intern/cycles/blender/addon/properties.py
+++ b/intern/cycles/blender/addon/properties.py
@@ -83,6 +83,7 @@ enum_use_layer_samples = (
 enum_sampling_pattern = (
     ('SOBOL', "Sobol", "Use Sobol 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 = (
diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt
index fbc30234dac..c7dcc928c0d 100644
--- a/intern/cycles/kernel/CMakeLists.txt
+++ b/intern/cycles/kernel/CMakeLists.txt
@@ -274,6 +274,8 @@ set(SRC_KERNEL_SAMPLE_HEADERS
   sample/mapping.h
   sample/mis.h
   sample/pattern.h
+  sample/sobol_burley.h
+  sample/util.h
 )
 
 set(SRC_KERNEL_UTIL_HEADERS
diff --git a/intern/cycles/kernel/integrator/path_state.h b/intern/cycles/kernel/integrator/path_state.h
index 5ec94b934ca..a41e922b593 100644
--- a/intern/cycles/kernel/integrator/path_state.h
+++ b/intern/cycles/kernel/integrator/path_state.h
@@ -321,8 +321,10 @@ ccl_device_inline float path_state_rng_1D_hash(KernelGlobals kg,
   /* 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, cmj_hash_simple(rng_state->rng_hash, hash), rng_state->sample, rng_state->rng_offset);
+  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,
diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h
index 9c67d909bd4..baca0d745e8 100644
--- a/intern/cycles/kernel/integrator/subsurface_random_walk.h
+++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h
@@ -229,7 +229,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg,
   const float phase_log = logf((diffusion_length + 1.0f) / (diffusion_length - 1.0f));
 
   /* Modify state for RNGs, decorrelated from other paths. */
-  rng_state.rng_hash = cmj_hash(rng_state.rng_hash + rng_state.rng_offset, 0xdeadbeef);
+  rng_state.rng_hash = hash_cmj_seeded_uint(rng_state.rng_hash + rng_state.rng_offset, 0xdeadbeef);
 
   /* Random walk until we hit the surface again. */
   bool hit = false;
diff --git a/intern/cycles/kernel/sample/jitter.h b/intern/cycles/kernel/sample/jitter.h
index b5cfa624406..dd170cf2120 100644
--- a/intern/cycles/kernel/sample/jitter.h
+++ b/intern/cycles/kernel/sample/jitter.h
@@ -1,20 +1,12 @@
 /* SPDX-License-Identifier: Apache-2.0
  * Copyright 2011-2022 Blender Foundation */
 
+#include "kernel/sample/util.h"
+#include "util/hash.h"
+
 #pragma once
 CCL_NAMESPACE_BEGIN
 
-ccl_device_inline uint32_t laine_karras_permutation(uint32_t x, uint32_t seed)
-{
-  x += seed;
-  x ^= (x * 0x6c50b47cu);
-  x ^= x * 0xb82f1e52u;
-  x ^= x * 0xc7afe638u;
-  x ^= x * 0x8d22f6e6u;
-
-  return x;
-}
-
 ccl_device_inline uint32_t nested_uniform_scramble(uint32_t x, uint32_t seed)
 {
   x = reverse_integer_bits(x);
@@ -24,46 +16,6 @@ ccl_device_inline uint32_t nested_uniform_scramble(uint32_t x, uint32_t seed)
   return x;
 }
 
-ccl_device_inline uint cmj_hash(uint i, uint p)
-{
-  i ^= p;
-  i ^= i >> 17;
-  i ^= i >> 10;
-  i *= 0xb36534e5;
-  i ^= i >> 12;
-  i ^= i >> 21;
-  i *= 0x93fc4795;
-  i ^= 0xdf6e307f;
-  i ^= i >> 17;
-  i *= 1 | p >> 18;
-
-  return i;
-}
-
-ccl_device_inline uint cmj_hash_simple(uint i, uint p)
-{
-  i = (i ^ 61) ^ p;
-  i += i << 3;
-  i ^= i >> 4;
-  i *= 0x27d4eb2d;
-  return i;
-}
-
-ccl_device_inline float cmj_randfloat(uint i, uint p)
-{
-  return cmj_hash(i, p) * (1.0f / 4294967808.0f);
-}
-
-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;
@@ -71,16 +23,12 @@ ccl_device float pmj_sample_1D(KernelGlobals kg, uint sample, uint rng_hash, uin
   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_x = hash_wang_seeded_float(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, hash);
-#else /* Use a _REGULAR_HASH_. */
-  const uint rv = cmj_hash(dimension, hash);
-#endif
+  const uint rv = hash_cmj_seeded_uint(dimension, hash);
 #ifdef _XOR_SHUFFLE_
 #  warning "Using XOR shuffle."
   const uint s = sample ^ rv;
@@ -101,11 +49,7 @@ 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, hash);
-#  else
-  float dx = cmj_randfloat(d, hash);
-#  endif
+  float dx = hash_cmj_seeded_float(d, hash);
   /* Jitter sample locations and map back into [0 1]. */
   fx = fx + dx + jitter_x;
   fx = fx - floorf(fx);
@@ -129,18 +73,14 @@ ccl_device void pmj_sample_2D(KernelGlobals kg,
   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);
+    jitter_x = hash_wang_seeded_float(dimension, rng_hash) *
+               kernel_data.integrator.scrambling_distance;
+    jitter_y = hash_wang_seeded_float(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, hash);
-#else /* Use a _REGULAR_HASH_. */
-  const uint rv = cmj_hash(dimension, hash);
-#endif
+  const uint rv = hash_cmj_seeded_uint(dimension, hash);
 #ifdef _XOR_SHUFFLE_
 #  warning "Using XOR shuffle."
   const uint s = sample ^ rv;
@@ -159,13 +99,8 @@ 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, hash);
-  float dy = cmj_randfloat_simple(d + 1, hash);
-#  else
-  float dx = cmj_randfloat(d, hash);
-  float dy = cmj_randfloat(d + 1, hash);
-#  endif
+  float dx = hash_cmj_seeded_float(d, hash);
+  float dy = hash_cmj_seeded_float(d + 1, hash);
   /* Jitter sample locations and map back to the unit square [0 1]x[0 1]. */
   float sx = fx + dx + jitter_x;
   float sy = fy + dy + jitter_y;
diff --git a/intern/cycles/kernel/sample/pattern.h b/intern/cycles/kernel/sample/pattern.h
index 89500d51872..e8c3acb5cf7 100644
--- a/intern/cycles/kernel/sample/pattern.h
+++ b/intern/cycles/kernel/sample/pattern.h
@@ -4,6 +4,7 @@
 #pragma once
 
 #include "kernel/sample/jitter.h"
+#include "kernel/sample/sobol_burley.h"
 #include "util/hash.h"
 
 CCL_NAMESPACE_BEGIN
@@ -48,6 +49,10 @@ ccl_device_forceinline float path_rng_1D(KernelGlobals kg,
   return (float)drand48();
 #endif
 
+  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
@@ -66,7 +71,7 @@ ccl_device_forceinline float path_rng_1D(KernelGlobals kg,
   /* Hash rng with dimension to solve correlation issues.
    * See T38710, T50116.
    */
-  uint tmp_rng = cmj_hash_simple(dimension, rng_hash);
+  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);
@@ -86,6 +91,11 @@ ccl_device_forceinline void path_rng_2D(KernelGlobals kg,
   return;
 #endif
 
+  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
diff --git a/intern/cycles/kernel/sample/sobol_burley.h b/intern/cycles/kernel/sample/sobol_burley.h
new file mode 100644
index 00000000000..4e041aa075e
--- /dev/null
+++ b/intern/cycles/kernel/sample/sobol_burley.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: Apache-2.0
+ * Copyright 2011-2022 Blender Foundation */
+
+/*
+ * A shuffled, Owen-scrambled Sobol sampler, implemented with the
+ * techniques from the paper "Practical Hash-based Owen Scrambling"
+ * by Brent Burley, 2020, Journal of Computer Graphics Techniques.
+ *
+ * Note that unlike a standard high-dimensional Sobol sequence, this
+ * Sobol sampler uses padding to achieve higher dimensions, as described
+ * in Burley's paper.
+ */
+
+#pragma once
+
+#include "kernel/sample/util.h"
+#include "util/hash.h"
+#include "util/math.h"
+#include "util/types.h"
+
+CCL_NAMESPACE_BEGIN
+
+/*
+ * Computes a single dimension of a sample from an Owen-scrambled
+ * Sobol sequence.  This is used in the main sampling functions,
+ * sobol_burley_sample_#D(), below.
+ *
+ * - rev_bit_index: the sample index, with reversed order bits.
+ * - dimension:     the sample dimension.
+ * - scramble_seed: the Owen scrambling seed.
+ *
+ * Note that the seed must be well randomized be

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list