1
+ // https://intro-to-restir.cwyman.org/presentations/2023ReSTIR_Course_Notes.pdf
2
+
1
3
#import bevy_core_pipeline ::tonemapping ::tonemapping_luminance as luminance
2
4
#import bevy_pbr ::pbr_deferred_types ::unpack_24bit_normal
3
5
#import bevy_pbr ::prepass_bindings ::PreviousViewUniforms
4
6
#import bevy_pbr ::rgb9e5 ::rgb9e5_to_vec3_
5
7
#import bevy_pbr ::utils ::{rand_f , octahedral_decode }
6
8
#import bevy_render ::maths ::PI
7
9
#import bevy_render ::view ::View
8
- #import bevy_solari ::reservoir ::{Reservoir , empty_reservoir , reservoir_valid , merge_reservoirs }
9
- #import bevy_solari ::sampling ::{generate_random_light_sample , calculate_light_contribution , trace_light_visibility , sample_disk }
10
+ #import bevy_solari ::sampling ::{LightSample , generate_random_light_sample , calculate_light_contribution , trace_light_visibility , sample_disk }
10
11
#import bevy_solari ::scene_bindings ::{previous_frame_light_id_translations , LIGHT_NOT_PRESENT_THIS_FRAME}
11
12
12
13
@group (1 ) @binding (0 ) var view_output : texture_storage_2d <rgba16float , write >;
@@ -24,7 +25,9 @@ var<push_constant> constants: PushConstants;
24
25
25
26
const INITIAL_SAMPLES = 32u ;
26
27
const SPATIAL_REUSE_RADIUS_PIXELS = 30.0 ;
27
- const CONFIDENCE_WEIGHT_CAP = 20.0 * f32 (INITIAL_SAMPLES);
28
+ const CONFIDENCE_WEIGHT_CAP = 20.0 ;
29
+
30
+ const NULL_RESERVOIR_SAMPLE = 0xFFFFFFFFu ;
28
31
29
32
@compute @workgroup_size (8 , 8 , 1 )
30
33
fn initial_and_temporal (@builtin (global_invocation_id ) global_id : vec3 <u32 >) {
@@ -112,7 +115,7 @@ fn generate_initial_reservoir(world_position: vec3<f32>, world_normal: vec3<f32>
112
115
reservoir . unbiased_contribution_weight *= reservoir . visibility;
113
116
}
114
117
115
- reservoir . confidence_weight = f32 (INITIAL_SAMPLES) ;
118
+ reservoir . confidence_weight = 1.0 ;
116
119
return reservoir ;
117
120
}
118
121
@@ -205,3 +208,82 @@ fn depth_ndc_to_view_z(ndc_depth: f32) -> f32 {
205
208
return view_pos . z / view_pos . w;
206
209
#endif
207
210
}
211
+
212
+ // Don't adjust the size of this struct without also adjusting RESERVOIR_STRUCT_SIZE.
213
+ struct Reservoir {
214
+ sample : LightSample ,
215
+ weight_sum : f32 ,
216
+ confidence_weight : f32 ,
217
+ unbiased_contribution_weight : f32 ,
218
+ visibility : f32 ,
219
+ }
220
+
221
+ fn empty_reservoir () -> Reservoir {
222
+ return Reservoir (
223
+ LightSample (vec2 (NULL_RESERVOIR_SAMPLE, 0u ), vec2 (0.0 )),
224
+ 0.0 ,
225
+ 0.0 ,
226
+ 0.0 ,
227
+ 0.0
228
+ );
229
+ }
230
+
231
+ fn reservoir_valid (reservoir : Reservoir ) -> bool {
232
+ return reservoir . sample. light_id. x != NULL_RESERVOIR_SAMPLE;
233
+ }
234
+
235
+ struct ReservoirMergeResult {
236
+ merged_reservoir : Reservoir ,
237
+ selected_sample_radiance : vec3 <f32 >,
238
+ }
239
+
240
+ fn merge_reservoirs (
241
+ canonical_reservoir : Reservoir ,
242
+ other_reservoir : Reservoir ,
243
+ world_position : vec3 <f32 >,
244
+ world_normal : vec3 <f32 >,
245
+ diffuse_brdf : vec3 <f32 >,
246
+ rng : ptr <function , u32 >,
247
+ ) -> ReservoirMergeResult {
248
+ // TODO: Balance heuristic MIS weights
249
+ let mis_weight_denominator = 1.0 / (canonical_reservoir . confidence_weight + other_reservoir . confidence_weight);
250
+
251
+ let canonical_mis_weight = canonical_reservoir . confidence_weight * mis_weight_denominator ;
252
+ let canonical_target_function = reservoir_target_function (canonical_reservoir , world_position , world_normal , diffuse_brdf );
253
+ let canonical_resampling_weight = canonical_mis_weight * (canonical_target_function . a * canonical_reservoir . unbiased_contribution_weight);
254
+
255
+ let other_mis_weight = other_reservoir . confidence_weight * mis_weight_denominator ;
256
+ let other_target_function = reservoir_target_function (other_reservoir , world_position , world_normal , diffuse_brdf );
257
+ let other_resampling_weight = other_mis_weight * (other_target_function . a * other_reservoir . unbiased_contribution_weight);
258
+
259
+ var combined_reservoir = empty_reservoir ();
260
+ combined_reservoir . weight_sum = canonical_resampling_weight + other_resampling_weight ;
261
+ combined_reservoir . confidence_weight = canonical_reservoir . confidence_weight + other_reservoir . confidence_weight;
262
+
263
+ // https://yusuketokuyoshi.com/papers/2024/Efficient_Visibility_Reuse_for_Real-time_ReSTIR_(Supplementary_Document).pdf
264
+ combined_reservoir . visibility = max (0.0 , (canonical_reservoir . visibility * canonical_resampling_weight
265
+ + other_reservoir . visibility * other_resampling_weight ) / combined_reservoir . weight_sum);
266
+
267
+ if rand_f (rng ) < other_resampling_weight / combined_reservoir . weight_sum {
268
+ combined_reservoir . sample = other_reservoir . sample;
269
+
270
+ let inverse_target_function = select (0.0 , 1.0 / other_target_function . a, other_target_function . a > 0.0 );
271
+ combined_reservoir . unbiased_contribution_weight = combined_reservoir . weight_sum * inverse_target_function ;
272
+
273
+ return ReservoirMergeResult (combined_reservoir , other_target_function . rgb);
274
+ } else {
275
+ combined_reservoir . sample = canonical_reservoir . sample;
276
+
277
+ let inverse_target_function = select (0.0 , 1.0 / canonical_target_function . a, canonical_target_function . a > 0.0 );
278
+ combined_reservoir . unbiased_contribution_weight = combined_reservoir . weight_sum * inverse_target_function ;
279
+
280
+ return ReservoirMergeResult (combined_reservoir , canonical_target_function . rgb);
281
+ }
282
+ }
283
+
284
+ fn reservoir_target_function (reservoir : Reservoir , world_position : vec3 <f32 >, world_normal : vec3 <f32 >, diffuse_brdf : vec3 <f32 >) -> vec4 <f32 > {
285
+ if ! reservoir_valid (reservoir ) { return vec4 (0.0 ); }
286
+ let light_contribution = calculate_light_contribution (reservoir . sample, world_position , world_normal ). radiance;
287
+ let target_function = luminance (light_contribution * diffuse_brdf );
288
+ return vec4 (light_contribution , target_function );
289
+ }
0 commit comments