Skip to content

Commit 1e78e49

Browse files
committed
Add Transform, view matrix
1 parent 21c56cd commit 1e78e49

File tree

7 files changed

+147
-2
lines changed

7 files changed

+147
-2
lines changed

guide/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@
4747
- [Pipeline Layout](descriptor_sets/pipeline_layout.md)
4848
- [Shader Buffer](descriptor_sets/shader_buffer.md)
4949
- [Texture](descriptor_sets/texture.md)
50+
- [View Matrix](descriptor_sets/view_matrix.md)
+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
![View Matrix](./view_matrix.png)
27.1 KB
Loading

src/app.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,14 @@ void App::inspect() {
563563
ImGui::DragFloat("line width", &m_shader->line_width, 0.25f,
564564
line_width_range[0], line_width_range[1]);
565565
}
566+
567+
ImGui::Separator();
568+
if (ImGui::TreeNode("View")) {
569+
ImGui::DragFloat2("position", &m_view_transform.position.x);
570+
ImGui::DragFloat("rotation", &m_view_transform.rotation);
571+
ImGui::DragFloat2("scale", &m_view_transform.scale.x, 0.1f);
572+
ImGui::TreePop();
573+
}
566574
}
567575
ImGui::End();
568576
}
@@ -571,9 +579,10 @@ void App::update_view() {
571579
auto const half_size = 0.5f * glm::vec2{m_framebuffer_size};
572580
auto const mat_projection =
573581
glm::ortho(-half_size.x, half_size.x, -half_size.y, half_size.y);
582+
auto const mat_view = m_view_transform.view_matrix();
583+
auto const mat_vp = mat_projection * mat_view;
574584
auto const bytes =
575-
std::bit_cast<std::array<std::byte, sizeof(mat_projection)>>(
576-
mat_projection);
585+
std::bit_cast<std::array<std::byte, sizeof(mat_vp)>>(mat_vp);
577586
m_view_ubo->write_at(m_frame_index, bytes);
578587
}
579588

src/app.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <shader_program.hpp>
99
#include <swapchain.hpp>
1010
#include <texture.hpp>
11+
#include <transform.hpp>
1112
#include <vma.hpp>
1213
#include <window.hpp>
1314
#include <filesystem>
@@ -107,6 +108,8 @@ class App {
107108
std::optional<RenderTarget> m_render_target{};
108109
bool m_wireframe{};
109110

111+
Transform m_view_transform{};
112+
110113
// waiter must be the last member to ensure it blocks until device is idle
111114
// before other members get destroyed.
112115
ScopedWaiter m_waiter{};

src/transform.cpp

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include <glm/gtc/matrix_transform.hpp>
2+
#include <transform.hpp>
3+
4+
namespace lvk {
5+
namespace {
6+
struct Matrices {
7+
glm::mat4 translation;
8+
glm::mat4 orientation;
9+
glm::mat4 scale;
10+
};
11+
12+
[[nodiscard]] auto to_matrices(glm::vec2 const position, float rotation,
13+
glm::vec2 const scale) -> Matrices {
14+
static constexpr auto mat_v = glm::identity<glm::mat4>();
15+
static constexpr auto axis_v = glm::vec3{0.0f, 0.0f, 1.0f};
16+
return Matrices{
17+
.translation = glm::translate(mat_v, glm::vec3{position, 0.0f}),
18+
.orientation = glm::rotate(mat_v, glm::radians(rotation), axis_v),
19+
.scale = glm::scale(mat_v, glm::vec3{scale, 1.0f}),
20+
};
21+
}
22+
} // namespace
23+
24+
auto Transform::model_matrix() const -> glm::mat4 {
25+
auto const [t, r, s] = to_matrices(position, rotation, scale);
26+
// right to left: scale first, then rotate, then translate.
27+
return t * r * s;
28+
}
29+
30+
auto Transform::view_matrix() const -> glm::mat4 {
31+
// view matrix is the inverse of the model matrix.
32+
// instead, perform translation and rotation in reverse order and with
33+
// negative values. or, use glm::lookAt().
34+
// scale is kept unchanged as the first transformation for
35+
// "intuitive" scaling on cameras.
36+
auto const [t, r, s] = to_matrices(-position, -rotation, scale);
37+
return r * t * s;
38+
}
39+
} // namespace lvk

src/transform.hpp

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#pragma once
2+
#include <glm/mat4x4.hpp>
3+
#include <glm/vec2.hpp>
4+
5+
namespace lvk {
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+
} // namespace lvk

0 commit comments

Comments
 (0)