Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1#import bevy_core_pipeline::tonemapping::tonemapping_luminance as luminance
2#import bevy_pbr::utils::{rand_f, rand_range_u, sample_cosine_hemisphere}
3#import bevy_render::view::View
4#import bevy_solari::presample_light_tiles::{ResolvedLightSamplePacked, unpack_resolved_light_sample}
5#import bevy_solari::sampling::{calculate_resolved_light_contribution, trace_light_visibility}
6#import bevy_solari::scene_bindings::{trace_ray, resolve_ray_hit_full, RAY_T_MIN}
7#import bevy_solari::world_cache::{
8 WORLD_CACHE_MAX_TEMPORAL_SAMPLES,
9 WORLD_CACHE_DIRECT_LIGHT_SAMPLE_COUNT,
10 WORLD_CACHE_MAX_GI_RAY_DISTANCE,
11 query_world_cache,
12 world_cache_active_cells_count,
13 world_cache_active_cell_indices,
14 world_cache_life,
15 world_cache_geometry_data,
16 world_cache_radiance,
17 world_cache_luminance_deltas,
18 world_cache_active_cells_new_radiance,
21@group(1) @binding(2) var<storage, read_write> light_tile_resolved_samples: array<ResolvedLightSamplePacked>;
22@group(1) @binding(12) var<uniform> view: View;
23struct PushConstants { frame_index: u32, reset: u32 }
24var<push_constant> constants: PushConstants;
26@compute @workgroup_size(64, 1, 1)
27fn sample_radiance(@builtin(workgroup_id) workgroup_id: vec3<u32>, @builtin(global_invocation_id) active_cell_id: vec3<u32>) {
28 if active_cell_id.x < world_cache_active_cells_count {
29 let cell_index = world_cache_active_cell_indices[active_cell_id.x];
30 let geometry_data = world_cache_geometry_data[cell_index];
31 var rng = cell_index + constants.frame_index;
33 // TODO: Initialize newly active cells with data from an adjacent LOD
35 var new_radiance = sample_random_light_ris(geometry_data.world_position, geometry_data.world_normal, workgroup_id.xy, &rng);
37#ifndef NO_MULTIBOUNCE
38 let ray_direction = sample_cosine_hemisphere(geometry_data.world_normal, &rng);
39 let ray_hit = trace_ray(geometry_data.world_position, ray_direction, RAY_T_MIN, WORLD_CACHE_MAX_GI_RAY_DISTANCE, RAY_FLAG_NONE);
40 if ray_hit.kind != RAY_QUERY_INTERSECTION_NONE {
41 let ray_hit = resolve_ray_hit_full(ray_hit);
42 let cell_life = atomicLoad(&world_cache_life[cell_index]);
43 new_radiance += ray_hit.material.base_color * query_world_cache(ray_hit.world_position, ray_hit.geometric_world_normal, view.world_position, cell_life, &rng);
44 }
45#endif
47 world_cache_active_cells_new_radiance[active_cell_id.x] = new_radiance;
48 }
51@compute @workgroup_size(64, 1, 1)
52fn blend_new_samples(@builtin(global_invocation_id) active_cell_id: vec3<u32>) {
53 if active_cell_id.x < world_cache_active_cells_count {
54 let cell_index = world_cache_active_cell_indices[active_cell_id.x];
56 let old_radiance = world_cache_radiance[cell_index];
57 let new_radiance = world_cache_active_cells_new_radiance[active_cell_id.x];
58 let luminance_delta = world_cache_luminance_deltas[cell_index];
60 // https://bsky.app/profile/gboisse.bsky.social/post/3m5blga3ftk2a
61 let sample_count = min(old_radiance.a + 1.0, WORLD_CACHE_MAX_TEMPORAL_SAMPLES);
62 let alpha = abs(luminance_delta) / max(luminance(old_radiance.rgb), 0.001);
63 let max_sample_count = mix(WORLD_CACHE_MAX_TEMPORAL_SAMPLES, 1.0, pow(saturate(alpha), 1.0 / 8.0));
64 let blend_amount = 1.0 / min(sample_count, max_sample_count);
66 let blended_radiance = mix(old_radiance.rgb, new_radiance, blend_amount);
67 let blended_luminance_delta = mix(luminance_delta, luminance(blended_radiance) - luminance(old_radiance.rgb), 1.0 / 8.0);
69 world_cache_radiance[cell_index] = vec4(blended_radiance, sample_count);
70 world_cache_luminance_deltas[cell_index] = blended_luminance_delta;
71 }
74fn sample_random_light_ris(world_position: vec3<f32>, world_normal: vec3<f32>, workgroup_id: vec2<u32>, rng: ptr<function, u32>) -> vec3<f32> {
75 var workgroup_rng = (workgroup_id.x * 5782582u) + workgroup_id.y;
76 let light_tile_start = rand_range_u(128u, &workgroup_rng) * 1024u;
78 var weight_sum = 0.0;
79 var selected_sample_radiance = vec3(0.0);
80 var selected_sample_target_function = 0.0;
81 var selected_sample_world_position = vec4(0.0);
82 let mis_weight = 1.0 / f32(WORLD_CACHE_DIRECT_LIGHT_SAMPLE_COUNT);
83 for (var i = 0u; i < WORLD_CACHE_DIRECT_LIGHT_SAMPLE_COUNT; i++) {
84 let tile_sample = light_tile_start + rand_range_u(1024u, rng);
85 let resolved_light_sample = unpack_resolved_light_sample(light_tile_resolved_samples[tile_sample], view.exposure);
86 let light_contribution = calculate_resolved_light_contribution(resolved_light_sample, world_position, world_normal);
88 let target_function = luminance(light_contribution.radiance);
89 let resampling_weight = mis_weight * (target_function * light_contribution.inverse_pdf);
91 weight_sum += resampling_weight;
93 if rand_f(rng) < resampling_weight / weight_sum {
94 selected_sample_radiance = light_contribution.radiance;
95 selected_sample_target_function = target_function;
96 selected_sample_world_position = resolved_light_sample.world_position;
97 }
98 }
100 var unbiased_contribution_weight = 0.0;
101 if all(selected_sample_radiance != vec3(0.0)) {
102 let inverse_target_function = select(0.0, 1.0 / selected_sample_target_function, selected_sample_target_function > 0.0);
103 unbiased_contribution_weight = weight_sum * inverse_target_function;
105 unbiased_contribution_weight *= trace_light_visibility(world_position, selected_sample_world_position);
106 }
108 return selected_sample_radiance * unbiased_contribution_weight;
Contents