Skip to content

Solari: Dynamic realtime global illumination #10000

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

Closed
wants to merge 104 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
e7a8cda
Switch to solari deps
JMS55 Sep 4, 2023
788981e
Port SolariScenePlugin
JMS55 Sep 4, 2023
55223e9
WIP port SolariGolobalIlluminationPlugin
JMS55 Sep 5, 2023
b996539
Add BufferCache
JMS55 Sep 5, 2023
c16e638
Finish host code
JMS55 Sep 5, 2023
9c7f5f7
Copy old shaders, misc fixes
JMS55 Sep 5, 2023
5dad197
Add back example
JMS55 Sep 5, 2023
5cc1513
Re-export model
JMS55 Sep 5, 2023
a49f223
Regenerate readme
JMS55 Sep 5, 2023
d045210
Change error message
JMS55 Sep 5, 2023
cb8f4a8
Add missing components to example
JMS55 Sep 5, 2023
cdf0271
TODO: Handle vertex attributes better
JMS55 Sep 5, 2023
12f7bb4
Revert "TODO: Handle vertex attributes better"
JMS55 Sep 6, 2023
8630b7b
Extract SolariEnabled
JMS55 Sep 6, 2023
e730e8f
Fix material handling
JMS55 Sep 6, 2023
7735a8b
More fixes
JMS55 Sep 6, 2023
b990581
WIP
JMS55 Sep 7, 2023
be23d62
Fix shaders more
JMS55 Sep 7, 2023
0ac122e
More shader fixes
JMS55 Sep 7, 2023
5807567
More shader porting
JMS55 Sep 7, 2023
01b3789
More shader fixes
JMS55 Sep 7, 2023
eb17dbb
Fix crash when solari not enabled
JMS55 Sep 7, 2023
5c10a3d
Actually fix extract system
JMS55 Sep 7, 2023
0336a00
More shader fixes
JMS55 Sep 8, 2023
7eec0d9
Fixes
JMS55 Sep 8, 2023
94c5fc8
Fix depth_to_world_position
JMS55 Sep 9, 2023
079306d
Add GI texture to main pass
JMS55 Sep 9, 2023
4c7d240
Fixes
JMS55 Sep 9, 2023
4dc8474
Apply GI to main pass
JMS55 Sep 9, 2023
f9c06ce
Misc cleanup
JMS55 Sep 9, 2023
e922bac
Fix noise
JMS55 Sep 10, 2023
79c4d6b
Fix NaNs
JMS55 Sep 10, 2023
1d8d769
Simplify shaders
JMS55 Sep 10, 2023
c3b77ef
Fix shaders
JMS55 Sep 10, 2023
856d00f
Fix sunlight
JMS55 Sep 10, 2023
8ac404a
Misc
JMS55 Sep 10, 2023
4960e5b
Fix emissive mesh detection
JMS55 Sep 10, 2023
2abda23
Misc
JMS55 Sep 12, 2023
171a0ea
WIP cascades
JMS55 Sep 12, 2023
e2ba98b
Two pass screen probe filtering with a wider kernel
JMS55 Sep 13, 2023
59014a8
Merge commit 'e2ba98bc162205127375c935ec799022e736be09' into solari2-…
JMS55 Sep 13, 2023
79831ea
Add comments
JMS55 Sep 13, 2023
2768bf6
Misc comment
JMS55 Sep 13, 2023
0976349
Adjust radiance interval calculations
JMS55 Sep 13, 2023
d33eb29
Misc
JMS55 Sep 13, 2023
7842376
Misc comment
JMS55 Sep 13, 2023
5e164f1
Misc comment
JMS55 Sep 13, 2023
455a524
Normal buffer fixes
JMS55 Sep 14, 2023
20da078
Use new TAA
JMS55 Sep 14, 2023
4c29e87
Merge commit '20da078b85be067c2672b7fd04e1c6242c750ea2' into solari2-…
JMS55 Sep 14, 2023
445b873
Misc comment fix
JMS55 Sep 14, 2023
3ad101d
Fixes
JMS55 Sep 14, 2023
24cf315
Merge branch 'solari2-cascades' of https://github.com/JMS55/bevy into…
JMS55 Sep 14, 2023
421e33e
WIP merge cascades
JMS55 Sep 15, 2023
af98b75
More WIP cascade merging
JMS55 Sep 15, 2023
4e0bf07
Revert incorrect change
JMS55 Sep 16, 2023
cc1e060
Fix cascade merge
JMS55 Sep 16, 2023
718d3c4
More merge fixes
JMS55 Sep 16, 2023
a048048
TODO
JMS55 Sep 16, 2023
c3c49fe
Add todo
JMS55 Sep 16, 2023
50eb303
Remove a todo
JMS55 Sep 16, 2023
ace6996
Finish cascade merge
JMS55 Sep 16, 2023
8c0c398
Improve screen_probes_filter
JMS55 Sep 16, 2023
e291af3
Add emissive bounce to screen probe traces
JMS55 Sep 16, 2023
70969aa
Add emissive bounce, last commit had the wrong message
JMS55 Sep 16, 2023
ed9f16b
Revert emissive trace
JMS55 Sep 16, 2023
88183cb
Apply plane distance weight to interpolation (idk if this really help…
JMS55 Sep 16, 2023
0210cb6
Adjust plane distance cutoff
JMS55 Sep 16, 2023
5d88829
Misc fix
JMS55 Sep 16, 2023
acf204c
Fix bug
JMS55 Sep 17, 2023
d5feb38
Fix radiance intervals
JMS55 Sep 17, 2023
053806a
Misc fix
JMS55 Sep 17, 2023
10b6b81
Many SH-related fixes
JMS55 Sep 18, 2023
39f906e
Add TODO
JMS55 Sep 18, 2023
c367c1d
Misc format
JMS55 Sep 18, 2023
27c5479
TODOs
JMS55 Sep 18, 2023
7ba8249
Fix get_spherical_harmonics_coefficents
JMS55 Sep 18, 2023
f1ab690
Count emissives in screen probe traces
JMS55 Sep 18, 2023
561bb5f
Misc rename
JMS55 Sep 18, 2023
32076f1
WIP reproject cascades
JMS55 Sep 19, 2023
af838be
TODO
JMS55 Sep 19, 2023
f63ef06
WIP: Combined reprojection + trace shaders
JMS55 Sep 20, 2023
edeaece
WIP combined reprojection and tracing
JMS55 Sep 21, 2023
460023d
Bug fix
JMS55 Sep 21, 2023
47feee3
Add history confidence
JMS55 Sep 21, 2023
627339d
WIP fixes
JMS55 Sep 21, 2023
283ac0a
WIP fixes
JMS55 Sep 24, 2023
e72e4bd
Misc rename
JMS55 Sep 24, 2023
dc28c77
Fix probe count calculations
JMS55 Sep 24, 2023
8a47a6c
Cleanup
JMS55 Sep 24, 2023
b8f0422
Remove AmbientLight from example
JMS55 Sep 24, 2023
58fb285
Add todo
JMS55 Sep 24, 2023
e018c58
Fix taa weights
JMS55 Sep 24, 2023
517ed2c
Use STBN
JMS55 Sep 24, 2023
d9bce2e
Misc
JMS55 Sep 26, 2023
c0df663
Fixes
JMS55 Sep 27, 2023
37e12d0
Todo cleanup
JMS55 Sep 27, 2023
f8d1a19
Add perf todo
JMS55 Sep 27, 2023
7f94c11
Require push constants
JMS55 Sep 28, 2023
8996e07
Fixes
JMS55 Sep 28, 2023
15a683c
WIP better weights
JMS55 Sep 28, 2023
a29d962
Add GI toggle to example
JMS55 Sep 28, 2023
645880d
Merge commit 'a9622408665662eff9ddae83d913c6cfa2fa61d2' into solari3
JMS55 Oct 6, 2023
1268a3f
Hack to get example to load
JMS55 Oct 6, 2023
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
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,17 @@ description = "Illustrates spot lights"
category = "3D Rendering"
wasm = true

[[example]]
name = "solari"
path = "examples/3d/solari.rs"
doc-scrape-examples = true

[package.metadata.example.solari]
name = "Solari"
description = "Solari realtime dynamic global illumination"
category = "3D Rendering"
wasm = false

[[example]]
name = "bloom_3d"
path = "examples/3d/bloom_3d.rs"
Expand Down
Binary file added assets/models/cornell_box.glb
Binary file not shown.
7 changes: 4 additions & 3 deletions crates/bevy_core_pipeline/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ impl ViewNode for BloomNode {
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
..Default::default()
});
downsampling_first_pass.set_render_pipeline(downsampling_first_pipeline);
downsampling_first_pass.set_bind_group(
Expand All @@ -226,7 +226,7 @@ impl ViewNode for BloomNode {
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
..Default::default()
});
downsampling_pass.set_render_pipeline(downsampling_pipeline);
downsampling_pass.set_bind_group(
Expand All @@ -251,7 +251,7 @@ impl ViewNode for BloomNode {
store: true,
},
})],
depth_stencil_attachment: None,
..Default::default()
});
upsampling_pass.set_render_pipeline(upsampling_pipeline);
upsampling_pass.set_bind_group(
Expand Down Expand Up @@ -282,6 +282,7 @@ impl ViewNode for BloomNode {
},
))],
depth_stencil_attachment: None,
..Default::default()
});
upsampling_final_pass.set_render_pipeline(upsampling_final_pipeline);
upsampling_final_pass.set_bind_group(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ impl Node for CASNode {
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
..Default::default()
};

let mut render_pass = render_context
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl Node for MainPass2dNode {
},
store: true,
}))],
depth_stencil_attachment: None,
..Default::default()
});

