From e363cbba19a98140be1909684c707e9d29d48b10 Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Sat, 17 Aug 2024 20:24:00 +0300 Subject: [PATCH] use cone shape to mark pixels in stencil buffer for spot lights - sphere was too inefficient since spot lights usually have quite narrow "beam" --- fyrox-impl/src/renderer/light/mod.rs | 117 +++++++++++++++++---------- 1 file changed, 75 insertions(+), 42 deletions(-) diff --git a/fyrox-impl/src/renderer/light/mod.rs b/fyrox-impl/src/renderer/light/mod.rs index 865584d73..8c85a32b7 100644 --- a/fyrox-impl/src/renderer/light/mod.rs +++ b/fyrox-impl/src/renderer/light/mod.rs @@ -85,6 +85,7 @@ pub struct DeferredLightRenderer { ambient_light_shader: AmbientLightShader, quad: GeometryBuffer, sphere: GeometryBuffer, + cone: GeometryBuffer, skybox: GeometryBuffer, flat_shader: FlatShader, skybox_shader: SkyboxShader, @@ -196,6 +197,16 @@ impl DeferredLightRenderer { GeometryBufferKind::StaticDraw, state, )?, + cone: GeometryBuffer::from_surface_data( + &SurfaceData::make_cone( + 16, + 0.5, + 1.0, + &Matrix4::new_translation(&Vector3::new(0.0, -1.0, 0.0)), + ), + GeometryBufferKind::StaticDraw, + state, + )?, flat_shader: FlatShader::new(state)?, skybox_shader: SkyboxShader::new(state)?, spot_shadow_map_renderer: SpotShadowMapRenderer::new( @@ -427,43 +438,71 @@ impl DeferredLightRenderer { let distance_to_camera = (light.global_position() - camera.global_position()).norm(); - let (raw_radius, shadows_distance, shadows_enabled, shadows_fade_out_range) = - if let Some(spot_light) = light.cast::() { - ( - spot_light.distance(), - settings.spot_shadows_distance, - spot_light.base_light_ref().is_cast_shadows() - && distance_to_camera <= settings.spot_shadows_distance - && settings.spot_shadows_enabled, - settings.spot_shadows_fade_out_range, - ) - } else if let Some(point_light) = light.cast::() { - ( - point_light.radius(), - settings.point_shadows_distance, - point_light.base_light_ref().is_cast_shadows() - && distance_to_camera <= settings.point_shadows_distance - && settings.point_shadows_enabled, - settings.point_shadows_fade_out_range, - ) - } else if let Some(directional) = light.cast::() { - ( - f32::MAX, - 0.0, - directional.base_light_ref().is_cast_shadows() - && settings.csm_settings.enabled, - 0.0, - ) - } else { - continue; - }; + let ( + raw_radius, + shadows_distance, + shadows_enabled, + shadows_fade_out_range, + bounding_shape, + shape_specific_matrix, + ) = if let Some(spot_light) = light.cast::() { + let margin = 5.0f32.to_radians(); + // Angle at the top vertex of the right triangle with vertecal side be 1.0 and horizontal + // side be 0.5. + let vertex_angle = 26.56f32.to_radians(); + let k_angle = (spot_light.full_cone_angle() * 0.5 + vertex_angle + margin).tan(); + ( + spot_light.distance(), + settings.spot_shadows_distance, + spot_light.base_light_ref().is_cast_shadows() + && distance_to_camera <= settings.spot_shadows_distance + && settings.spot_shadows_enabled, + settings.spot_shadows_fade_out_range, + &self.cone, + Matrix4::new_nonuniform_scaling(&Vector3::new( + spot_light.distance() * k_angle, + spot_light.distance() * 1.05, + spot_light.distance() * k_angle, + )), + ) + } else if let Some(point_light) = light.cast::() { + ( + point_light.radius(), + settings.point_shadows_distance, + point_light.base_light_ref().is_cast_shadows() + && distance_to_camera <= settings.point_shadows_distance + && settings.point_shadows_enabled, + settings.point_shadows_fade_out_range, + &self.sphere, + Matrix4::new_scaling(point_light.radius() * 1.15), + ) + } else if let Some(directional) = light.cast::() { + ( + f32::MAX, + 0.0, + directional.base_light_ref().is_cast_shadows() && settings.csm_settings.enabled, + 0.0, + // Makes no sense, but whatever. + &self.sphere, + Matrix4::identity(), + ) + } else { + continue; + }; let light_position = light.global_position(); let scl = light.local_transform().scale(); let light_radius_scale = scl.x.max(scl.y).max(scl.z); let light_radius = light_radius_scale * raw_radius; - let light_r_inflate = 1.05 * light_radius; - let light_radius_vec = Vector3::new(light_r_inflate, light_r_inflate, light_r_inflate); + let light_rotation = light + .global_transform() + .basis() + .try_inverse() + .unwrap_or_default() + .transpose() + .to_homogeneous(); + let bounding_shape_matrix = + Matrix4::new_translation(&light_position) * light_rotation * shape_specific_matrix; let emit_direction = light .up_vector() .try_normalize(f32::EPSILON) @@ -494,10 +533,8 @@ impl DeferredLightRenderer { let mut light_view_projection = Matrix4::identity(); // Mark lighted areas in stencil buffer to do light calculations only on them. - let sphere = &self.sphere; - pass_stats += frame_buffer.draw( - sphere, + bounding_shape, state, viewport, &self.flat_shader.program, @@ -520,15 +557,13 @@ impl DeferredLightRenderer { |mut program_binding| { program_binding.set_matrix4( &self.flat_shader.wvp_matrix, - &(view_projection - * Matrix4::new_translation(&light_position) - * Matrix4::new_nonuniform_scaling(&light_radius_vec)), + &(view_projection * bounding_shape_matrix), ); }, )?; pass_stats += frame_buffer.draw( - sphere, + bounding_shape, state, viewport, &self.flat_shader.program, @@ -551,9 +586,7 @@ impl DeferredLightRenderer { |mut program_binding| { program_binding.set_matrix4( &self.flat_shader.wvp_matrix, - &(view_projection - * Matrix4::new_translation(&light_position) - * Matrix4::new_nonuniform_scaling(&light_radius_vec)), + &(view_projection * bounding_shape_matrix), ); }, )?;