Компьютерная графика

Координатные пространства

2011 год. Battlefield 3 - первый AAA-шутер на Frostbite 2. Художники жалуются: персонажи деформируются при масштабировании. Баг занял неделю дебаггинга. Причина - нормали трансформировались не той матрицей. Не Model Matrix, а её обратно-транспонированной. Пять пространств, четыре матрицы, и одна перепутанная - сцена рассыпается.

  • **Vertex Shader в GPU:** первая стадия graphics pipeline. Его единственная обязательная задача - трансформировать вершину из Model Space в Clip Space через MVP
  • **Camera systems в играх:** FPS-камера, orbit camera, cinematic camera - всё это разные способы построить View Matrix
  • **VR-рендеринг:** два глаза = две View Matrix + две Projection Matrix с разным offset. Удвоение vertex pipeline

Катмулл, SGI и рождение графического pipeline

В 1974 году Эдвин Катмулл описал концепцию последовательного преобразования вершин через пространства в своей PhD-диссертации. SGI (Silicon Graphics) в 1982 аппаратно реализовала этот pipeline в рабочих станциях IRIS. В 1992 OpenGL стандартизировал именно этот порядок: Model -> World -> View -> Clip -> NDC. Каждый современный GPU - прямой наследник той архитектуры.

Предварительные знания

  • Linear Algebra for Graphics

Model Space (локальные координаты)

3D-художник лепит персонажа. Голова - в центре, руки - по бокам, ноги - внизу. Все координаты вершин заданы **относительно центра модели**. Это **Model Space** - собственная система координат объекта. Паспорт формы.

**Model Space** (Object Space, Local Space) - координатная система объекта. Центр (0,0,0) обычно в геометрическом центре или у основания. Все вершины 3D-модели хранятся в model space.

Разделение формы (model space) и размещения (model matrix) - выразительная абстракция. Один и тот же куб можно разместить в сцене 100 раз с разными позициями, поворотами и масштабами - меняется только model matrix, вершины остаются теми же. На этом работает GPU instancing в Unreal и Unity.

СвойствоModel SpaceЗачем нужно
Центр (origin)Центр объектаВращение происходит вокруг origin
Масштаб«Естественный» размерS = (1,1,1) = оригинальный размер
Общие вершиныОдин буфер на все экземплярыGPU instancing - тысячи деревьев из одной модели
Bounding boxВ model spaceДля collision detection до трансформации

100 деревьев в сцене используют одну и ту же 3D-модель. Что различается для каждого дерева?

World Space (мировые координаты)

Все объекты сцены живут в **World Space** - единой глобальной системе координат. Дом стоит на (10, 0, 5), дерево - на (15, 0, 8), персонаж - на (12, 0, 6). World Space - «карта мира» сцены. Здесь считают физику, AI и освещение.

**World Space** - глобальная система координат сцены. Вершина переходит из Model Space в World Space через Model Matrix: world_pos = ModelMatrix × model_pos. В World Space вычисляется освещение, физика, AI.

В World Space источник света и поверхность объекта существуют в одной системе координат. Это принципиально: нельзя посчитать dot(normal, light_dir), если normal в Model Space объекта, а light_dir - глобальный вектор в World Space. Нужно общее пространство.

Операция в World SpaceЗачемПример
Расстояние между объектамиФизика, AIВраг в радиусе 10 метров?
Направление на источник светаОсвещениеdot(normal, light_dir) = яркость
RaycastingВыбор объектов мышьюЛуч из камеры через пиксель в мир
Bounding volume проверкиBroad phase collisionAABB overlap в world space

Освещение обычно вычисляется в World Space. Почему не в Model Space?

View Space и матрица LookAt

Камера стоит в мире и смотрит в определённом направлении. **View Space** - это мир, «увиденный глазами камеры»: камера находится в (0,0,0), смотрит вдоль -Z (OpenGL). Весь мир пересчитывается в координаты камеры. Не камера летит к сцене - сцена летит к камере.

**View Matrix** (Camera Matrix) переводит координаты из World Space в View Space. Строится через **LookAt**: задаём позицию камеры (eye), точку, на которую смотрим (target), и вектор «вверх» (up). View Matrix = (Model Matrix камеры)^(-1).

**Почему не оставить World Space?** Проекция - следующий шаг - предполагает, что камера в начале координат и смотрит вдоль оси Z. View Matrix «перемещает весь мир» так, чтобы камера оказалась в origin. Математически это инверсия: camera_to_world^(-1) = world_to_camera.

Параметр LookAtЧто задаётТипичное значение
eyeПозиция камеры в мире(0, 5, 10) - сзади и сверху
targetКуда смотрим(0, 0, 0) - центр сцены
upГде «верх»(0, 1, 0) - Y вверх
РезультатView Matrix 4x4Обратная матрица позиции камеры

View Matrix - это обратная матрица трансформации камеры. Почему обратная?

Projection: Perspective vs Orthographic

Мир - трёхмерный. Экран - двумерный. **Проекция** сжимает 3D в 2D. Два типа: **perspective** (как видит глаз - далёкое меньше) и **orthographic** (без перспективы - инженерные чертежи). В играх - perspective. В CAD, 2D-играх, UI - orthographic.

