unify perspective/orthogonal cameras
This commit is contained in:
parent
5c6ef7c758
commit
8c0ae03add
|
|
@ -16,6 +16,39 @@ pub(crate) fn projection(fovy: f32, aspect: f32, base_distance: f32) -> Mat4 {
|
|||
Mat4::perspective_rh(fovy, aspect, 0.1 * base_distance, 10.0 * base_distance)
|
||||
}
|
||||
|
||||
pub(crate) struct Camera {
|
||||
pub y_frac: f32,
|
||||
pub y_angle: f32,
|
||||
pub y_range: f32,
|
||||
pub z_min: f32,
|
||||
pub z_max: f32,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn clamp_z_min(&mut self, min_z: f32) -> &mut Self {
|
||||
let (s, c) = (0.5 * self.y_angle).sin_cos();
|
||||
// let distance = 0.5 * self.y_len * c / s;
|
||||
let distance_scaled = 0.5 * self.y_range * c;
|
||||
if distance_scaled + self.z_min * s < min_z * s {
|
||||
self.z_min = min_z - distance_scaled / s;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn to_mat4(&self) -> Mat4 {
|
||||
let tan_fov = (0.5 * self.y_angle).tan();
|
||||
let z_range = self.z_max - self.z_min;
|
||||
let y_far = 0.5 * self.y_range + self.z_max * tan_fov;
|
||||
let u = -y_far / z_range;
|
||||
Mat4::from_cols_array_2d(&[
|
||||
[self.y_frac, 0., 0., 0.],
|
||||
[0., 1., 0., 0.],
|
||||
[0., 0., u, -tan_fov],
|
||||
[0., 0., u * self.z_min, 0.5 * self.y_range],
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -76,4 +109,33 @@ mod tests {
|
|||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_proj() {
|
||||
for (y_range, y_angle_deg) in [
|
||||
(10.000000, 53.130102f32),
|
||||
(11.547005, 60.000000f32),
|
||||
(20.000000, 90.000000f32),
|
||||
(34.641016, 120.000000f32),
|
||||
(40.000000, 126.869898f32),
|
||||
] {
|
||||
let y_angle = y_angle_deg.to_radians();
|
||||
let camera = Camera {
|
||||
y_frac: 0.5,
|
||||
y_angle,
|
||||
y_range,
|
||||
z_min: -9.0,
|
||||
z_max: 90.0,
|
||||
};
|
||||
let actual = camera.to_mat4();
|
||||
let expected = projection(y_angle, 1.0 / camera.y_frac, 10.0)
|
||||
* Affine3A::from_translation(vec3(0., 0., -10.0));
|
||||
let c = expected.x_axis.x / actual.x_axis.x;
|
||||
assert!(
|
||||
(c * actual).abs_diff_eq(expected, 1e-3),
|
||||
"projection at {y_angle_deg}d is wrong\nexpected: {expected}\nexpected: {}\ncomputed: {actual}",
|
||||
expected / c,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
17
src/lib.rs
17
src/lib.rs
|
|
@ -108,15 +108,22 @@ impl Core {
|
|||
}
|
||||
|
||||
fn render(&self, output: &wgpu::Texture, args: &RedrawArgs) {
|
||||
let aspect = {
|
||||
let y_frac = {
|
||||
let size = output.size();
|
||||
let w = size.width as f32;
|
||||
let h = size.height as f32;
|
||||
w / h
|
||||
h / w
|
||||
};
|
||||
let distance = 0.5 * args.cam_view_y / (0.5 * args.cam_view_angle).tan();
|
||||
let view = Mat4::from(camera::view(args.cam_yaw, args.cam_pitch, distance));
|
||||
let proj = camera::projection(args.cam_view_angle, aspect, distance);
|
||||
let proj = camera::Camera {
|
||||
y_frac,
|
||||
y_angle: args.cam_view_angle,
|
||||
y_range: args.cam_view_y,
|
||||
z_min: -0.5 * args.cam_view_y,
|
||||
z_max: 0.5 * args.cam_view_y,
|
||||
}
|
||||
.clamp_z_min(0.01 * args.cam_view_y)
|
||||
.to_mat4();
|
||||
let view = Mat4::from_mat3(camera::rotation(-args.cam_yaw, -args.cam_pitch));
|
||||
self.mesh_pipeline.set_params(
|
||||
&self.queue,
|
||||
render::mesh::Params {
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@
|
|||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>15.000000000000000</double>
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>150.000000000000000</double>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user