diff --git a/Cargo.toml b/Cargo.toml index c9b346a..ef0ee41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ bevy = { version = "0.13", default-features = false, features = [ "bevy_sprite", ] } fast_poisson = "1.0.0" +radsort = "0.1.0" thread_local = "1.1" [dev-dependencies] diff --git a/src/render/catalinzz/mod.rs b/src/render/catalinzz/mod.rs index 4eb5163..9f0ddd2 100644 --- a/src/render/catalinzz/mod.rs +++ b/src/render/catalinzz/mod.rs @@ -489,7 +489,7 @@ pub fn prepare_lights( mut commands: Commands, mut texture_cache: ResMut, main_views: Query>, - mut point_lights: Query>, + point_lights: Query<(Entity, &ExtractedPointLight2d)>, shadow_map_config: Res, mut shadow_map_storage: ResMut, mut gpu_meta_buffers: ResMut, @@ -506,8 +506,11 @@ pub fn prepare_lights( gpu_meta_buffers.clear(); - for (light_index, light_entity) in point_lights.iter_mut().enumerate() { - // TODO support different pcf settings for different lights + let mut point_lights = point_lights.iter().collect::>(); + radsort::sort_by_key(&mut point_lights, |(_, light)| light.id); + + for (light_index, (light_entity, _)) in point_lights.into_iter().enumerate() { + // TODO support different settings for different lights let meta_index = gpu_meta_buffers.push_light_meta(GpuShadowMapMeta { index: light_index as u32, size: shadow_map_config.size, diff --git a/src/render/catalinzz/shaders/shadow_2d_main_pass.wgsl b/src/render/catalinzz/shaders/shadow_2d_main_pass.wgsl index debeb81..6d892e7 100644 --- a/src/render/catalinzz/shaders/shadow_2d_main_pass.wgsl +++ b/src/render/catalinzz/shaders/shadow_2d_main_pass.wgsl @@ -103,15 +103,6 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4f { let attend_color = visibility * visibility * (*light).intensity * light_color.rgb; color += attend_color; } - - // if length(rel_px_ss) < light_range_ss { - // var visibility = pcf(rel_ss, shadow_map_meta.pcf_samples, pcf_radius_rel, i_light); - // visibility *= 1. - saturate( - // (rel_px_dist - light_radius_ss) / (light_range_ss - light_radius_ss) - // ); - // let attend_color = visibility * visibility * (*light).intensity * light_color.rgb; - // color += attend_color; - // } } return textureSample(main_tex, main_tex_sampler, in.uv) diff --git a/src/render/mod.rs b/src/render/mod.rs index f579201..9cf39a3 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -94,6 +94,7 @@ impl DynamicUniformIndex { #[derive(Component, Clone, Copy)] pub struct ExtractedPointLight2d { + pub id: u32, pub color: Color, pub intensity: f32, pub range: f32, @@ -106,15 +107,19 @@ pub fn extract_lights( point_lights_query: Extract>, spot_lights_query: Extract>, ) { + let mut id = 0; + commands.insert_or_spawn_batch( point_lights_query .iter() .map(|(entity, light, transform, visible_entities)| { let transform = GlobalTransform::from_translation(transform.translation()); + id += 1; ( entity, ( ExtractedPointLight2d { + id: id - 1, color: light.color, intensity: light.intensity, range: light.range, @@ -135,15 +140,17 @@ pub fn extract_lights( .iter() .map(|(entity, light, transform, visible_entities)| { let transform = GlobalTransform::from_translation(transform.translation()); + id += 1; ( entity, ( ExtractedPointLight2d { + id: id - 1, color: light.color, intensity: light.intensity, range: light.range, radius: light.radius, - spot_light_angles: light.sector.into_angles(), + spot_light_angles: light.sector.into_extent(), }, transform, visible_entities.clone(), @@ -158,7 +165,7 @@ pub fn extract_lights( pub fn prepare_lights( mut commands: Commands, main_views: Query<(Entity, &ExtractedView, &VisibleEntities), With>, - lights_query: Query<(&ExtractedPointLight2d, &GlobalTransform), With>, + lights_query: Query<(&ExtractedPointLight2d, &GlobalTransform)>, ambient_light: Res, render_device: Res, render_queue: Res, @@ -183,11 +190,14 @@ pub fn prepare_lights( main_view.projection * main_view.transform.compute_matrix().inverse() }); - for visible_light in visible_entities.entities.iter().copied() { - let Ok((light, light_transform)) = lights_query.get(visible_light) else { - continue; - }; + let mut visible_lights = visible_entities + .entities + .iter() + .filter_map(|e| lights_query.get(*e).ok()) + .collect::>(); + radsort::sort_by_key(&mut visible_lights, |(light, _)| light.id); + for (light, light_transform) in &visible_lights { let position_ws = light_transform.translation().extend(1.); let screen_size = 2. / Vec2::new( diff --git a/src/render/shaders/math.wgsl b/src/render/shaders/math.wgsl index c91113c..097f552 100644 --- a/src/render/shaders/math.wgsl +++ b/src/render/shaders/math.wgsl @@ -1,22 +1,35 @@ #define_import_path bevy_incandescent::math const PI: f32 = 3.14159265358979323846; +const TAU: f32 = 6.28318530717958647692; const FRAC_PI_2: f32 = 1.57079632679489661923; +// https://www.cnblogs.com/miloyip/archive/2013/04/19/3029852.html fn is_point_inside_sector( point: vec2f, center: vec2f, radius: f32, angles: array, ) -> bool { + if angles[1] >= TAU { + return true; + } + let p0 = point - center; let sqr_dist = p0.x * p0.x + p0.y * p0.y; if sqr_dist > radius * radius { return false; } - var pol = atan(p0.y / p0.x); - if p0.x < 0.0 { - pol += PI; + + let ctr = vec2f(cos(angles[0]), sin(angles[0])); + let cos_ext = cos(angles[1]); + let d = dot(p0, ctr); + + if d >= 0. && cos_ext >= 0. { + return d * d > sqr_dist * cos_ext * cos_ext; + } else if d < 0. && cos_ext < 0. { + return d * d < sqr_dist * cos_ext * cos_ext; + } else { + return d >= 0.; } - return pol > angles[0] && pol < angles[1]; }