diff --git a/crates/bevy_solari/src/realtime/restir_di.wgsl b/crates/bevy_solari/src/realtime/restir_di.wgsl index 19543666e5..c101157d75 100644 --- a/crates/bevy_solari/src/realtime/restir_di.wgsl +++ b/crates/bevy_solari/src/realtime/restir_di.wgsl @@ -8,7 +8,7 @@ #import bevy_render::maths::PI #import bevy_render::view::View #import bevy_solari::sampling::{ - LightSample, IndependentLightSample, generate_point_independent_sample, calculate_light_contribution_from_independent, calculate_light_contribution, + LightSample, IndependentLightSample, sample_random_light_contribution, calculate_light_contribution, trace_visibility, trace_light_visibility, sample_disk } @@ -29,6 +29,7 @@ struct PushConstants { frame_index: u32, reset: u32 } var constants: PushConstants; +const INITIAL_SAMPLES = 32u; const SPATIAL_REUSE_RADIUS_PIXELS = 30.0; const CONFIDENCE_WEIGHT_CAP = 20.0; @@ -47,8 +48,7 @@ fn compute_seed(global_id: vec3) -> u32 { } @compute @workgroup_size(8, 8, 1) -fn initial_and_temporal(@builtin(global_invocation_id) global_id: vec3, - @builtin(local_invocation_index) local_id: u32) { +fn initial_and_temporal(@builtin(global_invocation_id) global_id: vec3) { if any(global_id.xy >= vec2u(view.viewport.zw)) { return; } var rng = compute_seed(global_id); @@ -66,7 +66,7 @@ fn initial_and_temporal(@builtin(global_invocation_id) global_id: vec3, let base_color = pow(unpack4x8unorm(gpixel.r).rgb, vec3(2.2)); let diffuse_brdf = base_color / PI; - let initial_reservoir = generate_initial_reservoir(world_position, world_normal, diffuse_brdf, &rng, local_id); + let initial_reservoir = generate_initial_reservoir(world_position, world_normal, diffuse_brdf, &rng); let temporal_reservoir = load_temporal_reservoir(global_id.xy, depth, world_position, world_normal); let merge_result = merge_reservoirs(initial_reservoir, temporal_reservoir, world_position, world_normal, diffuse_brdf, &rng); @@ -115,19 +115,14 @@ fn spatial_and_shade(@builtin(global_invocation_id) global_id: vec3) { textureStore(view_output, global_id.xy, vec4(pixel_color, 1.0)); } -var position_independent_initial_samples: array; - -fn generate_initial_reservoir(world_position: vec3, world_normal: vec3, diffuse_brdf: vec3, rng: ptr, local_id: u32) -> Reservoir { - position_independent_initial_samples[local_id] = generate_point_independent_sample(rng); - workgroupBarrier(); - +fn generate_initial_reservoir(world_position: vec3, world_normal: vec3, diffuse_brdf: vec3, rng: ptr) -> Reservoir { var reservoir = empty_reservoir(); var reservoir_target_function = 0.0; var ray_direction = vec4(0.0); - for (var i = 0u; i < 64u; i++) { - let light_contribution = calculate_light_contribution_from_independent(position_independent_initial_samples[i], world_position, world_normal); + for (var i = 0u; i < INITIAL_SAMPLES; i++) { + let light_contribution = sample_random_light_contribution(rng, world_position, world_normal); - let mis_weight = 1.0 / 64.0; + let mis_weight = 1.0 / f32(INITIAL_SAMPLES); let target_function = luminance(light_contribution.radiance * diffuse_brdf); let resampling_weight = mis_weight * (target_function * light_contribution.inverse_pdf); diff --git a/crates/bevy_solari/src/scene/sampling.wgsl b/crates/bevy_solari/src/scene/sampling.wgsl index 43ea00193c..1f4ef7e9c9 100644 --- a/crates/bevy_solari/src/scene/sampling.wgsl +++ b/crates/bevy_solari/src/scene/sampling.wgsl @@ -58,8 +58,7 @@ struct SampleRandomLightResult { } fn sample_random_light(ray_origin: vec3, origin_world_normal: vec3, rng: ptr) -> SampleRandomLightResult { - let pos_independent_sample = generate_point_independent_sample(rng); - let light_contribution = calculate_light_contribution_from_independent(pos_independent_sample, ray_origin, origin_world_normal); + let light_contribution = sample_random_light_contribution(rng, ray_origin, origin_world_normal); let visibility = trace_visibility(ray_origin, light_contribution.ray_direction); return SampleRandomLightResult(light_contribution.radiance * visibility, light_contribution.inverse_pdf); } @@ -84,75 +83,22 @@ struct LightContribution { ray_direction: vec4, } -fn generate_point_independent_sample(rng: ptr) -> IndependentLightSample { +fn sample_random_light_contribution(rng: ptr, ray_origin: vec3, origin_world_normal: vec3) -> LightContribution { let light_count = arrayLength(&light_sources); let light_id = rand_range_u(light_count, rng); let light_source = light_sources[light_id]; var light_sample = LightSample(vec2(light_id, 0), rand_vec2f(rng)); - var sample: IndependentLightSample; + var light_contribution: LightContribution; if light_source.kind == LIGHT_SOURCE_KIND_DIRECTIONAL { - sample = calculate_directional_light_independent_contribution(light_sample, light_source.id); + light_contribution = calculate_directional_light_contribution(light_sample, light_source.id, origin_world_normal); } else { let triangle_count = light_source.kind >> 1u; - light_sample.light_id.y = rand_range_u(triangle_count, rng); - sample = calculate_emissive_mesh_independent_contribution(light_sample, light_source.id, triangle_count); + light_contribution = calculate_emissive_mesh_contribution(light_sample, light_source.id, triangle_count, ray_origin, origin_world_normal); } + light_contribution.inverse_pdf *= f32(light_count); - sample.inverse_pdf *= f32(light_count); - return sample; -} - -fn calculate_directional_light_independent_contribution(light_sample: LightSample, directional_light_id: u32) -> IndependentLightSample { - let directional_light = directional_lights[directional_light_id]; - -#ifdef DIRECTIONAL_LIGHT_SOFT_SHADOWS - // Sample a random direction within a cone whose base is the sun approximated as a disk - // https://www.realtimerendering.com/raytracinggems/unofficial_RayTracingGems_v1.9.pdf#0004286901.INDD%3ASec30%3A305 - let cos_theta = (1.0 - light_sample.random.x) + light_sample.random.x * directional_light.cos_theta_max; - let sin_theta = sqrt(1.0 - cos_theta * cos_theta); - let phi = light_sample.random.y * PI_2; - let x = cos(phi) * sin_theta; - let y = sin(phi) * sin_theta; - var ray_direction = vec3(x, y, cos_theta); - - // Rotate the ray so that the cone it was sampled from is aligned with the light direction - ray_direction = build_orthonormal_basis(directional_light.direction_to_light) * ray_direction; -#else - let ray_direction = directional_light.direction_to_light; -#endif - return IndependentLightSample(light_sample, directional_light.luminance, directional_light.inverse_pdf, vec4(ray_direction, 0.0), vec3(0.0)); -} - -fn calculate_emissive_mesh_independent_contribution(light_sample: LightSample, instance_id: u32, triangle_count: u32) -> IndependentLightSample { - let barycentrics = triangle_barycentrics(light_sample.random); - let triangle_id = light_sample.light_id.y; - let triangle_data = resolve_triangle_for_di(instance_id, triangle_id, barycentrics); - let inverse_pdf = f32(triangle_count) * triangle_data.triangle_area; - return IndependentLightSample(light_sample, triangle_data.emissive, inverse_pdf, vec4(triangle_data.world_position, 1.0), triangle_data.world_normal); -} - -fn calculate_light_contribution_from_independent(sample: IndependentLightSample, ray_origin: vec3, origin_world_normal: vec3) -> LightContribution { - var radiance = vec3(0.0); - var ray_direction = vec4(0.0); - if sample.pos_or_direction.w == 0.0 { - // Directional light - ray_direction = vec4(sample.pos_or_direction.xyz, RAY_T_MAX); - let cos_theta_origin = saturate(dot(ray_direction.xyz, origin_world_normal)); - radiance = sample.radiance * cos_theta_origin; - } else { - // Emissive mesh - let end_point = sample.pos_or_direction.xyz; - ray_direction = vec4(end_point - ray_origin, 0.0); - let light_distance_squared = dot(ray_direction.xyz, ray_direction.xyz); - ray_direction.w = sqrt(light_distance_squared); - ray_direction = vec4(ray_direction.xyz / ray_direction.w, ray_direction.w); - let cos_theta_origin = saturate(dot(ray_direction.xyz, origin_world_normal)); - let cos_theta_light = saturate(dot(-ray_direction.xyz, sample.normal)); - - radiance = sample.radiance * cos_theta_origin * (cos_theta_light / light_distance_squared); - } - return LightContribution(sample.sample, radiance, sample.inverse_pdf, ray_direction); + return light_contribution; } fn calculate_light_contribution(light_sample: LightSample, ray_origin: vec3, origin_world_normal: vec3) -> LightContribution {