raytracers/rtchallenge/src/transformations.rs

72 lines
2.2 KiB
Rust

use crate::{
matrices::Matrix4x4,
tuples::{cross, Tuple},
};
/// Create a matrix representing a eye at `from` looking at `to`, with an `up`
/// as the up vector.
pub fn view_transform(from: Tuple, to: Tuple, up: Tuple) -> Matrix4x4 {
let forward = (to - from).normalize();
let left = cross(forward, up.normalize());
let true_up = cross(left, forward);
Matrix4x4::new(
[left.x, left.y, left.z, 0.],
[true_up.x, true_up.y, true_up.z, 0.],
[-forward.x, -forward.y, -forward.z, 0.],
[0., 0., 0., 1.],
) * Matrix4x4::translation(-from.x, -from.y, -from.z)
}
#[cfg(test)]
mod tests {
use crate::{
matrices::{identity, scaling, translation, Matrix4x4},
transformations::view_transform,
tuples::{point, vector},
};
#[test]
fn default_orientation() {
// The transformation matrix for the default orientation.
let from = point(0., 0., 0.);
let to = point(0., 0., -1.);
let up = vector(0., 1., 0.);
let t = view_transform(from, to, up);
assert_eq!(t, identity());
}
#[test]
fn looking_positive_z() {
// A view transformation matrix looking in positive z direction.
let from = point(0., 0., 0.);
let to = point(0., 0., 1.);
let up = vector(0., 1., 0.);
let t = view_transform(from, to, up);
assert_eq!(t, scaling(-1., 1., -1.));
}
#[test]
fn transformation_moves_world() {
// The view transformation moves the world.
let from = point(0., 0., 8.);
let to = point(0., 0., 0.);
let up = vector(0., 1., 0.);
let t = view_transform(from, to, up);
assert_eq!(t, translation(0., 0., -8.));
}
#[test]
fn arbitrary_view() {
// An arbitrary view transformation.
let from = point(1., 3., 2.);
let to = point(4., -2., 8.);
let up = vector(1., 1., 0.);
let t = view_transform(from, to, up);
assert_eq!(
t,
Matrix4x4::new(
[-0.50709, 0.50709, 0.67612, -2.36643],
[0.76772, 0.60609, 0.12122, -2.82843],
[-0.35857, 0.59761, -0.71714, 0.],
[0., 0., 0., 1.],
)
);
}
}