|
| 1 | +# View Matrix |
| 2 | + |
| 3 | +Integrating the view matrix will be quite simple and short. First, transformations for objects and cameras/views can be encapsulated into a single struct: |
| 4 | + |
| 5 | +```cpp |
| 6 | +struct Transform { |
| 7 | + glm::vec2 position{}; |
| 8 | + float rotation{}; |
| 9 | + glm::vec2 scale{1.0f}; |
| 10 | + |
| 11 | + [[nodiscard]] auto model_matrix() const -> glm::mat4; |
| 12 | + [[nodiscard]] auto view_matrix() const -> glm::mat4; |
| 13 | +}; |
| 14 | +``` |
| 15 | +
|
| 16 | +Extracting the common logic into a helper, both member functions can be implemented easily: |
| 17 | +
|
| 18 | +```cpp |
| 19 | +namespace { |
| 20 | +struct Matrices { |
| 21 | + glm::mat4 translation; |
| 22 | + glm::mat4 orientation; |
| 23 | + glm::mat4 scale; |
| 24 | +}; |
| 25 | +
|
| 26 | +[[nodiscard]] auto to_matrices(glm::vec2 const position, float rotation, |
| 27 | + glm::vec2 const scale) -> Matrices { |
| 28 | + static constexpr auto mat_v = glm::identity<glm::mat4>(); |
| 29 | + static constexpr auto axis_v = glm::vec3{0.0f, 0.0f, 1.0f}; |
| 30 | + return Matrices{ |
| 31 | + .translation = glm::translate(mat_v, glm::vec3{position, 0.0f}), |
| 32 | + .orientation = glm::rotate(mat_v, glm::radians(rotation), axis_v), |
| 33 | + .scale = glm::scale(mat_v, glm::vec3{scale, 1.0f}), |
| 34 | + }; |
| 35 | +} |
| 36 | +} // namespace |
| 37 | +
|
| 38 | +auto Transform::model_matrix() const -> glm::mat4 { |
| 39 | + auto const [t, r, s] = to_matrices(position, rotation, scale); |
| 40 | + // right to left: scale first, then rotate, then translate. |
| 41 | + return t * r * s; |
| 42 | +} |
| 43 | +
|
| 44 | +auto Transform::view_matrix() const -> glm::mat4 { |
| 45 | + // view matrix is the inverse of the model matrix. |
| 46 | + // instead, perform translation and rotation in reverse order and with |
| 47 | + // negative values. or, use glm::lookAt(). |
| 48 | + // scale is kept unchanged as the first transformation for |
| 49 | + // "intuitive" scaling on cameras. |
| 50 | + auto const [t, r, s] = to_matrices(-position, -rotation, scale); |
| 51 | + return r * t * s; |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +Add a `Transform` member to `App` to represent the view/camera, inspect its members, and combine with the existing projection matrix: |
| 56 | + |
| 57 | +```cpp |
| 58 | +Transform m_view_transform{}; |
| 59 | + |
| 60 | +// ... |
| 61 | +ImGui::Separator(); |
| 62 | +if (ImGui::TreeNode("View")) { |
| 63 | + ImGui::DragFloat2("position", &m_view_transform.position.x); |
| 64 | + ImGui::DragFloat("rotation", &m_view_transform.rotation); |
| 65 | + ImGui::DragFloat2("scale", &m_view_transform.scale.x); |
| 66 | + ImGui::TreePop(); |
| 67 | +} |
| 68 | + |
| 69 | +// ... |
| 70 | +auto const mat_view = m_view_transform.view_matrix(); |
| 71 | +auto const mat_vp = mat_projection * mat_view; |
| 72 | +auto const bytes = |
| 73 | + std::bit_cast<std::array<std::byte, sizeof(mat_vp)>>(mat_vp); |
| 74 | +m_view_ubo->write_at(m_frame_index, bytes); |
| 75 | +``` |
| 76 | +
|
| 77 | +Naturally, moving the view left moves everything else - currently only a single RGBY quad - to the _right_. |
| 78 | +
|
| 79 | + |
0 commit comments