feat(render): add relative-to-eye uniform fields (inert)

Append a rotation-only view-projection plus the eye split into high/low
f32 (double-single) to the shared Uniforms buffer, and the matching
Camera helpers (view_proj_rte, eye_high_low). Fields are added at the end
so existing field offsets — and every shader still reading only the
legacy view_proj — are unaffected. No shader consumes them yet; the wire
pipeline migrates first in the next step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-23 18:28:52 +03:00
1 parent 091b8e1805
commit 8f16ba1ac2
2 files changed
+61

No files matched your search

+19
View File
@@ -26,6 +26,19 @@ pub struct Uniforms {
/// Pads the struct to 112 B (next multiple of 16) so wgpu's uniform
/// alignment rules are satisfied.
pub _pad: [f32; 2],
// ── Relative-to-eye (double-single) additions ───────────────────────────
// Appended at the end so existing field offsets are unchanged; shaders that
// still read only the legacy fields keep working. Pipelines migrate to RTE
// one at a time. `view_rot` is the rotation-only view-projection; vertices
// pre-subtract the eye (via `eye_high`/`eye_low`, two f32 emulating f64) so
// the large eye translation never enters the f32 matrix → no large-coord
// jitter.
pub view_rot: glam::Mat4,
pub eye_high: [f32; 3],
pub _pad_eh: f32,
pub eye_low: [f32; 3],
pub _pad_el: f32,
}
impl Uniforms {
@@ -36,6 +49,7 @@ impl Uniforms {
} else {
0.0
};
let (eye_high, eye_low) = camera.eye_high_low();
Self {
view_proj: camera.view_proj(bounds),
camera_pos: camera.position_vec4(),
@@ -45,6 +59,11 @@ impl Uniforms {
flat_shade: 0.0,
transparency_enable: 1.0,
_pad: [0.0; 2],
view_rot: camera.view_proj_rte(bounds),
eye_high,
_pad_eh: 0.0,
eye_low,
_pad_el: 0.0,
}
}
}
+42
View File
@@ -113,6 +113,48 @@ impl Camera {
OPENGL_TO_WGPU * proj * view
}
/// Relative-to-eye view-projection: identical projection, but the view
/// matrix carries rotation only (translation zeroed). Positions fed to it
/// must already be expressed relative to the eye (done per-vertex with
/// double-single precision in the shader), so the large eye translation
/// never enters the f32 matrix and large-coordinate jitter disappears.
#[allow(dead_code)] // consumed by the RTE uniform / shaders (next phase)
pub fn view_proj_rte(&self, bounds: Rectangle) -> Mat4 {
let near = self.distance * 0.001;
let far = self.distance * 1000.0;
let aspect = bounds.width / bounds.height;
let up_dir = self.rotation * Vec3::Y;
let mut view = Mat4::look_at_rh(self.eye(), self.target.as_vec3(), up_dir);
// Zero the translation column → pure rotation (world→view basis).
view.w_axis = glam::vec4(0.0, 0.0, 0.0, 1.0);
let proj = match self.projection {
Projection::Perspective => Mat4::perspective_rh(self.fov_y, aspect, near, far),
Projection::Orthographic => {
let h = self.ortho_size();
let w = h * aspect;
Mat4::orthographic_rh(-w, w, -h, h, near, far)
}
};
OPENGL_TO_WGPU * proj * view
}
/// Eye position split into two f32 (high + low) emulating f64, for the
/// double-single relative-to-eye shaders. `high + low ≈ eye` to ~f64
/// precision; the shader subtracts these from each vertex's own high/low.
#[allow(dead_code)] // consumed by the RTE uniform (next phase)
pub fn eye_high_low(&self) -> ([f32; 3], [f32; 3]) {
let e = self.eye_f64();
let high = [e.x as f32, e.y as f32, e.z as f32];
let low = [
(e.x - high[0] as f64) as f32,
(e.y - high[1] as f64) as f32,
(e.z - high[2] as f64) as f32,
];
(high, low)
}
/// Project a screen point onto an arbitrary world-space plane.
///
/// The plane is defined by `plane_normal` (unit vector) and a `plane_point`