Compute cascade view-projection matrices by splitting the camera frustum into NUM_CASCADES slices and fitting a tight ortho projection around each slice from the light's perspective. `light_dir` points from the surface toward the light (the same convention as the rest of the engine).
(
&mut self,
light_dir: [f32; 3],
_camera_pos: [f32; 3],
camera_view: [[f32; 4]; 4],
camera_proj: [[f32; 4]; 4],
near: f32,
far: f32,
sc
| 332 | /// `light_dir` points from the surface toward the light (the same |
| 333 | /// convention as the rest of the engine). |
| 334 | pub fn compute_cascade_vps( |
| 335 | &mut self, |
| 336 | light_dir: [f32; 3], |
| 337 | _camera_pos: [f32; 3], |
| 338 | camera_view: [[f32; 4]; 4], |
| 339 | camera_proj: [[f32; 4]; 4], |
| 340 | near: f32, |
| 341 | far: f32, |
| 342 | scene_bounds: Option<([f32; 3], [f32; 3])>, |
| 343 | ) { |
| 344 | let len = (light_dir[0] * light_dir[0] |
| 345 | + light_dir[1] * light_dir[1] |
| 346 | + light_dir[2] * light_dir[2]) |
| 347 | .sqrt(); |
| 348 | let d = if len > 1e-6 { |
| 349 | [light_dir[0] / len, light_dir[1] / len, light_dir[2] / len] |
| 350 | } else { |
| 351 | [0.0, 1.0, 0.0] |
| 352 | }; |
| 353 | |
| 354 | // Compute frustum split distances using practical split scheme |
| 355 | // (Nvidia GPU Gems 3, Chapter 10): blend of logarithmic and |
| 356 | // uniform split for stability. |
| 357 | let lambda = 0.5f32; // blend factor (0 = uniform, 1 = logarithmic) |
| 358 | let ratio = far / near; |
| 359 | let mut splits = [0.0f32; NUM_CASCADES + 1]; |
| 360 | splits[0] = near; |
| 361 | for i in 1..NUM_CASCADES { |
| 362 | let p = i as f32 / NUM_CASCADES as f32; |
| 363 | let log_split = near * ratio.powf(p); |
| 364 | let uniform_split = near + (far - near) * p; |
| 365 | splits[i] = lambda * log_split + (1.0 - lambda) * uniform_split; |
| 366 | } |
| 367 | splits[NUM_CASCADES] = far; |
| 368 | |
| 369 | // Store view-space Z split distances for shader cascade selection. |
| 370 | // cascade_splits[i] = far edge of cascade i. |
| 371 | for i in 0..NUM_CASCADES { |
| 372 | self.cascade_splits[i] = splits[i + 1]; |
| 373 | } |
| 374 | |
| 375 | // Light-space basis vectors for texel snapping |
| 376 | let up_hint = if d[1].abs() > 0.99 { |
| 377 | [1.0f32, 0.0, 0.0] |
| 378 | } else { |
| 379 | [0.0f32, 1.0, 0.0] |
| 380 | }; |
| 381 | let right = normalize3([ |
| 382 | up_hint[1] * d[2] - up_hint[2] * d[1], |
| 383 | up_hint[2] * d[0] - up_hint[0] * d[2], |
| 384 | up_hint[0] * d[1] - up_hint[1] * d[0], |
| 385 | ]); |
| 386 | let ortho_up = [ |
| 387 | d[1] * right[2] - d[2] * right[1], |
| 388 | d[2] * right[0] - d[0] * right[2], |
| 389 | d[0] * right[1] - d[1] * right[0], |
| 390 | ]; |
| 391 |
no test coverage detected