Multiple improvements to sampling.
1. Use better constants for the hash-based Owen scrambling. 2. Use golden ratio sampling for the wavelength dimension. On the use of golden ratio sampling: Since hero wavelength sampling uses multiple equally-spaced wavelengths, and most samplers only consider the spacing of individual samples, those samplers weren't actually doing a good job of distributing all the wavelengths evenly. Golden ratio sampling, on the other hand, does this effortlessly by its nature, and the resulting reduction of color noise is huge.
This commit is contained in:
parent
e0cb436209
commit
9b4781c81d
|
@ -244,9 +244,9 @@ impl<'a> Renderer<'a> {
|
||||||
// Calculate image plane x and y coordinates
|
// Calculate image plane x and y coordinates
|
||||||
let (img_x, img_y) = {
|
let (img_x, img_y) = {
|
||||||
let filter_x =
|
let filter_x =
|
||||||
fast_logit(get_sample(0, si as u32, (x, y), self.seed), 1.5) + 0.5;
|
fast_logit(get_sample(4, si as u32, (x, y), self.seed), 1.5) + 0.5;
|
||||||
let filter_y =
|
let filter_y =
|
||||||
fast_logit(get_sample(1, si as u32, (x, y), self.seed), 1.5) + 0.5;
|
fast_logit(get_sample(5, si as u32, (x, y), self.seed), 1.5) + 0.5;
|
||||||
let samp_x = (filter_x + x as f32) * cmpx;
|
let samp_x = (filter_x + x as f32) * cmpx;
|
||||||
let samp_y = (filter_y + y as f32) * cmpy;
|
let samp_y = (filter_y + y as f32) * cmpy;
|
||||||
((samp_x - 0.5) * x_extent, (0.5 - samp_y) * y_extent)
|
((samp_x - 0.5) * x_extent, (0.5 - samp_y) * y_extent)
|
||||||
|
@ -262,8 +262,8 @@ impl<'a> Renderer<'a> {
|
||||||
get_sample(2, si as u32, (x, y), self.seed),
|
get_sample(2, si as u32, (x, y), self.seed),
|
||||||
get_sample(3, si as u32, (x, y), self.seed),
|
get_sample(3, si as u32, (x, y), self.seed),
|
||||||
),
|
),
|
||||||
get_sample(4, si as u32, (x, y), self.seed),
|
get_sample(1, si as u32, (x, y), self.seed),
|
||||||
map_0_1_to_wavelength(get_sample(5, si as u32, (x, y), self.seed)),
|
map_0_1_to_wavelength(get_sample(0, si as u32, (x, y), self.seed)),
|
||||||
si as u32,
|
si as u32,
|
||||||
);
|
);
|
||||||
paths.push(path);
|
paths.push(path);
|
||||||
|
@ -693,14 +693,29 @@ impl LightPath {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_sample(dimension: u32, i: u32, pixel_co: (u32, u32), seed: u32) -> f32 {
|
fn get_sample(dimension: u32, i: u32, pixel_co: (u32, u32), seed: u32) -> f32 {
|
||||||
let pixel_id = pixel_co.0 ^ (pixel_co.1 << 16);
|
let pixel_id = pixel_co.0 ^ (pixel_co.1 << 16);
|
||||||
if dimension < sobol::NUM_DIMENSIONS as u32 {
|
match dimension {
|
||||||
|
0 => {
|
||||||
|
// Golden ratio sampling.
|
||||||
|
// NOTE: use this for the wavelength dimension, because
|
||||||
|
// due to the nature of hero wavelength sampling this ends up
|
||||||
|
// being crazily more efficient than pretty much any other sampler,
|
||||||
|
// and reduces variance by a huge amount.
|
||||||
let scramble = hash_u32(pixel_id, seed);
|
let scramble = hash_u32(pixel_id, seed);
|
||||||
sobol::sample_owen_scramble(dimension, i, hash_u32(dimension, scramble))
|
let n = (i + scramble) * 2654435769;
|
||||||
} else {
|
n as f32 * (1.0 / (1u64 << 32) as f32)
|
||||||
|
}
|
||||||
|
n if (n - 1) < sobol::NUM_DIMENSIONS as u32 => {
|
||||||
|
// Sobol sampling.
|
||||||
|
let scramble = hash_u32(pixel_id, seed);
|
||||||
|
sobol::sample_owen_scramble(dimension - 1, i, hash_u32(dimension, scramble))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Random sampling.
|
||||||
use crate::hash::hash_u32_to_f32;
|
use crate::hash::hash_u32_to_f32;
|
||||||
hash_u32_to_f32(dimension ^ (i << 16), pixel_id)
|
hash_u32_to_f32(dimension ^ (i << 16), pixel_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct BucketJob {
|
struct BucketJob {
|
||||||
|
|
|
@ -78,13 +78,13 @@ pub fn sample_owen_scramble(dimension: u32, index: u32, scramble: u32) -> f32 {
|
||||||
// scrambling is a strict subset of Owen scrambling, and therefore does
|
// scrambling is a strict subset of Owen scrambling, and therefore does
|
||||||
// not invalidate the Owen scrambling itself.
|
// not invalidate the Owen scrambling itself.
|
||||||
//
|
//
|
||||||
// The constants here are large primes, selected semi-carefully to maximize
|
// The constants here were selected through an optimization process
|
||||||
// avalanche between bits.
|
// to maximize unidirectional low-bias avalanche between bits.
|
||||||
n = n.reverse_bits();
|
n = n.reverse_bits();
|
||||||
n ^= scramble; // Apply the scramble parameter.
|
n ^= scramble; // Apply the scramble parameter.
|
||||||
n ^= n.wrapping_mul(0x2d2c6e5d << 1);
|
n ^= n.wrapping_mul(0x08afbbe0);
|
||||||
n ^= n.wrapping_mul(0x52d391b3 << 1);
|
n ^= n.wrapping_mul(0xa7389b46);
|
||||||
n ^= n.wrapping_mul(0x736caf6f << 1);
|
n ^= n.wrapping_mul(0x42bf6dbc);
|
||||||
n = n.reverse_bits();
|
n = n.reverse_bits();
|
||||||
|
|
||||||
u32_to_0_1_f32(n)
|
u32_to_0_1_f32(n)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user