EN-005 V2 — bake the aerial-perspective 3D LUT for the current camera + sun. Called each frame from `end_frame_with_scene` before scene_compose runs (which samples the LUT). The LUT depends on the camera transform, so unlike the sky-view LUT it can't be gated on a dirty flag — every frame's view is unique.
(&mut self)
| 8540 | /// so unlike the sky-view LUT it can't be gated on a dirty |
| 8541 | /// flag — every frame's view is unique. |
| 8542 | fn dispatch_aerial_perspective_lut(&mut self) { |
| 8543 | // Compute inverse VP from current view + projection. |
| 8544 | let v = self.current_view_matrix; |
| 8545 | let p = self.current_proj_matrix; |
| 8546 | // Multiply column-major: vp = p * v |
| 8547 | let mut vp = [[0.0_f32; 4]; 4]; |
| 8548 | for r in 0..4 { |
| 8549 | for c in 0..4 { |
| 8550 | let mut s = 0.0; |
| 8551 | for k in 0..4 { |
| 8552 | s += p[k][r] * v[c][k]; |
| 8553 | } |
| 8554 | vp[c][r] = s; |
| 8555 | } |
| 8556 | } |
| 8557 | let inv_vp = util::mat4_invert(vp); |
| 8558 | |
| 8559 | // Camera world position from the view matrix. View is |
| 8560 | // world→camera, so cam_pos = -R^T * t where R is rotation, |
| 8561 | // t is the translation column. Equivalent: invert view and |
| 8562 | // take its translation column. |
| 8563 | let inv_view = util::mat4_invert(v); |
| 8564 | let cam_pos = [inv_view[3][0], inv_view[3][1], inv_view[3][2]]; |
| 8565 | |
| 8566 | let params = AerialParams { |
| 8567 | cam_pos: [cam_pos[0], cam_pos[1], cam_pos[2], AERIAL_MAX_DIST_KM], |
| 8568 | inv_vp, |
| 8569 | sun: [ |
| 8570 | self.sun_direction[0], |
| 8571 | self.sun_direction[1], |
| 8572 | self.sun_direction[2], |
| 8573 | self.sun_intensity, |
| 8574 | ], |
| 8575 | knobs: [self.rayleigh_density, self.mie_density, 0.0, 0.0], |
| 8576 | }; |
| 8577 | self.queue |
| 8578 | .write_buffer(&self.aerial_perspective_uniform_buffer, 0, bytemuck::bytes_of(¶ms)); |
| 8579 | |
| 8580 | let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { |
| 8581 | label: Some("aerial_perspective_encoder"), |
| 8582 | }); |
| 8583 | { |
| 8584 | let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { |
| 8585 | label: Some("aerial_perspective_compute"), |
| 8586 | timestamp_writes: None, |
| 8587 | }); |
| 8588 | pass.set_pipeline(&self.aerial_perspective_pipeline); |
| 8589 | pass.set_bind_group(0, &self.aerial_perspective_bind_group, &[]); |
| 8590 | let gx = (AERIAL_W + 7) / 8; |
| 8591 | let gy = (AERIAL_H + 7) / 8; |
| 8592 | pass.dispatch_workgroups(gx, gy, AERIAL_D); |
| 8593 | } |
| 8594 | self.queue.submit(std::iter::once(encoder.finish())); |
| 8595 | } |
| 8596 | |
| 8597 | /// EN-005 Phase 4 — derive a sky-tinted fog color from the |
| 8598 | /// atmosphere. Approximates "what color would you see looking at |
no test coverage detected