diff --git a/raytracing/src/matrix.cpp b/raytracing/src/matrix.cpp index 53011aa..1cf946a 100644 --- a/raytracing/src/matrix.cpp +++ b/raytracing/src/matrix.cpp @@ -397,6 +397,24 @@ Matrix Matrix::shearing(double a_xy, double a_xz, double a_yx, double a_yz, doub /* ------------------------------------------------------------------------- */ +Matrix Matrix::view_transform(Tuple a_from, Tuple a_to, Tuple an_up) +{ + Tuple the_forward = (a_to - a_from).normalize(); + Tuple the_up_normalized = an_up.normalize(); + Tuple the_left = the_forward.cross(the_up_normalized); + Tuple the_true_up = the_left.cross(the_forward); + Matrix the_orientation = { + { the_left.x(), the_left.y(), the_left.z(), 0}, + { the_true_up.x(), the_true_up.y(), the_true_up.z(), 0}, + {-the_forward.x(), -the_forward.y(), -the_forward.z(), 0}, + { 0, 0, 0, 1} + }; + + return the_orientation * translation(-a_from.x(), -a_from.y(), -a_from.z()); +} + +/* ------------------------------------------------------------------------- */ + bool Matrix::validate_dimensions(const std::initializer_list> &a_values) const { for (const auto &the_row : a_values) diff --git a/raytracing/src/matrix.h b/raytracing/src/matrix.h index b142cdc..71bb8ce 100644 --- a/raytracing/src/matrix.h +++ b/raytracing/src/matrix.h @@ -77,6 +77,7 @@ namespace Raytracer static Matrix rotation_y(double a_radians); static Matrix rotation_z(double a_radians); static Matrix shearing(double a_xy, double a_xz, double a_yx, double a_yz, double a_zx, double a_zy); + static Matrix view_transform(Tuple a_from, Tuple a_to, Tuple an_up); private: bool validate_dimensions(const std::initializer_list> &a_values) const; diff --git a/tests/07_making_scene.cpp b/tests/07_making_scene.cpp index 82e2588..d0a17d2 100644 --- a/tests/07_making_scene.cpp +++ b/tests/07_making_scene.cpp @@ -390,3 +390,116 @@ SCENARIO("The color with an intersection behind the ray", "[features/world.featu } } } + +/* ------------------------------------------------------------------------- */ + +SCENARIO("The transformation matrix for the default orientation", "[features/transformations.feature]") +{ + GIVEN("from <- point(0, 0, 0)") + { + Tuple from = Tuple::Point(0, 0, 0); + AND_GIVEN("to <- point(0, 0, -1)") + { + Tuple to = Tuple::Point(0, 0, -1); + AND_GIVEN("up <- vector(0, 1, 0)") + { + Tuple up = Tuple::Vector(0, 1, 0); + WHEN("t <- view_transform(from, to, up)") + { + Matrix t = Matrix::view_transform(from, to, up); + THEN("t = identify_matrix") + { + REQUIRE(t == Matrix::identity()); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("A view transformation matrix looking in positive z direction", "[features/transformations.feature]") +{ + GIVEN("from <- point(0, 0, 0)") + { + Tuple from = Tuple::Point(0, 0, 0); + AND_GIVEN("to <- point(0, 0, 1)") + { + Tuple to = Tuple::Point(0, 0, 1); + AND_GIVEN("up <- vector(0, 1, 0)") + { + Tuple up = Tuple::Vector(0, 1, 0); + WHEN("t <- view_transform(from, to, up)") + { + Matrix t = Matrix::view_transform(from, to, up); + THEN("t = scaling(-1, 1, -1)") + { + REQUIRE(t == Matrix::scaling(-1, 1, -1)); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("A view transformation moves the world", "[features/transformations.feature]") +{ + GIVEN("from <- point(0, 0, 8)") + { + Tuple from = Tuple::Point(0, 0, 8); + AND_GIVEN("to <- point(0, 0, 0)") + { + Tuple to = Tuple::Point(0, 0, 0); + AND_GIVEN("up <- vector(0, 1, 0)") + { + Tuple up = Tuple::Vector(0, 1, 0); + WHEN("t <- view_transform(from, to, up)") + { + Matrix t = Matrix::view_transform(from, to, up); + THEN("t = translation(0, 0, -8)") + { + REQUIRE(t == Matrix::translation(0, 0, -8)); + } + } + } + } + } +} + +/* ------------------------------------------------------------------------- */ + +SCENARIO("An arbitrary view transformation", "[features/transformations.feature]") +{ + GIVEN("from <- point(1, 3, 2)") + { + Tuple from = Tuple::Point(1, 3, 2); + AND_GIVEN("to <- point(4, -2, 8)") + { + Tuple to = Tuple::Point(4, -2, 8); + AND_GIVEN("up <- vector(1, 1, 0)") + { + Tuple up = Tuple::Vector(1, 1, 0); + WHEN("t <- view_transform(from, to, up)") + { + Matrix t = Matrix::view_transform(from, to, up); + THEN("t iq the following 4x4 matrix") + { + // | -0.50709 | 0.50709 | 0.67612 | -2.36643 | + // | 0.76772 | 0.60609 | 0.12122 | -2.82843 | + // | -0.35857 | 0.59761 | -0.71714 | 0.00000 | + // | 0.00000 | 0.00000 | 0.00000 | 1.00000 | + REQUIRE(t == Matrix({ + {-0.50709, 0.50709, 0.67612, -2.36643}, + { 0.76772, 0.60609, 0.12122, -2.82843}, + {-0.35857, 0.59761, -0.71714, 0.00000}, + { 0.00000, 0.00000, 0.00000, 1.00000} + })); + } + } + } + } + } +}