Комбинирование преобразований

Реальный объект масштабируют, поворачивают и переносят — и порядок этих действий меняет результат.

Композиция преобразований — последовательное применение нескольких матриц, выражаемое их произведением; порядок умножения важен.

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

Матрицу модели обычно собирают как Перенос × Поворот × Масштаб. Перепутаете порядок — и объект окажется не там или искажённым. Матричное умножение некоммутативно: A×B≠B×A. Это самый частый источник «объект телепортировался».

Порядок имеет значение

Сравним два сценария над точкой (1,0): «сначала повернуть, потом сдвинуть» и «сначала сдвинуть, потом повернуть». Результаты разные.

import math

def rotate(x, y, deg):
    a = math.radians(deg)
    return (x*math.cos(a) - y*math.sin(a), x*math.sin(a) + y*math.cos(a))

def translate(x, y, dx, dy):
    return (x + dx, y + dy)

x, y = 1.0, 0.0
# Вариант A: повернуть на 90, затем сдвинуть на (2,0)
rx, ry = rotate(x, y, 90)
ax, ay = translate(rx, ry, 2, 0)
# Вариант B: сдвинуть на (2,0), затем повернуть на 90
tx, ty = translate(x, y, 2, 0)
bx, by = rotate(tx, ty, 90)
print("A (поворот->перенос):", (round(ax,2), round(ay,2)))
print("B (перенос->поворот):", (round(bx,2), round(by,2)))

Вывод:

A (поворот->перенос): (2.0, 1.0)
B (перенос->поворот): (0.0, 3.0)

Те же два действия, разный порядок — точки оказались в совершенно разных местах.

Канонический порядок: масштаб → поворот → перенос

Обычно объект сначала масштабируют (вокруг своего центра), затем поворачивают, затем переносят в мир. На уровне записи матриц это M = T * R * S, потому что матрицы применяются к вектору справа налево: сперва S, потом R, потом T.

Запись:  M = T * R * S
Действие на вектор v:  M*v = T*(R*(S*v))
                            масштаб ->|
                              поворот ->|
                                перенос ->|

Зачем сворачивать в одну матрицу

Если каждый кадр умножать вершину последовательно на S, R, T — это три умножения на вершину. Гораздо дешевле один раз перемножить T×R×S в матрицу M на процессоре и слать в шейдер готовую M: тогда на вершину остаётся одно умножение.

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

CPU собирает матрицу модели для каждого объекта, перемножает с View и Projection в MVP и передаёт её в вершинный шейдер как uniform. Шейдер делает единственное умножение MVP * vec4(pos, 1.0). Так миллионы вершин обрабатываются с минимумом работы на каждую.

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

  • Переставить местами поворот и перенос и удивляться, что объект вращается вокруг чужой точки.
  • Считать умножение матриц коммутативным — оно не коммутативно.
  • Применять неравномерный масштаб после поворота, искажая объект непредсказуемо.

Итоги

  • Умножение матриц некоммутативно: порядок меняет результат.
  • Канон: масштаб → поворот → перенос, в записи M = T*R*S.
  • Матрицы применяются к вектору справа налево.
  • Свёртка в одну матрицу экономит умножения на каждой вершине.
Проверьте себя
1. Верно ли, что A×B = B×A для матриц преобразований?
AДа, всегда
BНет, умножение матриц некоммутативно
CТолько для поворотов
DТолько для переносов
2. Что означает запись M = T * R * S при применении к вектору?
AСначала перенос, потом масштаб
BСначала масштаб, потом поворот, потом перенос
CВсе одновременно
DТолько поворот
3. Зачем перемножать S, R, T в одну матрицу на CPU?
AЧтобы хранить меньше вершин
BЧтобы на каждую вершину осталось одно умножение вместо трёх
CЧтобы включить тени
DЧтобы поменять цвет