Skip to content

Векторы и матрицы

Что уже должно быть понятно:

  • WGSL: типы vec2, vec3, vec4, mat4x4
  • clip space и координаты от -1 до 1

Что появится в этой главе:

  • векторы и операции с ними
  • матрицы и умножение матрицы на вектор
  • крейт glam для работы с математикой в Rust

Итог: теоретическая глава — визуального результата нет, но без этой базы невозможна следующая глава про MVP-трансформации


Эта глава — единственная в руководстве без визуального результата. Она даёт ровно тот минимум математики, который понадобится для следующей главы, где мы начнём двигать и вращать объекты в 3D. Если вы уже знакомы с векторами и матрицами — смело пропускайте.

Векторы

Вектор — это набор чисел. В графике используются двумерные (vec2), трёхмерные (vec3) и четырёхмерные (vec4) векторы. Они представляют позиции, направления, цвета и другие данные.

rust
use glam::Vec3;

let a = Vec3::new(1.0, 2.0, 3.0);
let b = Vec3::new(4.0, 5.0, 6.0);

Сложение и вычитание

Поэлементно:

(1,2,3)+(4,5,6)=(5,7,9)(4,5,6)(1,2,3)=(3,3,3)

Сложение векторов — это смещение: если точка находится на расстоянии a от начала координат, а мы добавляем b, то получаем новое положение a+b.

Векторное сложение: правило треугольника

Умножение на скаляр

Каждая компонента умножается на число:

2(1,2,3)=(2,4,6)

Длина и нормализация

Длина вектора:

|(3,4,0)|=32+42+02=5

Нормализация — вектор единичной длины, сохраняющий направление:

normalize(3,4,0)=(35, 45, 0)

Нормализованные векторы используются для направлений (нормали, лучи света, направление камеры). Длина всегда 1.

rust
let len = a.length();
let dir = a.normalize();

Скалярное произведение (dot product)

Результат — одно число:

ab=a1b1+a2b2+a3b3

Два важных свойства:

  • ab>0 — векторы «смотрят» в одну сторону
  • ab=0 — векторы перпендикулярны
  • ab<0 — векторы «смотрят» в противоположные стороны

В графике dot product используется для расчёта освещения (см. Нормали и базовый свет): насколько поверхность повёрнута к источнику света.

rust
let d = a.dot(b);

Векторное произведение (cross product)

Результат — новый вектор, перпендикулярный обоим исходным:

(1,0,0)×(0,1,0)=(0,0,1)

Используется для построения систем координат и вычисления нормалей к поверхности. В этом руководстве мы встретимся с ним при backface culling (GPU вычисляет cross product для определения стороны грани) и при построении TBN-матрицы для normal mapping.

rust
let c = a.cross(b);

Матрицы

Матрица — прямоугольная таблица чисел. В графике почти всегда используются матрицы 4×4 (mat4x4<f32> в WGSL, Mat4 в glam). Они описывают трансформации: сдвиг, поворот, масштаб, проекцию.

Умножение матрицы на вектор трансформирует этот вектор — сдвигает, поворачивает или проецирует:

Умножение матрицы 4×4 на векторp=Mp

В WGSL это записывается напрямую:

wgsl
let m: mat4x4<f32> = ...;
let pos: vec4<f32> = vec4<f32>(x, y, z, 1.0);
let result = m * pos;

Умножение матриц

Матрицы можно перемножать — результатом будет матрица, описывающая последовательное применение обеих трансформаций:

Mmvp=MprojectionMviewMmodel

Порядок важен: ABBA. В графике матрицы применяются справа налево — сначала Mmodel, потом Mview, потом Mprojection.

rust
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) не изменяет вектор — умножение на неё даёт тот же результат:

Iv=v
rust
let identity = Mat4::IDENTITY;

Четыре базовых трансформации, которые мы будем использовать:

ТрансформацияЧто делаетglam
Сдвиг (translation)Перемещает объектMat4::from_translation(v)
Поворот (rotation)Вращает вокруг осиMat4::from_axis_angle(axis, angle)
Масштаб (scale)Увеличивает/уменьшаетMat4::from_scale(v)
Проекция (projection)Проецирует 3D → 2DMat4::perspective_rh(...) / Mat4::orthographic_rh(...)

Любая комбинация сдвигов, поворотов и масштабов — это одна матрица 4×4. GPU умножает её на каждую вершину за одну операцию.

Зачем четвёртая компонента (w)

Вершины в 3D — это vec3. Но матрицы 4×4 требуют vec4. Четвёртая компонента w нужна для перспективного деления:

  • w=1 — обычная позиция (точка в пространстве)
  • w=0 — направление (вектор, не подверженный сдвигу)

После умножения на матрицу проекции w становится пропорциональным глубине. GPU делит x/w, y/w, z/w — так далёкие объекты выглядят меньше.

glam

Для работы с векторами и матрицами в Rust используется крейт glam. Он уже есть в workspace-зависимостях с нужными фичами:

rust
use glam::{Vec2, Vec3, Vec4, Mat4};

Соответствие типов Rust и WGSL:

RustWGSLНазначение
Vec2vec2<f32>2D-позиция, UV-координаты
Vec3vec3<f32>3D-позиция, направление, цвет
Vec4vec4<f32>Однородные координаты
Mat4mat4x4<f32>Трансформации

glam реализует encase::ShaderType для Mat4, Vec3 и других типов — их можно напрямую передавать в uniform-буферы без ручной сериализации.

Типичные ошибки

Ненормализованные направления

Направления (нормали, лучи света) должны быть единичной длины — normalize() обязателен. Если передать ненормализованный вектор в dot(n, light_dir), результат будет зависеть от длины вектора, а не только от угла, и освещение будет некорректным.

Порядок умножения матриц

Умножение матриц некоммутативно: ABBA. В графике матрицы применяются справа налево: 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).


Этого минимума достаточно для следующей главы, где мы применим матрицы на практике: сдвинем прямоугольник, повернём его и в конечном счёте построим вращающийся куб.

Опубликовано под лицензией CC-BY-4.0