Системы координат и пространства

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

Пространство (space) — система координат с собственным началом и осями, удобная для конкретного этапа рендеринга.

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

Один и тот же объект описывается по-разному в зависимости от точки зрения: относительно себя, относительно мира, относительно камеры. Перевод между пространствами — это умножение на матрицы, и именно из-за него возникают «модельная», «видовая» и «проекционная» матрицы. Путаница в пространствах — частый источник багов (объект не там, повёрнут не туда).

Цепочка пространств

Локальное (model space)      вершины относительно центра объекта
        | * матрица модели (Model)
        v
Мировое (world space)        объект размещён и повёрнут в сцене
        | * матрица вида (View)
        v
Видовое (view/camera space)  всё относительно камеры (камера в начале)
        | * матрица проекции (Projection)
        v
Клип-пространство (clip)      готово к отсечению и делению на w
        | деление на w + viewport
        v
Экранное (screen space)      координаты пикселей

Зачем столько пространств

  • Локальное: художник моделирует объект «вокруг нуля», не думая, где он окажется в сцене.
  • Мировое: один и тот же меш можно поставить в сцену сто раз в разных местах — меняется только матрица модели.
  • Видовое: удобно считать освещение и проекцию, когда камера в начале координат и смотрит вдоль оси.
  • Клип и экран: железо отсекает невидимое и переводит в пиксели.

Перенос точки в мировое пространство

Самый простой переход — перенос (translation): прибавить позицию объекта к локальной координате вершины. Покажем на векторах.

# Вершина в локальном пространстве + позиция объекта в мире
def to_world(local, obj_pos):
    return tuple(l + p for l, p in zip(local, obj_pos))

local_vertex = (0.0, 1.0, 0.0)   # верхушка модели относительно её центра
object_position = (10.0, 0.0, -5.0)  # где объект стоит в мире
world = to_world(local_vertex, object_position)
print("Локально:", local_vertex)
print("В мире:  ", world)

Вывод:

Локально: (0.0, 1.0, 0.0)
В мире:   (10.0, 1.0, -5.0)

Верхушка модели, заданная относительно своего центра, оказалась в нужном месте сцены простым переносом. Полная матрица модели добавляет ещё поворот и масштаб — об этом в разделе про матрицы.

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

На практике три матрицы (Model, View, Projection) перемножают в одну матрицу MVP и умножают на неё каждую вершину прямо в вершинном шейдере: gl_Position = MVP * vec4(position, 1.0). Камера в видовом пространстве считается стоящей в начале координат — на самом деле двигают весь мир в противоположную сторону, что эквивалентно движению камеры.

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

  • Считать освещение в неправильном пространстве (например, мировые нормали против видовых позиций) — свет ляжет криво.
  • Забыть порядок умножения матриц: он не коммутативен, P*V*M ≠ M*V*P.
  • Путать «камера движется вперёд» и «мир движется назад» — в видовом пространстве это одно и то же.

Итоги

  • Вершина проходит: локальное → мировое → видовое → клип → экранное пространство.
  • Каждый переход — умножение на матрицу (Model, View, Projection).
  • Локальное удобно для моделирования, видовое — для камеры и света.
  • MVP-матрица объединяет три преобразования в одно умножение.
Проверьте себя
1. В каком порядке вершина проходит пространства?
AЭкран → клип → мир → модель
BМодель → мир → вид → клип → экран
CВид → модель → экран → мир
DМир → экран → модель → клип
2. Зачем моделировать объект в локальном пространстве вокруг нуля?
AТак быстрее рисуется
BЧтобы один меш можно было разместить в сцене много раз через матрицу модели
CЧтобы не нужны были текстуры
DЭто требование GLSL
3. Что объединяет матрица MVP?
AЦвет, свет и тень
BModel, View и Projection в одно преобразование
CТри текстуры
DВершинный и фрагментный шейдеры