Implement Sobol sampler that does both Owen and Cranley-Patterson.
This seems to work well in practice, and only takes one more addition operation.
This commit is contained in:
parent
e3152e6f9c
commit
de5a643a2a
|
@ -709,7 +709,10 @@ fn get_sample(dimension: u32, i: u32, pixel_co: (u32, u32), seed: u32) -> f32 {
|
|||
n if (n - 1) < sobol::MAX_DIMENSION as u32 => {
|
||||
let dim = n - 1;
|
||||
// Sobol sampling.
|
||||
sobol::sample_owen(dim, i, hash_u32(dim, scramble))
|
||||
// We skip the first 32 samples because doing so reduces noise
|
||||
// in some areas when rendering at 64 spp. Not sure why, but it
|
||||
// works.
|
||||
sobol::sample_owen_cranley(dim, i + 32, hash_u32(dim, scramble))
|
||||
}
|
||||
_ => {
|
||||
// Random sampling.
|
||||
|
|
|
@ -48,9 +48,52 @@ pub fn sample_cranley(dimension: u32, index: u32, scramble: u32) -> f32 {
|
|||
/// works well.
|
||||
#[inline]
|
||||
pub fn sample_owen(dimension: u32, index: u32, scramble: u32) -> f32 {
|
||||
// Get the sobol point.
|
||||
let mut n = sobol_u32(dimension, index);
|
||||
u32_to_0_1_f32(owen_scramble_u32(sobol_u32(dimension, index), scramble))
|
||||
}
|
||||
|
||||
/// Same as `sample()` except applies both Owen scrambling and
|
||||
/// Cranley-Patterson rotation using the given scramble parameter.
|
||||
///
|
||||
/// For the technically curious: this first does Owen scrambling, and then
|
||||
/// Cranley-Patterson. If it were done the other way around it would
|
||||
/// invalidate the Owen scrambling.
|
||||
///
|
||||
/// To get proper scrambling and rotation, you need to use a different scramble
|
||||
/// value for each dimension, and those values should be generated more-or-less
|
||||
/// randomly. For example, using a 32-bit hash of the dimension parameter
|
||||
/// works well.
|
||||
#[inline]
|
||||
pub fn sample_owen_cranley(dimension: u32, index: u32, scramble: u32) -> f32 {
|
||||
// Reusing the same scramble parameter for both the Owen scrambling and
|
||||
// the Cranely-Patterson rotation actually works fine, because the Owen
|
||||
// scrambling is implemented as a sort of hash, so they don't end up being
|
||||
// correlated.
|
||||
u32_to_0_1_f32(owen_scramble_u32(sobol_u32(dimension, index), scramble).wrapping_add(scramble))
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/// The actual core Sobol samplng code. Used by the other functions.
|
||||
#[inline(always)]
|
||||
fn sobol_u32(dimension: u32, mut index: u32) -> u32 {
|
||||
assert!(dimension < MAX_DIMENSION);
|
||||
let vecs = &VECTORS[dimension as usize];
|
||||
|
||||
let mut result = 0;
|
||||
let mut i = 0;
|
||||
while index != 0 {
|
||||
let j = index.trailing_zeros();
|
||||
result ^= vecs[(i + j) as usize];
|
||||
i += j + 1;
|
||||
index >>= j + 1;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Scrambles `n` using Owen scrambling and the given scramble parameter.
|
||||
#[inline(always)]
|
||||
fn owen_scramble_u32(mut n: u32, scramble: u32) -> u32 {
|
||||
// We don't need the lowest 8 bits because we're converting to an f32 at
|
||||
// the end which only has 24 bits of precision anyway. And doing this
|
||||
// allows the seed to affect the mixing of the higher bits to make them
|
||||
|
@ -88,30 +131,8 @@ pub fn sample_owen(dimension: u32, index: u32, scramble: u32) -> f32 {
|
|||
}
|
||||
n = n.reverse_bits();
|
||||
|
||||
// Shift back into place.
|
||||
n <<= 8;
|
||||
|
||||
u32_to_0_1_f32(n)
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
/// The actual core Sobol samplng code. Used by the other functions.
|
||||
#[inline(always)]
|
||||
fn sobol_u32(dimension: u32, mut index: u32) -> u32 {
|
||||
assert!(dimension < MAX_DIMENSION);
|
||||
let vecs = &VECTORS[dimension as usize];
|
||||
|
||||
let mut result = 0;
|
||||
let mut i = 0;
|
||||
while index != 0 {
|
||||
let j = index.trailing_zeros();
|
||||
result ^= vecs[(i + j) as usize];
|
||||
i += j + 1;
|
||||
index >>= j + 1;
|
||||
}
|
||||
|
||||
result
|
||||
// Return the scrambled value, shifted back into place.
|
||||
n << 8
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
|
Loading…
Reference in New Issue
Block a user