if let Some(viewport) = camera.viewport.as_ref() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ impl ViewNode for MainOpaquePass3dNode {
}),
stencil_ops: None,
}),
..Default::default()
});

if let Some(viewport) = camera.viewport.as_ref() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ impl ViewNode for MainTransparentPass3dNode {
}),
stencil_ops: None,
}),
..Default::default()
});

if let Some(viewport) = camera.viewport.as_ref() {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/src/fxaa/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ impl ViewNode for FxaaNode {
resolve_target: None,
ops: Operations::default(),
})],
depth_stencil_attachment: None,
..Default::default()
};

let mut render_pass = render_context
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/src/msaa_writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl Node for MsaaWritebackNode {
load: LoadOp::Clear(Default::default()),
store: true,
}))],
depth_stencil_attachment: None,
..Default::default()
};

let bind_group =
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/src/prepass/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ impl ViewNode for PrepassNode {
}),
stencil_ops: None,
}),
..Default::default()
});
if let Some(viewport) = camera.viewport.as_ref() {
render_pass.set_camera_viewport(viewport);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/src/taa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ impl ViewNode for TAANode {
ops: Operations::default(),
}),
],
depth_stencil_attachment: None,
..Default::default()
});
taa_pass.set_render_pipeline(taa_pipeline);
taa_pass.set_bind_group(0, &taa_bind_group, &[]);
Expand Down
132 changes: 49 additions & 83 deletions crates/bevy_core_pipeline/src/taa/taa.wgsl
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
// References:
// https://www.elopezr.com/temporal-aa-and-the-quest-for-the-holy-trail
// https://alextardif.com/TAA.html
// http://behindthepixels.io/assets/files/TemporalAA.pdf
// http://leiy.cc/publications/TAA/TAA_EG2020_Talk.pdf
// https://advances.realtimerendering.com/s2014/index.html#_HIGH-QUALITY_TEMPORAL_SUPERSAMPLING

