pub(super) fn compute_cascade_splits(near: f32, far: f32, count: u32, lambda: f32) -> [f32; 4] {
let mut splits = [far; 4];
let n = count.min(4) as usize;
for i in 1..=n {
let p = i as f32 / n as f32;
let log_split = near * (far / near).powf(p);
let lin_split = near + (far - near) * p;
splits[i - 1] = lambda * log_split + (1.0 - lambda) * lin_split;
}
splits
}
pub(super) fn compute_cascade_matrix(
light_dir: glam::Vec3,
camera_view: glam::Mat4,
fov: f32,
aspect: f32,
split_near: f32,
split_far: f32,
tile_size: f32,
) -> glam::Mat4 {
let inv_view = camera_view.inverse();
let tan_half_fov = (fov * 0.5).tan();
let mut corners_world = [glam::Vec3::ZERO; 8];
for (i, &z) in [split_near, split_far].iter().enumerate() {
let half_h = tan_half_fov * z;
let half_w = half_h * aspect;
let view_corners = [
glam::Vec3::new(-half_w, -half_h, -z),
glam::Vec3::new(half_w, -half_h, -z),
glam::Vec3::new(half_w, half_h, -z),
glam::Vec3::new(-half_w, half_h, -z),
];
for (j, vc) in view_corners.iter().enumerate() {
corners_world[i * 4 + j] = inv_view.transform_point3(*vc);
}
}
let center = corners_world
.iter()
.copied()
.fold(glam::Vec3::ZERO, |a, b| a + b)
/ 8.0;
let radius = corners_world
.iter()
.map(|c| (*c - center).length())
.fold(0.0f32, f32::max);
let dir = light_dir.normalize();
let light_up = if dir.z.abs() > 0.99 {
glam::Vec3::X
} else {
glam::Vec3::Z
};
let light_view = glam::Mat4::look_at_rh(dir * 500.0, glam::Vec3::ZERO, light_up);
let texel_size = (radius * 2.0) / tile_size;
let center_ls = light_view.transform_point3(center);
let snapped_cx = if texel_size > 0.0 {
(center_ls.x / texel_size).floor() * texel_size
} else {
center_ls.x
};
let snapped_cy = if texel_size > 0.0 {
(center_ls.y / texel_size).floor() * texel_size
} else {
center_ls.y
};
let min_x = snapped_cx - radius;
let max_x = snapped_cx + radius;
let min_y = snapped_cy - radius;
let max_y = snapped_cy + radius;
let mut min_z = f32::MAX;
let mut max_z = f32::MIN;
for c in &corners_world {
let ls_z = light_view.transform_point3(*c).z;
min_z = min_z.min(ls_z);
max_z = max_z.max(ls_z);
}
min_z -= 15.0;
max_z += 5.0;
let light_proj =
glam::Mat4::orthographic_rh(min_x, max_x, min_y, max_y, -max_z, -min_z);
light_proj * light_view
}