Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support decoupled shading from reservoirs #42

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 63 additions & 26 deletions blade-render/code/ray-trace.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,16 @@

const PI: f32 = 3.1415926;
const MAX_RESERVOIRS: u32 = 2u;
// See "9.1 pairwise mis for robust reservoir reuse"
// See "9.1 pairwise mis for robust reservoir reuse" in
// "Correlations and Reuse for Fast and Accurate Physically Based Light Transport"
const PAIRWISE_MIS: bool = true;
// Base MIS for canonical samples. The constant isolates a critical difference between
// Bitterli's pseudocode (where it's 1) and NVidia's RTXDI implementation (where it's 0).
// With Bitterli's 1 we have MIS not respecting the prior history enough.
const BASE_CANONICAL_MIS: f32 = 0.05;
// See "DECOUPLING SHADING AND REUSE" in
// "Rearchitecting Spatiotemporal Resampling for Production"
const DECOUPLED_SHADING: bool = false;

struct MainParams {
frame_index: u32,
Expand Down Expand Up @@ -56,6 +59,7 @@ struct LiveReservoir {
selected_target_score: f32,
weight_sum: f32,
history: f32,
shaded_color: vec3<f32>
}

fn compute_target_score(radiance: vec3<f32>) -> f32 {
Expand All @@ -79,27 +83,67 @@ fn get_pixel_from_reservoir_index(index: i32, camera: CameraParams) -> vec2<i32>
fn bump_reservoir(r: ptr<function, LiveReservoir>, history: f32) {
(*r).history += history;
}
fn make_reservoir(ls: LightSample, light_index: u32, brdf: vec3<f32>) -> LiveReservoir {

fn make_reservoir_from_light_sample(ls: LightSample, light_index: u32, brdf: vec3<f32>) -> LiveReservoir {
let shaded = ls.radiance * brdf;
let target_score = compute_target_score(shaded);
var r: LiveReservoir;
r.selected_uv = ls.uv;
r.selected_light_index = light_index;
r.selected_target_score = compute_target_score(ls.radiance * brdf);
r.weight_sum = r.selected_target_score / ls.pdf;
r.selected_target_score = target_score;
r.weight_sum = target_score / ls.pdf;
r.history = 1.0;
r.shaded_color = shaded;
return r;
}

fn merge_light_sample(r: ptr<function, LiveReservoir>, ls: LightSample, light_index: u32, brdf: vec3<f32>, random: f32) -> bool {
let shaded = ls.radiance * brdf;
let target_score = compute_target_score(shaded);
if (target_score <= 0.0) {
return false;
}

let weight = target_score / ls.pdf;
(*r).weight_sum += weight;
(*r).history += 1.0;

if ((*r).weight_sum * random < weight) {
(*r).selected_light_index = light_index;
(*r).selected_uv = ls.uv;
(*r).selected_target_score = target_score;
(*r).shaded_color = shaded;
return true;
} else {
return false;
}
}

fn merge_reservoir(r: ptr<function, LiveReservoir>, other: LiveReservoir, random: f32) -> bool {
(*r).weight_sum += other.weight_sum;
(*r).history += other.history;
if ((*r).weight_sum * random < other.weight_sum) {
if (other.selected_target_score <= 0.0) {
return false;
}

let ratio = other.weight_sum / (*r).weight_sum;
if (DECOUPLED_SHADING) {
(*r).shaded_color = mix((*r).shaded_color, other.shaded_color, ratio);
}

if (random < ratio) {
(*r).selected_light_index = other.selected_light_index;
(*r).selected_uv = other.selected_uv;
(*r).selected_target_score = other.selected_target_score;
if (!DECOUPLED_SHADING) {
(*r).shaded_color = other.shaded_color;
}
return true;
} else {
return false;
}
}

fn unpack_reservoir(f: StoredReservoir, max_history: u32) -> LiveReservoir {
var r: LiveReservoir;
r.selected_light_index = f.light_index;
Expand All @@ -108,6 +152,7 @@ fn unpack_reservoir(f: StoredReservoir, max_history: u32) -> LiveReservoir {
let history = min(f.confidence, f32(max_history));
r.weight_sum = f.contribution_weight * f.target_score * history;
r.history = history;
r.shaded_color = vec3<f32>(0.0); // needs to be updated
return r;
}
fn pack_reservoir_detail(r: LiveReservoir, denom_factor: f32) -> StoredReservoir {
Expand Down Expand Up @@ -261,11 +306,10 @@ fn estimate_target_pdf_with_occlusion(surface: Surface, position: vec3<f32>, lig

if (check_ray_occluded(position, direction, debug_len)) {
return TargetPdf();
} else {
//Note: same as `evaluate_reflected_light`
let radiance = textureSampleLevel(env_map, sampler_nearest, light_uv, 0.0).xyz;
return make_target_pdf(brdf * radiance);
}
//Note: same as `evaluate_reflected_light`
let radiance = textureSampleLevel(env_map, sampler_nearest, light_uv, 0.0).xyz;
return make_target_pdf(brdf * radiance);
}

fn evaluate_sample(ls: LightSample, surface: Surface, start_pos: vec3<f32>, debug_len: f32) -> f32 {
Expand Down Expand Up @@ -331,7 +375,6 @@ fn compute_restir(surface: Surface, pixel: vec2<i32>, rng: ptr<function, RandomS
}

var canonical = LiveReservoir();
var canonical_radiance = vec3<f32>(0.0);
for (var i = 0u; i < parameters.num_environment_samples; i += 1u) {
var ls: LightSample;
if (parameters.environment_importance_sampling != 0u) {
Expand All @@ -342,10 +385,7 @@ fn compute_restir(surface: Surface, pixel: vec2<i32>, rng: ptr<function, RandomS

let brdf = evaluate_sample(ls, surface, position, debug_len);
if (brdf > 0.0) {
let other = make_reservoir(ls, 0u, vec3<f32>(brdf));
if (merge_reservoir(&canonical, other, random_gen(rng))) {
canonical_radiance = ls.radiance * brdf;
}
merge_light_sample(&canonical, ls, 0u, vec3<f32>(brdf), random_gen(rng));
} else {
bump_reservoir(&canonical, 1.0);
}
Expand Down Expand Up @@ -394,15 +434,14 @@ fn compute_restir(surface: Surface, pixel: vec2<i32>, rng: ptr<function, RandomS

// Next, evaluate the MIS of each of the samples versus the canonical one.
var reservoir = LiveReservoir();
var shaded_color = vec3<f32>(0.0);
var mis_canonical = BASE_CANONICAL_MIS;
for (var rid = 0u; rid < accepted_count; rid += 1u) {
let neighbor_index = accepted_reservoir_indices[rid];
let neighbor = prev_reservoirs[neighbor_index];

let max_history = select(parameters.spatial_tap_history, parameters.temporal_history, rid == temporal_index);
var other: LiveReservoir;
var other_color: vec3<f32>;
var other_history = other.history; //TODO
if (PAIRWISE_MIS) {
let neighbor_pixel = get_pixel_from_reservoir_index(neighbor_index, prev_camera);
let neighbor_history = min(neighbor.confidence, f32(max_history));
Expand Down Expand Up @@ -435,34 +474,32 @@ fn compute_restir(surface: Surface, pixel: vec2<i32>, rng: ptr<function, RandomS
other.selected_uv = neighbor.light_uv;
other.selected_target_score = t_neighbor_at_canonical.score;
other.weight_sum = t_neighbor_at_canonical.score * neighbor.contribution_weight * mis_neighbor.weight;
//Note: should be needed according to the paper
// other.history *= min(mis_neighbor.history, mis_sub_canonical.history);
other_color = t_neighbor_at_canonical.color;
other.shaded_color = t_neighbor_at_canonical.color;
//Note: the paper modifies
//other_history = min(mis_neighbor.history, mis_sub_canonical.history);
} else {
other = unpack_reservoir(neighbor, max_history);
other_color = evaluate_reflected_light(surface, other.selected_light_index, other.selected_uv);
other.shaded_color = evaluate_reflected_light(surface, other.selected_light_index, other.selected_uv);
}

if (other.weight_sum <= 0.0) {
bump_reservoir(&reservoir, other.history);
} else if (merge_reservoir(&reservoir, other, random_gen(rng))) {
shaded_color = other_color;
} else {
merge_reservoir(&reservoir, other, random_gen(rng));
}
}

// Finally, merge in the canonical sample
if (PAIRWISE_MIS) {
canonical.weight_sum *= mis_canonical / canonical.history;
}
if (merge_reservoir(&reservoir, canonical, random_gen(rng))) {
shaded_color = canonical_radiance;
}
merge_reservoir(&reservoir, canonical, random_gen(rng));

let effective_history = select(reservoir.history, BASE_CANONICAL_MIS + f32(accepted_count), PAIRWISE_MIS);
let stored = pack_reservoir_detail(reservoir, effective_history);
reservoirs[pixel_index] = stored;
var ro = RestirOutput();
ro.radiance = stored.contribution_weight * shaded_color;
ro.radiance = stored.contribution_weight * reservoir.shaded_color;
return ro;
}

Expand Down
Loading