// Controls how much to blend between the current and past samples
// Lower numbers = less of the current sample and more of the past sample = more smoothing
// Values chosen empirically
const DEFAULT_HISTORY_BLEND_RATE: f32 = 0.1; // Default blend rate to use when no confidence in history
const MIN_HISTORY_BLEND_RATE: f32 = 0.015; // Minimum blend rate allowed, to ensure at least some of the current sample is used

#import bevy_core_pipeline::fullscreen_vertex_shader

@group(0) @binding(0) var view_target: texture_2d<f32>;
@group(0) @binding(1) var history: texture_2d<f32>;
@group(0) @binding(2) var motion_vectors: texture_2d<f32>;
Expand All @@ -24,7 +17,7 @@ struct Output {
@location(1) history: vec4<f32>,
};

// TAA is ideally applied after tonemapping, but before post processing
// TAA is ideally applied after tonemapping (if not tonemapping in the main pass), but before post processing
// Post processing wants to go before tonemapping, which conflicts
// Solution: Put TAA before tonemapping, tonemap TAA input, apply TAA, invert-tonemap TAA output
// https://advances.realtimerendering.com/s2014/index.html#_HIGH-QUALITY_TEMPORAL_SUPERSAMPLING, slide 20
Expand Down Expand Up @@ -65,11 +58,11 @@ fn clip_towards_aabb_center(history_color: vec3<f32>, current_color: vec3<f32>,
}