**Perspective Projection** - имитирует человеческое зрение. Определяется FOV (field of view), aspect ratio, near plane и far plane. Далёкие объекты уменьшаются. **Orthographic** - параллельная проекция, размер не зависит от расстояния.

**Frustum** (усечённая пирамида) - объём пространства, видимый камерой при perspective projection. Всё за пределами frustum **отсекается** (clipping) - GPU не тратит ресурсы на невидимые объекты. Это происходит ещё в Clip Space, до perspective divide.

ПараметрPerspectiveOrthographic
FOV (field of view)Определяет ширину обзораНе используется
Near planeМинимальная дистанция рендерингаМинимальная дистанция
Far planeМаксимальная дистанцияМаксимальная дистанция
Эффект глубиныДалёкое уменьшаетсяНет эффекта глубины
Последняя строка матрицы[0, 0, -1, 0][0, 0, 0, 1]

**Near plane** нельзя ставить близко к 0! Z-buffer имеет конечную точность, и при near близком к 0 далёкие объекты «сливаются» (Z-fighting). Рекомендация: near >= 0.1 для игр.

Perspective projection matrix имеет -1 в позиции [3][2]. Зачем?

NDC: Normalized Device Coordinates

После projection вершина в **Clip Space** с координатами (x, y, z, w). GPU выполняет **perspective divide**: делит x, y, z на w. Результат - **NDC** (Normalized Device Coordinates). Все видимые координаты попадают в [-1, 1]. Вне - отсекается.

**NDC** (Normalized Device Coordinates) - нормализованное пространство после perspective divide. В OpenGL: x ∈ [-1, 1], y ∈ [-1, 1], z ∈ [-1, 1]. В DirectX/Vulkan: z ∈ [0, 1]. Всё за пределами - отсечено.

Последний шаг: **Viewport Transform** - из NDC [-1,1] в пиксельные координаты экрана [0, width] × [0, height]. Формулы: screen_x = (ndc_x + 1) / 2 * width, screen_y = (ndc_y + 1) / 2 * height.

ПространствоМатрица переходаДиапазон координатЧто происходит
Model -> WorldModel Matrix (TRS)ПроизвольныйРазмещение в сцене
World -> ViewView Matrix (LookAt)ПроизвольныйКамера -> origin
View -> ClipProjection MatrixПроизвольный (с w)Перспектива
Clip -> NDCPerspective divide (÷w)[-1, 1]^3Нормализация
NDC -> ScreenViewport transform[0,W] × [0,H]Пиксели экрана

От локальных координат модели до пикселя на экране - 5 пространств и 4 матричных перехода. GPU вычисляет MVP = P × V × M один раз и применяет к миллионам вершин параллельно. Это и есть vertex shader - первый этап graphics pipeline.

Perspective projection - это просто деление x и y на z

Полная perspective projection matrix включает FOV (угол обзора), aspect ratio, near plane и far plane. Деление на z - только часть процесса. Матрица масштабирует x и y с учётом FOV/aspect, преобразует z для корректного Z-buffer, и копирует -z в w для perspective divide

Простое x/z не учитывает: 1) FOV - насколько широко видит камера, 2) aspect ratio - экран не квадратный, 3) near/far - нужно отсечение по глубине и правильное распределение точности Z-buffer. Матрица проекции решает все задачи одним умножением.

Вершина после projection имеет clip-координаты (3, 2, -8, -10). Каковы NDC-координаты?

Ключевые идеи

  • **Model Space** - локальные координаты объекта. Model Matrix (TRS) размещает его в мире
  • **World Space** - общая сцена. View Matrix (LookAt^-1) переносит мир в координаты камеры
  • **Projection** (perspective/orthographic) сжимает 3D в frustum. Perspective divide (÷w) создаёт перспективу
  • **NDC** [-1,1]^3 -> Viewport Transform -> пиксели экрана. Полный pipeline: MVP = P × V × M

Связанные темы

Координатные пространства - основа для понимания всего rendering pipeline:

  • Линейная алгебра для графики — Матрицы 4x4, однородные координаты, TRS - математический фундамент всех трансформаций
  • Растеризация — После NDC->Screen вершины поступают на растеризатор, который заполняет пиксели между ними
  • Z-Buffer — NDC z-координата используется для определения, какой объект ближе к камере (depth testing)

Вопросы для размышления

  • Почему GPU вычисляет MVP = P × V × M заранее, а не применяет три матрицы последовательно к каждой вершине?
  • При VR-рендеринге две камеры (левый и правый глаз) имеют разные View Matrix. Какие части pipeline можно переиспользовать?
  • Что произойдёт с перспективой, если FOV увеличить до 170°? А уменьшить до 5°?

Связанные уроки

  • cg-02 — Матрицы 4x4 и однородные координаты - математика всех переходов
  • cg-04 — NDC z-координата идёт прямо в depth buffer для Z-fighting
  • cg-01 — Растеризатор получает вершины уже в Screen Space
  • geo-01 — Аффинные преобразования - формальный базис TRS-матриц
  • arvr-01 — VR: два frustum, две View Matrix, stereo rendering
  • cg-05 — Shading требует нормалей в правильном пространстве
  • la-06-transformations
Координатные пространства

0

1

Войти