Векторы и матрицы
Что уже должно быть понятно:
- WGSL: типы
vec2,vec3,vec4,mat4x4 - clip space и координаты от -1 до 1
Что появится в этой главе:
- векторы и операции с ними
- матрицы и умножение матрицы на вектор
- крейт
glamдля работы с математикой в Rust
Итог: теоретическая глава — визуального результата нет, но без этой базы невозможна следующая глава про MVP-трансформации
Эта глава — единственная в руководстве без визуального результата. Она даёт ровно тот минимум математики, который понадобится для следующей главы, где мы начнём двигать и вращать объекты в 3D. Если вы уже знакомы с векторами и матрицами — смело пропускайте.
Векторы
Вектор — это набор чисел. В графике используются двумерные (vec2), трёхмерные (vec3) и четырёхмерные (vec4) векторы. Они представляют позиции, направления, цвета и другие данные.
use glam::Vec3;
let a = Vec3::new(1.0, 2.0, 3.0);
let b = Vec3::new(4.0, 5.0, 6.0);Сложение и вычитание
Поэлементно:
Сложение векторов — это смещение: если точка находится на расстоянии
Умножение на скаляр
Каждая компонента умножается на число:
Длина и нормализация
Длина вектора:
Нормализация — вектор единичной длины, сохраняющий направление:
Нормализованные векторы используются для направлений (нормали, лучи света, направление камеры). Длина всегда 1.
let len = a.length();
let dir = a.normalize();Скалярное произведение (dot product)
Результат — одно число:
Два важных свойства:
— векторы «смотрят» в одну сторону — векторы перпендикулярны — векторы «смотрят» в противоположные стороны
В графике dot product используется для расчёта освещения (см. Нормали и базовый свет): насколько поверхность повёрнута к источнику света.
let d = a.dot(b);Векторное произведение (cross product)
Результат — новый вектор, перпендикулярный обоим исходным:
Используется для построения систем координат и вычисления нормалей к поверхности. В этом руководстве мы встретимся с ним при backface culling (GPU вычисляет cross product для определения стороны грани) и при построении TBN-матрицы для normal mapping.
let c = a.cross(b);Матрицы
Матрица — прямоугольная таблица чисел. В графике почти всегда используются матрицы 4×4 (mat4x4<f32> в WGSL, Mat4 в glam). Они описывают трансформации: сдвиг, поворот, масштаб, проекцию.
Умножение матрицы на вектор трансформирует этот вектор — сдвигает, поворачивает или проецирует:
В WGSL это записывается напрямую:
let m: mat4x4<f32> = ...;
let pos: vec4<f32> = vec4<f32>(x, y, z, 1.0);
let result = m * pos;Умножение матриц
Матрицы можно перемножать — результатом будет матрица, описывающая последовательное применение обеих трансформаций:
Порядок важен:
use glam::Mat4;
let model = Mat4::from_translation(Vec3::new(1.0, 0.0, 0.0));
let view = Mat4::look_at_rh(eye, target, up);
let projection = Mat4::perspective_rh(fov, aspect, near, far);
let mvp = projection * view * model;Типы трансформаций
Единичная матрица (identity) не изменяет вектор — умножение на неё даёт тот же результат:
let identity = Mat4::IDENTITY;Четыре базовых трансформации, которые мы будем использовать:
| Трансформация | Что делает | glam |
|---|---|---|
| Сдвиг (translation) | Перемещает объект | Mat4::from_translation(v) |
| Поворот (rotation) | Вращает вокруг оси | Mat4::from_axis_angle(axis, angle) |
| Масштаб (scale) | Увеличивает/уменьшает | Mat4::from_scale(v) |
| Проекция (projection) | Проецирует 3D → 2D | Mat4::perspective_rh(...) / Mat4::orthographic_rh(...) |
Любая комбинация сдвигов, поворотов и масштабов — это одна матрица 4×4. GPU умножает её на каждую вершину за одну операцию.
Зачем четвёртая компонента (w)
Вершины в 3D — это vec3. Но матрицы 4×4 требуют vec4. Четвёртая компонента
— обычная позиция (точка в пространстве) — направление (вектор, не подверженный сдвигу)
После умножения на матрицу проекции
glam
Для работы с векторами и матрицами в Rust используется крейт glam. Он уже есть в workspace-зависимостях с нужными фичами:
use glam::{Vec2, Vec3, Vec4, Mat4};Соответствие типов Rust и WGSL:
| Rust | WGSL | Назначение |
|---|---|---|
Vec2 | vec2<f32> | 2D-позиция, UV-координаты |
Vec3 | vec3<f32> | 3D-позиция, направление, цвет |
Vec4 | vec4<f32> | Однородные координаты |
Mat4 | mat4x4<f32> | Трансформации |
glam реализует encase::ShaderType для Mat4, Vec3 и других типов — их можно напрямую передавать в uniform-буферы без ручной сериализации.
Типичные ошибки
Ненормализованные направления
Направления (нормали, лучи света) должны быть единичной длины — normalize() обязателен. Если передать ненормализованный вектор в dot(n, light_dir), результат будет зависеть от длины вектора, а не только от угла, и освещение будет некорректным.
Порядок умножения матриц
Умножение матриц некоммутативно: projection * view * model — сначала model, потом view, потом projection. Перепутать порядок — получить перевёрнутую или искажённую картинку.
Путаница между dot и cross product
Скалярное произведение (dot) возвращает число — косинус угла между векторами. Векторное произведение (cross) возвращает вектор, перпендикулярный обоим исходным. Они решают разные задачи: dot — «насколько векторы сонаправлены», cross — «найти перпендикуляр».
Попробуйте сами
Упражнение 1
Проверьте на бумаге: Vec3::new(1.0, 2.0, 3.0).dot(Vec3::new(4.0, 5.0, 6.0)) должно быть 32. Затем вычислите в Rust и сравните результат.
Упражнение 2
Создайте матрицу сдвига Mat4::from_translation(Vec3::new(2.0, 0.0, 0.0)) и умножьте на Vec4::new(0.0, 0.0, 0.0, 1.0). Убедитесь, что результат — (2.0, 0.0, 0.0, 1.0).
Упражнение 3
Проверьте, что cross product (1, 0, 0) × (0, 1, 0) даёт (0, 0, 1), а затем убедитесь через dot, что результат перпендикулярен обоим исходным векторам (dot = 0).
Этого минимума достаточно для следующей главы, где мы применим матрицы на практике: сдвинем прямоугольник, повернём его и в конечном счёте построим вращающийся куб.