Матрица вида и матрица проекции

Две матрицы превращают мир в картинку с конкретной камеры: матрица вида ставит камеру, матрица проекции создаёт перспективу.

Матрица вида (View) переводит мир в систему координат камеры; матрица проекции (Projection) отображает объём видимости в клип-пространство.

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

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

Матрица вида: look-at

Камера задаётся тремя векторами: где она стоит (eye), куда смотрит (target) и где у неё «верх» (up). Из них строят три оси камеры и собирают матрицу вида. Идея — поставить камеру в начало координат и развернуть мир так, будто смотрим вдоль оси Z.

Камера look-at:

      up
       ^
       |        target
   eye o-------->  *
            forward = target - eye

Перспективная и ортографическая проекция

Перспектива сжимает дальнее (реализм, игры от первого лица). Ортография сохраняет размеры независимо от глубины (чертежи, изометрические 2.5D-игры, UI).

Перспектива (frustum, усечённая пирамида):

   \        /
    \      /     дальняя плоскость шире
     \    /
      \  /
       \/  <- камера

Ортография (параллелепипед):

   |        |
   |        |    размеры не зависят от глубины
   |        |
   |________|

Сравним поведение проекций на простой модели: перспектива делит на глубину, ортография — нет.

def perspective(x, y, z, focal=2.0):
    return (round(focal*x/z, 3), round(focal*y/z, 3))

def orthographic(x, y, z, scale=0.5):
    return (round(x*scale, 3), round(y*scale, 3))  # z не влияет

print("Перспектива, z=2:", perspective(1, 1, 2))
print("Перспектива, z=8:", perspective(1, 1, 8))
print("Ортография, z=2: ", orthographic(1, 1, 2))
print("Ортография, z=8: ", orthographic(1, 1, 8))

Вывод:

Перспектива, z=2: (1.0, 1.0)
Перспектива, z=8: (0.25, 0.25)
Ортография, z=2:  (0.5, 0.5)
Ортография, z=8:  (0.5, 0.5)

В перспективе дальний объект (z=8) сжался вчетверо, в ортографии остался того же размера — глубина проигнорирована.

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

Перспективная проекция определяется углом обзора (field of view), соотношением сторон экрана (aspect) и ближней/дальней плоскостями отсечения (near/far). Она кладёт глубину в координату w, чтобы последующее деление дало перспективу. Объекты ближе near или дальше far отсекаются. Слишком большое отношение far/near вызывает z-fighting — мерцание из-за нехватки точности буфера глубины.

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

  • Ставить near = 0 — это ломает математику проекции; near должен быть строго положительным.
  • Огромный far при крошечном near — потеря точности глубины и мерцание поверхностей.
  • Забыть aspect ratio — картинка растянется при несовпадении со сторонами экрана.

Итоги

  • Матрица вида ставит камеру (eye, target, up) и переводит мир в её координаты.
  • Перспектива сжимает дальнее, ортография сохраняет размеры.
  • Проекция задаётся FOV, aspect, near и far.
  • Плохие near/far дают потерю точности глубины (z-fighting).
Проверьте себя
1. Какими тремя векторами задаётся камера в матрице вида look-at?
Ared, green, blue
Beye (позиция), target (цель), up (верх)
Cx, y, z скорости
Dnear, far, fov
2. Чем ортографическая проекция отличается от перспективной?
AОна цветная
BОна не сжимает дальние объекты — размеры не зависят от глубины
CОна работает быстрее в 100 раз
DОна не нужна в 3D
3. Что вызывает z-fighting (мерцание поверхностей)?
AСлишком мало вершин
BНехватка точности буфера глубины из-за большого отношения far/near
CОтсутствие текстур
DВысокий FPS