fn sample_history(u: f32, v: f32) -> vec3<f32> {
return textureSample(history, linear_sampler, vec2(u, v)).rgb;
return textureSampleLevel(history, linear_sampler, vec2(u, v), 0.0).rgb;
}

fn sample_view_target(uv: vec2<f32>) -> vec3<f32> {
var sample = textureSample(view_target, nearest_sampler, uv).rgb;
var sample = textureSampleLevel(view_target, nearest_sampler, uv, 0.0).rgb;
#ifdef TONEMAP
sample = tonemap(sample);
#endif
Expand All @@ -81,50 +74,45 @@ fn taa(@location(0) uv: vec2<f32>) -> Output {
let texture_size = vec2<f32>(textureDimensions(view_target));
let texel_size = 1.0 / texture_size;

// Fetch the current sample
let original_color = textureSample(view_target, nearest_sampler, uv);
var current_color = original_color.rgb;
#ifdef TONEMAP
current_color = tonemap(current_color);
#endif

#ifndef RESET
// Pick the closest motion_vector from 5 samples (reduces aliasing on the edges of moving entities)
// https://advances.realtimerendering.com/s2014/index.html#_HIGH-QUALITY_TEMPORAL_SUPERSAMPLING, slide 27
let offset = texel_size * 2.0;
let d_uv_tl = uv + vec2(-offset.x, offset.y);
let d_uv_tr = uv + vec2(offset.x, offset.y);
let d_uv_bl = uv + vec2(-offset.x, -offset.y);
let d_uv_br = uv + vec2(offset.x, -offset.y);
// Loop over 3x3 neighborhood of the pre-TAA rendered texture
// https://alextardif.com/TAA.html
var current_color = vec3(0.0);
var weight_sum = 0.0;
var moment_1 = vec3(0.0);
var moment_2 = vec3(0.0);
var closest_depth = 0.0;
var closest_uv = uv;
let d_tl = textureSample(depth, nearest_sampler, d_uv_tl);
let d_tr = textureSample(depth, nearest_sampler, d_uv_tr);
var closest_depth = textureSample(depth, nearest_sampler, uv);
let d_bl = textureSample(depth, nearest_sampler, d_uv_bl);
let d_br = textureSample(depth, nearest_sampler, d_uv_br);
if d_tl > closest_depth {
closest_uv = d_uv_tl;
closest_depth = d_tl;
}
if d_tr > closest_depth {
closest_uv = d_uv_tr;
closest_depth = d_tr;
}
if d_bl > closest_depth {
closest_uv = d_uv_bl;
closest_depth = d_bl;
}
if d_br > closest_depth {
closest_uv = d_uv_br;
var weights = array(0.05556, 0.88889, 0.05556);
for (var x = -1.0; x <= 1.0; x += 1.0) {
for (var y = -1.0; y <= 1.0; y += 1.0) {
let sample_uv = uv + (vec2(x, y) * texel_size);
let sample = sample_view_target(sample_uv);

// Apply Mitchell-Netravali kernel over the jittered 3x3 neighborhood to reduce softness
let weight = weights[u32(x + 1.0)] * weights[u32(y + 1.0)];
weight_sum += weight;
current_color += sample * weight;

// Calculate first and second color moments for use with variance clipping
moment_1 += sample;
moment_2 += sample * sample;

// Find closest pixel to take motion vectors from (reduces aliasing on the edges of moving entities)
let sample_depth = textureSampleLevel(depth, nearest_sampler, sample_uv, 0.0);
if sample_depth > closest_depth {
closest_depth = sample_depth;
closest_uv = sample_uv;
}
}
}
let closest_motion_vector = textureSample(motion_vectors, nearest_sampler, closest_uv).rg;
current_color /= weight_sum;

// Reproject to find the equivalent sample from the past
// Uses 5-sample Catmull-Rom filtering (reduces blurriness)
// Catmull-Rom filtering: https://gist.github.com/TheRealMJP/c83b8c0f46b63f3a88a5986f4fa982b1
// Ignoring corners: https://www.activision.com/cdn/research/Dynamic_Temporal_Antialiasing_and_Upsampling_in_Call_of_Duty_v4.pdf#page=68
// Technically we should renormalize the weights since we're skipping the corners, but it's basically the same result
let history_uv = uv - closest_motion_vector;
let history_uv = uv - textureSampleLevel(motion_vectors, nearest_sampler, closest_uv, 0.0).rg;
let sample_position = history_uv * texture_size;
let texel_center = floor(sample_position - 0.5) + 0.5;
let f = sample_position - texel_center;
Expand All @@ -145,52 +133,30 @@ fn taa(@location(0) uv: vec2<f32>) -> Output {
// Constrain past sample with 3x3 YCoCg variance clipping (reduces ghosting)
// YCoCg: https://advances.realtimerendering.com/s2014/index.html#_HIGH-QUALITY_TEMPORAL_SUPERSAMPLING, slide 33
// Variance clipping: https://developer.download.nvidia.com/gameworks/events/GDC2016/msalvi_temporal_supersampling.pdf
let s_tl = sample_view_target(uv + vec2(-texel_size.x, texel_size.y));
let s_tm = sample_view_target(uv + vec2( 0.0, texel_size.y));
let s_tr = sample_view_target(uv + vec2( texel_size.x, texel_size.y));
let s_ml = sample_view_target(uv + vec2(-texel_size.x, 0.0));
let s_mm = RGB_to_YCoCg(current_color);
let s_mr = sample_view_target(uv + vec2( texel_size.x, 0.0));
let s_bl = sample_view_target(uv + vec2(-texel_size.x, -texel_size.y));
let s_bm = sample_view_target(uv + vec2( 0.0, -texel_size.y));
let s_br = sample_view_target(uv + vec2( texel_size.x, -texel_size.y));
let moment_1 = s_tl + s_tm + s_tr + s_ml + s_mm + s_mr + s_bl + s_bm + s_br;
let moment_2 = (s_tl * s_tl) + (s_tm * s_tm) + (s_tr * s_tr) + (s_ml * s_ml) + (s_mm * s_mm) + (s_mr * s_mr) + (s_bl * s_bl) + (s_bm * s_bm) + (s_br * s_br);
let mean = moment_1 / 9.0;
let variance = (moment_2 / 9.0) - (mean * mean);
let std_deviation = sqrt(max(variance, vec3(0.0)));
history_color = RGB_to_YCoCg(history_color);
history_color = clip_towards_aabb_center(history_color, s_mm, mean - std_deviation, mean + std_deviation);
history_color = YCoCg_to_RGB(history_color);

// How confident we are that the history is representative of the current frame
var history_confidence = textureSample(history, nearest_sampler, uv).a;
let pixel_motion_vector = abs(closest_motion_vector) * texture_size;
if pixel_motion_vector.x < 0.01 && pixel_motion_vector.y < 0.01 {
// Increment when pixels are not moving
history_confidence += 10.0;
} else {
// Else reset
history_confidence = 1.0;
}
history_color = clip_towards_aabb_center(history_color, current_color, mean - std_deviation, mean + std_deviation);

// Blend current and past sample
// Use more of the history if we're confident in it (reduces noise when there is no motion)
// https://hhoppe.com/supersample.pdf, section 4.1
let current_color_factor = clamp(1.0 / history_confidence, MIN_HISTORY_BLEND_RATE, DEFAULT_HISTORY_BLEND_RATE);
current_color = mix(history_color, current_color, current_color_factor);
#endif // #ifndef RESET
// Use more of the history if it's been visible for a few frames (reduces noise)
var accumulated_samples = textureSampleLevel(history, nearest_sampler, history_uv, 0.0).a;
// If the history_uv is pointing off-screen, reset accumulated sample count
accumulated_samples *= f32(all(saturate(history_uv) == history_uv));
#ifdef RESET
accumulated_samples = 0.0;
#endif
accumulated_samples = max(accumulated_samples + 1.0, 8.0);

// Blend current and past sample
current_color = mix(history_color, current_color, 1.0 / accumulated_samples);

// Write output to history and view target
var out: Output;
#ifdef RESET
let history_confidence = 1.0 / MIN_HISTORY_BLEND_RATE;
#endif
out.history = vec4(current_color, history_confidence);
out.history = vec4(current_color, accumulated_samples);
current_color = YCoCg_to_RGB(current_color);
#ifdef TONEMAP
current_color = reverse_tonemap(current_color);
#endif
out.view_target = vec4(current_color, original_color.a);
out.view_target = vec4(current_color, 1.0);
return out;
}
2 changes: 1 addition & 1 deletion crates/bevy_core_pipeline/src/tonemapping/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ impl ViewNode for TonemappingNode {
store: true,
},
})],
depth_stencil_attachment: None,
..Default::default()
};

let mut render_pass = render_context
Expand Down
Loading