Use independent Sobol sequences for different sets of dimensions.

This reduces correlation artifacts, while preserving good convergence
for the most part.
This commit is contained in:
Nathan Vegdahl 2020-04-22 18:38:05 +09:00
parent aecff883ab
commit 22b7151919
2 changed files with 17 additions and 9 deletions

View File

@ -374,7 +374,7 @@ pub struct LightPath {
sampling_seed: u32, sampling_seed: u32,
pixel_co: (u32, u32), pixel_co: (u32, u32),
sample_number: u32, // Which sample in the LDS sequence this is. sample_number: u32, // Which sample in the LDS sequence this is.
dim_offset: Cell<u32>, dim_offset: u32,
time: f32, time: f32,
wavelength: f32, wavelength: f32,
@ -407,7 +407,7 @@ impl LightPath {
sampling_seed: sampling_seed, sampling_seed: sampling_seed,
pixel_co: pixel_co, pixel_co: pixel_co,
sample_number: sample_number, sample_number: sample_number,
dim_offset: Cell::new(6), dim_offset: 0,
time: time, time: time,
wavelength: wavelength, wavelength: wavelength,
@ -430,9 +430,14 @@ impl LightPath {
) )
} }
fn next_lds_samp(&self) -> f32 { fn next_lds_sequence(&mut self) {
let dimension = self.dim_offset.get(); self.dim_offset = 0;
self.dim_offset.set(dimension + 1); self.sampling_seed += 1;
}
fn next_lds_samp(&mut self) -> f32 {
let dimension = self.dim_offset;
self.dim_offset += 1;
get_sample( get_sample(
dimension, dimension,
self.sample_number, self.sample_number,
@ -481,6 +486,7 @@ impl LightPath {
self.light_attenuation /= self.closure_sample_pdf; self.light_attenuation /= self.closure_sample_pdf;
// Prepare light ray // Prepare light ray
self.next_lds_sequence();
let light_n = self.next_lds_samp(); let light_n = self.next_lds_samp();
let light_uvw = ( let light_uvw = (
self.next_lds_samp(), self.next_lds_samp(),
@ -599,6 +605,7 @@ impl LightPath {
// Sample closure // Sample closure
let (dir, filter, pdf) = { let (dir, filter, pdf) = {
self.next_lds_sequence();
let u = self.next_lds_samp(); let u = self.next_lds_samp();
let v = self.next_lds_samp(); let v = self.next_lds_samp();
closure.sample( closure.sample(
@ -701,10 +708,7 @@ fn get_sample(dimension: u32, i: u32, pixel_co: (u32, u32), seed: u32) -> f32 {
match dimension { match dimension {
d if d < sobol::MAX_DIMENSION as u32 => { d if d < sobol::MAX_DIMENSION as u32 => {
// Sobol sampling. // Sobol sampling.
// We skip the first 4 samples, because that mitigates some poor sobol::sample(d, i, seed)
// sampling at low sample counts like 16.
sobol::sample(d, i + 4, seed)
// halton::sample(d, i + seed)
} }
d => { d => {
// Random sampling. // Random sampling.

View File

@ -15,8 +15,12 @@ include!(concat!(env!("OUT_DIR"), "/vectors.inc"));
/// parameter exceeds 2^16-1, the sample set will start repeating. /// parameter exceeds 2^16-1, the sample set will start repeating.
#[inline] #[inline]
pub fn sample(dimension: u32, index: u32, seed: u32) -> f32 { pub fn sample(dimension: u32, index: u32, seed: u32) -> f32 {
// This index shuffling approach is due to Brent Burley, and is
// what allows us to create statistically independent Sobol sequences.
let shuffled_rev_index = lk_scramble(index.reverse_bits(), hash(seed, 2)); let shuffled_rev_index = lk_scramble(index.reverse_bits(), hash(seed, 2));
let scramble = hash(dimension ^ seed, 2); let scramble = hash(dimension ^ seed, 2);
let sobol = lk_scramble(sobol_u32_rev(dimension, shuffled_rev_index), scramble).reverse_bits(); let sobol = lk_scramble(sobol_u32_rev(dimension, shuffled_rev_index), scramble).reverse_bits();
u32_to_0_1_f32(sobol) u32_to_0_1_f32(sobol)
} }