diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index c73424c86a091..8e1cfdd41d34b 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -58,10 +58,17 @@ use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetApp, Assets, Handle}; use bevy_ecs::prelude::*; use bevy_render::{ - camera::CameraUpdateSystem, extract_component::ExtractComponentPlugin, - extract_resource::ExtractResourcePlugin, prelude::Color, render_asset::prepare_assets, - render_graph::RenderGraph, render_phase::sort_phase_system, render_resource::Shader, - texture::Image, view::VisibilitySystems, ExtractSchedule, Render, RenderApp, RenderSet, + camera::{CameraUpdateSystem, Projection}, + extract_component::ExtractComponentPlugin, + extract_resource::ExtractResourcePlugin, + prelude::Color, + render_asset::prepare_assets, + render_graph::RenderGraph, + render_phase::sort_phase_system, + render_resource::Shader, + texture::Image, + view::VisibilitySystems, + ExtractSchedule, Render, RenderApp, RenderSet, }; use bevy_transform::TransformSystem; use environment_map::EnvironmentMapPlugin; @@ -273,7 +280,11 @@ impl Plugin for PbrPlugin { .after(TransformSystem::TransformPropagate) .after(VisibilitySystems::CheckVisibility) .after(CameraUpdateSystem), - update_directional_light_cascades + ( + clear_directional_light_cascades, + build_directional_light_cascades::, + ) + .chain() .in_set(SimulationLightSystems::UpdateDirectionalLightCascades) .after(TransformSystem::TransformPropagate) .after(CameraUpdateSystem), diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 599bd352933f4..3b087ffc6daf9 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -1,14 +1,13 @@ use std::collections::HashSet; use bevy_ecs::prelude::*; -use bevy_math::{Mat4, Rect, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles}; +use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles}; use bevy_reflect::prelude::*; use bevy_render::{ - camera::Camera, + camera::{Camera, CameraProjection}, color::Color, extract_component::ExtractComponent, extract_resource::ExtractResource, - prelude::Projection, primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, HalfSpace, Sphere}, render_resource::BufferBindingType, renderer::RenderDevice, @@ -397,9 +396,18 @@ pub struct Cascade { pub(crate) texel_size: f32, } -pub fn update_directional_light_cascades( +pub fn clear_directional_light_cascades(mut lights: Query<(&DirectionalLight, &mut Cascades)>) { + for (directional_light, mut cascades) in lights.iter_mut() { + if !directional_light.shadows_enabled { + continue; + } + cascades.cascades.clear(); + } +} + +pub fn build_directional_light_cascades( directional_light_shadow_map: Res, - views: Query<(Entity, &GlobalTransform, &Projection, &Camera)>, + views: Query<(Entity, &GlobalTransform, &P, &Camera)>, mut lights: Query<( &GlobalTransform, &DirectionalLight, @@ -432,7 +440,6 @@ pub fn update_directional_light_cascades( let light_to_world = Mat4::from_quat(transform.compute_transform().rotation); let light_to_world_inverse = light_to_world.inverse(); - cascades.cascades.clear(); for (view_entity, projection, view_to_world) in views.iter().copied() { let camera_to_light_view = light_to_world_inverse * view_to_world; let view_cascades = cascades_config @@ -449,17 +456,8 @@ pub fn update_directional_light_cascades( }; let z_far = -far_bound; - let corners = match projection { - Projection::Perspective(projection) => frustum_corners( - projection.aspect_ratio, - (projection.fov / 2.).tan(), - z_near, - z_far, - ), - Projection::Orthographic(projection) => { - frustum_corners_ortho(projection.area, z_near, z_far) - } - }; + let corners = projection.get_frustum_corners(z_near, z_far); + calculate_cascade( corners, directional_light_shadow_map.size as f32, @@ -473,36 +471,6 @@ pub fn update_directional_light_cascades( } } -fn frustum_corners_ortho(area: Rect, z_near: f32, z_far: f32) -> [Vec3A; 8] { - // NOTE: These vertices are in the specific order required by [`calculate_cascade`]. - [ - Vec3A::new(area.max.x, area.min.y, z_near), // bottom right - Vec3A::new(area.max.x, area.max.y, z_near), // top right - Vec3A::new(area.min.x, area.max.y, z_near), // top left - Vec3A::new(area.min.x, area.min.y, z_near), // bottom left - Vec3A::new(area.max.x, area.min.y, z_far), // bottom right - Vec3A::new(area.max.x, area.max.y, z_far), // top right - Vec3A::new(area.min.x, area.max.y, z_far), // top left - Vec3A::new(area.min.x, area.min.y, z_far), // bottom left - ] -} - -fn frustum_corners(aspect_ratio: f32, tan_half_fov: f32, z_near: f32, z_far: f32) -> [Vec3A; 8] { - let a = z_near.abs() * tan_half_fov; - let b = z_far.abs() * tan_half_fov; - // NOTE: These vertices are in the specific order required by [`calculate_cascade`]. - [ - Vec3A::new(a * aspect_ratio, -a, z_near), // bottom right - Vec3A::new(a * aspect_ratio, a, z_near), // top right - Vec3A::new(-a * aspect_ratio, a, z_near), // top left - Vec3A::new(-a * aspect_ratio, -a, z_near), // bottom left - Vec3A::new(b * aspect_ratio, -b, z_far), // bottom right - Vec3A::new(b * aspect_ratio, b, z_far), // top right - Vec3A::new(-b * aspect_ratio, b, z_far), // top left - Vec3A::new(-b * aspect_ratio, -b, z_far), // bottom left - ] -} - /// Returns a [`Cascade`] for the frustum defined by `frustum_corners`. /// The corner vertices should be specified in the following order: /// first the bottom right, top right, top left, bottom left for the near plane, then similar for the far plane. diff --git a/crates/bevy_render/src/camera/projection.rs b/crates/bevy_render/src/camera/projection.rs index 7b62385cac792..912dc8009f37c 100644 --- a/crates/bevy_render/src/camera/projection.rs +++ b/crates/bevy_render/src/camera/projection.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use bevy_app::{App, Plugin, PostStartup, PostUpdate}; use bevy_ecs::{prelude::*, reflect::ReflectComponent}; -use bevy_math::{Mat4, Rect, Vec2}; +use bevy_math::{Mat4, Rect, Vec2, Vec3A}; use bevy_reflect::{ std_traits::ReflectDefault, GetTypeRegistration, Reflect, ReflectDeserialize, ReflectSerialize, }; @@ -58,6 +58,7 @@ pub trait CameraProjection { fn get_projection_matrix(&self) -> Mat4; fn update(&mut self, width: f32, height: f32); fn far(&self) -> f32; + fn get_frustum_corners(&self, z_near: f32, z_far: f32) -> [Vec3A; 8]; } /// A configurable [`CameraProjection`] that can select its projection type at runtime. @@ -101,6 +102,13 @@ impl CameraProjection for Projection { Projection::Orthographic(projection) => projection.far(), } } + + fn get_frustum_corners(&self, z_near: f32, z_far: f32) -> [Vec3A; 8] { + match self { + Projection::Perspective(projection) => projection.get_frustum_corners(z_near, z_far), + Projection::Orthographic(projection) => projection.get_frustum_corners(z_near, z_far), + } + } } impl Default for Projection { @@ -153,6 +161,24 @@ impl CameraProjection for PerspectiveProjection { fn far(&self) -> f32 { self.far } + + fn get_frustum_corners(&self, z_near: f32, z_far: f32) -> [Vec3A; 8] { + let tan_half_fov = (self.fov / 2.).tan(); + let a = z_near.abs() * tan_half_fov; + let b = z_far.abs() * tan_half_fov; + let aspect_ratio = self.aspect_ratio; + // NOTE: These vertices are in the specific order required by [`calculate_cascade`]. + [ + Vec3A::new(a * aspect_ratio, -a, z_near), // bottom right + Vec3A::new(a * aspect_ratio, a, z_near), // top right + Vec3A::new(-a * aspect_ratio, a, z_near), // top left + Vec3A::new(-a * aspect_ratio, -a, z_near), // bottom left + Vec3A::new(b * aspect_ratio, -b, z_far), // bottom right + Vec3A::new(b * aspect_ratio, b, z_far), // top right + Vec3A::new(-b * aspect_ratio, b, z_far), // top left + Vec3A::new(-b * aspect_ratio, -b, z_far), // bottom left + ] + } } impl Default for PerspectiveProjection { @@ -309,6 +335,21 @@ impl CameraProjection for OrthographicProjection { fn far(&self) -> f32 { self.far } + + fn get_frustum_corners(&self, z_near: f32, z_far: f32) -> [Vec3A; 8] { + let area = self.area; + // NOTE: These vertices are in the specific order required by [`calculate_cascade`]. + [ + Vec3A::new(area.max.x, area.min.y, z_near), // bottom right + Vec3A::new(area.max.x, area.max.y, z_near), // top right + Vec3A::new(area.min.x, area.max.y, z_near), // top left + Vec3A::new(area.min.x, area.min.y, z_near), // bottom left + Vec3A::new(area.max.x, area.min.y, z_far), // bottom right + Vec3A::new(area.max.x, area.max.y, z_far), // top right + Vec3A::new(area.min.x, area.max.y, z_far), // top left + Vec3A::new(area.min.x, area.min.y, z_far), // bottom left + ] + } } impl Default for OrthographicProjection {