Матрицы преобразований: перенос, поворот, масштаб

Любое движение объекта в сцене — это перенос, поворот, масштаб или их комбинация, и всё это — матрицы.

Матрица преобразования — таблица чисел, умножение на которую перемещает, поворачивает или масштабирует координаты вершины.

Зачем это знать

Вместо того чтобы по отдельности пересчитывать каждую вершину, графика применяет одну матрицу ко всем сразу. GPU умеет умножать матрицы на векторы молниеносно. Понимая три базовых преобразования, вы понимаете, как объект оказывается в нужном месте, под нужным углом и нужного размера.

Масштаб

Масштаб умножает координаты на коэффициенты по осям. Диагональная матрица с (sx, sy, sz) растягивает или сжимает объект.

def scale(v, sx, sy, sz):
    x, y, z = v
    return (x * sx, y * sy, z * sz)

print("Увеличить вдвое:", scale((1, 1, 1), 2, 2, 2))
print("Сплющить по Y:  ", scale((1, 1, 1), 1, 0.5, 1))

Вывод:

Увеличить вдвое: (2, 2, 2)
Сплющить по Y:   (1, 0.5, 1)

Поворот

Поворот в плоскости задаётся синусами и косинусами угла. Поворот точки на угол θ вокруг начала координат в 2D:

x' = x*cos(θ) - y*sin(θ)
y' = x*sin(θ) + y*cos(θ)
import math

def rotate2d(x, y, deg):
    a = math.radians(deg)
    xr = x * math.cos(a) - y * math.sin(a)
    yr = x * math.sin(a) + y * math.cos(a)
    return round(xr, 3), round(yr, 3)

print("Точка (1,0) на 90 град:", rotate2d(1, 0, 90))
print("Точка (1,0) на 45 град:", rotate2d(1, 0, 45))
print("Точка (1,0) на 180 град:", rotate2d(1, 0, 180))

Вывод:

Точка (1,0) на 90 град: (0.0, 1.0)
Точка (1,0) на 45 град: (0.707, 0.707)
Точка (1,0) на 180 град: (-1.0, 0.0)

Точка (1,0) после поворота на 90° уехала в (0,1), на 180° — в (-1,0). Ровно как стрелка часов.

Перенос

Перенос (translation) сдвигает все точки на постоянный вектор. Как мы видели в уроке про однородные координаты, в 4×4 матрице сдвиг живёт в последнем столбце.

Как работает под капотом

GPU хранит преобразования как 4×4 матрицы и умножает их на однородные вершины. Поворот и масштаб не задевают компоненту w, перенос использует столбец переноса. Вершинный шейдер выполняет это умножение для каждой из миллионов вершин параллельно — поэтому матричная форма так выгодна.

Частые ошибки

  • Подавать угол в градусах туда, где ждут радианы (или наоборот) — объект повернётся «не на столько».
  • Неравномерный масштаб (разные sx, sy, sz) искажает нормали — их потом нужно править отдельной матрицей.
  • Считать, что поворот вокруг произвольной точки = простой поворот: сначала перенесите точку в начало координат.

Итоги

  • Три базовых преобразования: масштаб, поворот, перенос.
  • Поворот выражается через sin и cos угла.
  • В 4×4 матрице поворот и масштаб — в верхнем блоке 3×3, перенос — в последнем столбце.
  • GPU применяет матрицу ко всем вершинам параллельно.
Проверьте себя
1. Куда переедет точка (1,0) после поворота на 90° против часовой стрелки?
A(0,1)
B(-1,0)
C(0,-1)
D(1,1)
2. Что делает диагональная матрица масштаба с коэффициентами (2,2,2)?
AПоворачивает объект
BУвеличивает объект вдвое по всем осям
CСдвигает объект
DДелает его прозрачным
3. Почему перенос требует 4×4 матрицы?
AДля скорости
BСдвиг нельзя выразить умножением 3×3; нужна однородная координата w
CЧтобы хранить цвет
DТак короче код