Add orthographic camera support back to directional shadows (#7796)
# Objective Fixes #7797 ## Solution This **seems** like a simple fix, but I'm not 100% confident and I may have messed up the math in some way. In particular, I'm not sure what I should be using for an FOV value. However, this seems to be producing similar results to 0.9. Here's the `orthographic` example with a default directional light. edit: better screen grab below.
This commit is contained in:
parent
91ff782439
commit
a39c22386b
@ -1,7 +1,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles};
|
||||
use bevy_math::{Mat4, Rect, UVec2, UVec3, Vec2, Vec3, Vec3A, Vec3Swizzles, Vec4, Vec4Swizzles};
|
||||
use bevy_reflect::prelude::*;
|
||||
use bevy_render::{
|
||||
camera::Camera,
|
||||
@ -413,19 +413,12 @@ pub fn update_directional_light_cascades(
|
||||
) {
|
||||
let views = views
|
||||
.iter()
|
||||
.filter_map(|view| match view {
|
||||
// TODO: orthographic camera projection support.
|
||||
(entity, transform, Projection::Perspective(projection), camera)
|
||||
if camera.is_active =>
|
||||
{
|
||||
Some((
|
||||
entity,
|
||||
projection.aspect_ratio,
|
||||
(0.5 * projection.fov).tan(),
|
||||
transform.compute_matrix(),
|
||||
))
|
||||
.filter_map(|(entity, transform, projection, camera)| {
|
||||
if camera.is_active {
|
||||
Some((entity, projection, transform.compute_matrix()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -444,27 +437,38 @@ pub fn update_directional_light_cascades(
|
||||
let light_to_world_inverse = light_to_world.inverse();
|
||||
|
||||
cascades.cascades.clear();
|
||||
for (view_entity, aspect_ratio, tan_half_fov, view_to_world) in views.iter().copied() {
|
||||
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
|
||||
.bounds
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, far_bound)| {
|
||||
// Negate bounds as -z is camera forward direction.
|
||||
let z_near = if idx > 0 {
|
||||
(1.0 - cascades_config.overlap_proportion)
|
||||
* -cascades_config.bounds[idx - 1]
|
||||
} else {
|
||||
-cascades_config.minimum_distance
|
||||
};
|
||||
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)
|
||||
}
|
||||
};
|
||||
calculate_cascade(
|
||||
aspect_ratio,
|
||||
tan_half_fov,
|
||||
corners,
|
||||
directional_light_shadow_map.size as f32,
|
||||
light_to_world,
|
||||
camera_to_light_view,
|
||||
// Negate bounds as -z is camera forward direction.
|
||||
if idx > 0 {
|
||||
(1.0 - cascades_config.overlap_proportion)
|
||||
* -cascades_config.bounds[idx - 1]
|
||||
} else {
|
||||
-cascades_config.minimum_distance
|
||||
},
|
||||
-far_bound,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
@ -473,37 +477,45 @@ 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.
|
||||
fn calculate_cascade(
|
||||
aspect_ratio: f32,
|
||||
tan_half_fov: f32,
|
||||
frustum_corners: [Vec3A; 8],
|
||||
cascade_texture_size: f32,
|
||||
light_to_world: Mat4,
|
||||
camera_to_light: Mat4,
|
||||
z_near: f32,
|
||||
z_far: f32,
|
||||
) -> Cascade {
|
||||
debug_assert!(z_near <= 0.0, "z_near {z_near} must be <= 0.0");
|
||||
debug_assert!(z_far <= 0.0, "z_far {z_far} must be <= 0.0");
|
||||
// NOTE: This whole function is very sensitive to floating point precision and instability and
|
||||
// has followed instructions to avoid view dependence from the section on cascade shadow maps in
|
||||
// Eric Lengyel's Foundations of Game Engine Development 2: Rendering. Be very careful when
|
||||
// modifying this code!
|
||||
|
||||
let a = z_near.abs() * tan_half_fov;
|
||||
let b = z_far.abs() * tan_half_fov;
|
||||
// NOTE: These vertices are in a specific order: bottom right, top right, top left, bottom left
|
||||
// for near then for far
|
||||
let frustum_corners = [
|
||||
Vec3A::new(a * aspect_ratio, -a, z_near),
|
||||
Vec3A::new(a * aspect_ratio, a, z_near),
|
||||
Vec3A::new(-a * aspect_ratio, a, z_near),
|
||||
Vec3A::new(-a * aspect_ratio, -a, z_near),
|
||||
Vec3A::new(b * aspect_ratio, -b, z_far),
|
||||
Vec3A::new(b * aspect_ratio, b, z_far),
|
||||
Vec3A::new(-b * aspect_ratio, b, z_far),
|
||||
Vec3A::new(-b * aspect_ratio, -b, z_far),
|
||||
];
|
||||
|
||||
let mut min = Vec3A::splat(f32::MAX);
|
||||
let mut max = Vec3A::splat(f32::MIN);
|
||||
for corner_camera_view in frustum_corners {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user