Пространственный перенос компьютерная графика
Геометрические преобразования – это такие преобразования графических объектов, при которых изменяется направление, масштаб и положение координатной системы, используемой для определения местонахождения точек в пространстве. При этом само пространство не подвергается каким-либо изменениям, т.е. сама структура графического изображения сохраняется. Следовательно, при геометрических преобразованиях неважен тип объекта, так как в преобразованиях участвуют точки.
- Перенос начала координат
- Масштабирование
- Поворот осей координат
Перенос
При выполнении данной операции происходит перенос начала координат на некоторую величину.
Обозначим старую систему координат СК1, новую – СК2. При переносе новое начало координат в системе координат СК1 лежит в точке (Tx, Ty), в новой же системе СК2 старое начало координат является точкой (-Tx, -Ty). Начало координат (x,y) системы СК1 в системе СК2 станет точкой (x-Tx, y-Ty).
Стоит упомянуть, что при переносе сохраняются как направление осей координат, так и масштаб.
Геометрия на плоскости
Аффинные преобразования на плоскости
То есть, любое линейное преобразование на плоскости это композиция растягиваний и сдвигов. Что означает, что какой бы ни была матрица нашего преобразования, начало координат всегда перейдёт в начало координат. Таким образом, линейные преобразования — это прекрасно, но если мы не можем представить элементарного параллельного переноса, то наша жизнь будет печальна. Или можем? А что, если добавить его отдельно и записать аффинное преобразование как композицию линейной части и параллельного переноса? Примерно вот так:
Это, конечно, прекрасная запись, но вот только давайте посмотрим, на что похожей выглядит композиция двух таких преобразований (я напоминаю, что в реальной жизни нам нужно уметь аккумулировать десятки преобразований):
Это начинает выглядеть крайне неприятно уже для одной-единственной композиции. Попробуйте преобразовать это выражение, чтобы применить к нашему объекту только одно преобразование вида линейная часть + параллельный перенос. Лично мне очень не хочется этого делать.
Пора перейти к трём измерениям
Давайте объяснять только что произошедшую магию.
Как и в случае двумерных аффинных преобразований, в трёхмерном пространстве мы тоже будем использовать однородные координаты.
Берём точку (x,y,z), погружаем её в четырёхмерное пространство, добавив единицу в четвёртую компоненту, преобразуем в четырёх измерениях и проецируем обратно в 3d. Например, возьмём такое преобразование:
Проекция на 3д даёт следующие координаты:
Хорошо запомним этот результат, но на пару минут его отложим. Давайте вернёмся к стандартному определению центральной проекции в обычном 3д, без однородных координат и прочих экзотических вещей. Пусть у нас будет точка P=(x,y,z), которую мы хотим спроецировать на плоскость z=0, камера находится на оси z на расстоянии c от центра координат.
Мы знаем, что треугольники ABC и ODC подобны. То есть, мы можем записать |AB|/|AC|=|OD|/|OC| => x/(c-z) = x'/c.
Рассматривая треугольники CPB и CP'D, можно легко прийти к подобной записи и для координаты y:
Итак, это очень-очень похоже на результат проекции через однородные координаты, только там это всё считалось одним матричным умножением. Мы вывели зависимость коэффициентов r = -1/c.
Хотя если вы просто возьмёте эту формулу, не поняв весь предыдущий текст, то я вас ненавижу. Итак, если мы хотим построить центральную перспективу с (важно!) камерой, находящейся на оси z на расстоянии c от начала координат, то сначала мы погружаем трёхмерные точки в четырёхмерное пространство, добавив 1. Затем умножаем на следующую матрицу и проецируем результат обратно в 3D:
Мы деформировали наш объект так, что теперь для построения проволочного рендера с перспективой нам достаточно просто забыть про новополученную координату z. Если мы хотим строить z-буфер, то, разумеется, мы её используем. Слепок кода доступен на гитхабе. Результат его работы виден в самом начале нашей статьи.
Этот вид компьютерной графики вобрал в себя очень много из векторной, а также из растровой компьютерной графики. Применяется она при разработке дизайн-проектов интерьера, архитектурных объектов, в рекламе, при создании обучающих компьютерных программ, видео-роликов, наглядных изображений деталей и изделий в машиностроении и др.
Трёхмерная компьютерная графика позволяет создавать объёмные трёхмерные сцены с моделированием условий освещения и установкой точек зрения.
Для изучения приёмов и средств композиции, таких как передача пространства, среды, светотени, законов линейной, воздушной и цветовой перспективы здесь очевидны преимущества этого вида компьютерной графики над векторной и растровой графикой. В трехмерной графике изображения (или персонажи) моделируются и перемещаются в виртуальном пространстве, в природной среде или в интерьере, а их анимация позволяет увидеть объект с любой точки зрения, переместить в искусственно созданной среде и пространстве, разумеется, при сопровождении специальных эффектов.
Трёхмерная компьютерная графика, как и векторная, является объектно-ориентированной, что позволяет изменять как все элементы трёхмерной сцены, так и каждый объект в отдельности. Этот вид компьютерной графики обладает большими возможностями для поддержки технического черчения. С помощью графических редакторов трёхмерной компьютерной графики, например Autodesk 3D Studio, можно выполнять наглядные изображения деталей и изделий машиностроения, а также выполнять макетирование зданий и архитектурных объектов, изучаемых в соответствующем разделе архитектурно-строительного черчения. Наряду с этим может быть осуществлена графическая поддержка таких разделов начертательной геометрии, как перспектива, аксонометрические и ортогональные проекции, т.к. принципы построения изображений в трёхмерной компьютерной графике частично заимствованы из них.
Для декоративно-прикладного искусства трёхмерная компьютерная графика предоставляет возможность макетирования будущих изделий с передачей фактуры и текстуры материалов, из которых эти изделия будут выполнены. Возможность увидеть с любых точек зрения макет изделия до его воплощения в материале позволяет внести изменения и исправления в его форму или пропорции, которые могут быть уже невозможны после начала работы (например, ювелирные изделия, декоративное литьё из металла и др.). В том же направлении трёхмерная компьютерная графика может быть использована для поддержки скульптуры, дизайна, художественной графики и др. Объёмная трёхмерная анимация и спецэффекты также создаются средствами трёхмерной графики. Создание учебных роликов для обучающих программ может стать основным применением этих возможностей трёхмерной компьютерной графики.
К средствам работы с трёхмерной графикой,относят такой графический редактор как 3D Studio MAX. Это один из самых известных трёхмерных редакторов, он часто используется при создании фильмов. Разработка программы 3D Studio МАХ была начата в 1993 году. Версия 3D Studio МАХ 1.0 вышла в 1995 году на платформе Windows NT.
Уже тогда некоторые эксперты осторожно высказывали мнение, что МАХ может конкурировать с другими пакетами трехмерной графики. Осенью 2003 года discreet выпускает ЗD MAX 6. Новые инструменты анимации частиц в связке с модулями позволяют создавать фотореалистичные атмосферные эффекты. Появились встроенная поддержка капельно-сетчатых объектов, полноценная сетевая визуализация, импорт данных из САD-приложений, новые возможности для моделирования. Но кроме 3D Studio MAX есть и другие, не менее популярные программы трёхмерного моделирования, например Maya. Maya – это программа-аналог 3D Studio MAX, но она предназначена, в первую очередь, для анимации и для передачи мимики на лице трёхмерного актёра. Кроме того, в Maya удобнее рисовать. 3D Studio MAX направлен в первую очередь на качественную визуализацию предметов, ещё в нём можно выполнять примитивные чертежи.
Вообще для черчения существуют свои программы трёхмерного моделирования, самые известные из них AutoCAD, ArhiCAD. AutoCAD предназначен, в первую очередь, для машиностроительного черчения, а ArhiCAD для архитектурного моделирования.
Что же требует трехмерная графика от человека?
Конечно же, умение моделировать различные формы и конструкции при помощи различных программных средств, а также знания ортогонального (прямоугольного) и центрального проецирования. Последняя - называется перспективой. Очень хорошее качество моделирования достигается при помощи тщательного подбора текстур и материалов в сочетании с правильным размещением в сцене источников освещения и камер. Основой для построения любой пространственной формы является плоскость и грань объекта. Плоскость в трехмерной графике задается с помощью трех точек, соединенных отрезками прямых линий.
Именно это условие дает возможность описать с помощью получаемых плоскостей «пространственную сетку», которая представляет собой модель объекта. Затем объекту дополнительно присваиваются характеристики поверхности объекта – материал. В свою очередь, материал характеризует качество поверхности, например, полированная, шероховатая, блестящая и др. Описывается и его текстура (камень, ткань, стекло и др.). Задаются и оптические свойства, например, прозрачность, отражение или преломление световых лучей и т.д.
Наряду с этим, трехмерному объекту можно задать условия освещения и выбрать точку обзора (камеру) для получения наиболее интересного наглядного изображения. Постановка, состоящая из трехмерного объекта, условий освещения и выбранной точки зрения, называется «трехмерной сценой». А вот для описания трехмерного пространства и объекта, находящегося внутри его, используется хорошо уже знакомый Вам координатный метод.
Существуют различные методы моделирования трехмерных объектов. Например, метод текстового описания модели с помощью специальных языков программирования «Скрипт».
Секундочку, но ведь на ноль делить нельзя!
- Мы погружаем наше 2d пространство в 3d, сделав его плоскостью z=1
- Делаем что хотим в 3d
- Для каждой точки, которую хотим спроецировать обратно в 2d, проводим прямую между началом координат и данной точкой и ищем её пересечение с физической плоскостью z=1.
Теперь давайте представим вертикальный рельс, проходящий через точку (x,y,1). Куда спроецируется точка (x,y,1)? Конечно же, в (x,y):
Теперь давайте начнём скользить вниз по рельсу, например, точка (x, y, 1/2) спроецируется в (2x, 2y):
Продолжим скользить: точка (x,y,1/4) спроецируется в (4x, 4y):
Продолжая скользить к нулю по z, наша проекция уходит всё дальше и дальше от центра координат по направлению (x,y).
То есть, точка (x,y,0) проецируется в бесконечно далёкую точку в направлении (x,y). А что это? Правильно, это вектор!
Однородные координаты дают возможность различать вектор и точку. Если программист пишет vec2 v(x,y), это вектор или точка?
Трудно сказать. А в однородных координатах всё, что с нулём по третьей компоненте, это вектор, всё остальное конечные точки.
Смотрите: вектор + вектор = вектор. Вектор-вектор = вектор. Точка + вектор = точка. Ну не здорово ли?
Линейные преобразования плоскости
Линейное на плоскости отображение задаётся соответствующей матрицей. Если мы возьмём точку (x,y), то её преобразование записывается следующим образом:
Самое простое (невырожденное) преобразование задаётся единичной матрицей, оно просто оставляет каждую точку на месте
Коэффициенты на диагонали матрицы задают растягивание/сжатие плоскости. Давайте проиллюстрируем картинкой: например, если мы запишем следующее преобразование:
То белый объект (квадрат с отрезанным углом) преобразуется в жёлтый. Красный и зелёный отрезки дают единичные векторы по оси x и y, соответственно.
Все картинки к этой статье сгенерированы вот этим кодом.
Зачем вообще использовать матрицы? Потому что это удобно. Начнём с того, что в матричной форме преобразование всего объекта можно записать вот таким образом:
Здесь преобразование то же, что и в предыдущем примере, а вот матрица в две строки и пять столбцов не что иное, как массив координат нашего куба с обрезанным углом. Мы просто взяли целиком массив, умножили на преобразование, и получили уже преобразованный объект. Красиво? Окей, согласен, притянуто за уши.
Настоящая причина в том, что крайне регулярно мы хотим, чтобы объект подвергся нескольким преобразованиями подряд. Представьте, что вы пишете в вашем коде функции преобразований типа
Этот код делает два линейных преобразования на каждую вершину объекта, а они исчисляются в миллионах. И преобразований зачастую мы хотим с добрый десяток. Дорого. А с матричным подходом мы перемножаем все матрицы преобразования и умножаем на наш объект один раз. В умножении мы можем ставить скобки где хотим, правда ведь?
Продолжаем разговор, мы знаем, что диагональные элементы нам дают масштабирование по осям. За что отвечают два других коэффициента матрицы? Давайте рассмотрим такое преобразование:
Не что иное, как простой сдвиг вдоль оси x. Второй анти-диагональный элемент даст сдвиг вдоль оси y. Таким образом, базовых линейных преобразований на плоскости только два: растягивание по осям и сдвиг вдоль оси. Постойте, скажут мне, а как же, например, вращение вокруг начала координат?
Выясняется, что вращение может быть представлено как композиция трёх сдвигов, здесь белый объект преобразован сначала в красный, затем в зелёный, затем в синий:
Но не будем ударяться в крайности, матрица вращения против часовой стрелки вокруг начала координат может быть записана напрямую (помните про расстановку скобок?):
Перемножать мы можем, конечно, в любом порядке, только давайте не забывать, что для матриц умножение некоммутативно:
Что нормально, сдвинуть и затем повернуть (красный объект) не то же самое, что сначала повернуть, а затем сдвинуть (зелёный объект):
Масштабирование
При выполнении операции масштабирования отрезок единичной длины на оси X становится отрезком длиной Sx на оси X’; отрезок единичной длины на оси Y становится отрезком длиной Sy на оси Y’. Получается, что в новой системе точка с координатой (1,1) преобразуется в точку (Sx, Sy), точка (x,y) будет иметь координаты (x*Sx, y*Sy).
Стоит отметить, что при масштабировании начало координат и направление осей новой и старой систем координат совпадают, меняется лишь масштаб по осям.
Пример составного преобразования
Я уже говорил, что нам нужно уметь накапливать десятки преобразований. Почему? Предположим, вам нужно повернуть плоский объект вокруг точки (x0, y0). Как это сделать? Можно пойти и искать формулы, а можно сделать самим, ведь у нас есть все инструменты.
Мы умеем вращать вокруг центра координат, мы умеем сдвигать. Что ещё надо? Сдвигаем x0,y0 в центр координат, вращаем, возвращаем назад. Халява!
В 3д последовательности действий будут немного длиннее, но смысл остаётся прежним: нам достаточно уметь делать несколько базовых преобразований, и с их помощью мы можем закодировать какое угодно сложное.
Постойте, а имею ли я право трогать нижнюю строку матрицы 3x3?
Ещё как! Давайте применим вот это преобразование:
к нашему стандартному тестовому объекту. Напоминаю, что тестовый объект белый, единичные икс и игрек вектора показаны красным и зелёным, соответственно
Вот наш преобразованный объект:
И вот тут начинается самое интересное. Помните наше упражнение про игрек-буфер? Здесь мы будем делать практически то же самое.
Мы будем проецировать наш двумерный объект на прямую x=0. Причём теперь усложним задачу: проекция будет центральной, наша камера находится в точке (5, 0) и смотрит в начало координат. Чтобы найти проекцию, мы должны провести прямые, проходящие через точку камеры и каждую вершину нашего объекта (жёлтые прямые), а затем найти их пересечение с прямой экрана (белая вертикальная).
А теперь давайте уберём оригинальный объект и вместо него нарисуем трансформированный.
Если мы используем обычную ортогональную проекцию нашего трансформированного объекта, то мы найдём ровно те же самые точки!
Ведь что делает это отображение? Оно каждое вертикальное ребро оставляет вертикальным, но при этом растягивает те, которые близко к камере, и сжимает те, что дальше от камеры. Правильно подобрав коэффициент растяжения-сжатия мы можем как раз достичь эффекта, что простой ортогональной проекцией мы получаем изображение в перспективном искажении! В следующем параграфе мы добавим одно измерение и покажем, откуда взялся коэффициент -1/5.
Однородные координаты
А что же делать? Колдовать! Представьте теперь, что я допишу руками одну строчку и один столбец к нашей матрице преобразования и добавлю третью координату, которая равна единице у вектора, который мы преобразовываем:
При умножении этой 3x3 матрицы и нашего вектора, дополненного единицей, мы снова получили вектор с единицей в третьей компоненте, а остальные две имеют ровно тот вид, который мы хотели! Колдунство.
На самом деле, идея очень простая: параллельный перенос не является линейной операцией в двумерном пространстве.
Поэтому мы погружаем наше двумерное пространство в трёхмерное (добавив единицу в третью компоненту). Это означает, что наше двумерное пространство это плоскость z=1 внутри трёхмерного. Затем мы делаем линейное преобразование в трёхмерном пространстве и проецируем всё трехмерное пространство обратно на нашу физическую плоскость. Параллельный перенос от этого не стал линеен, но пайплайн всё же прост.
Как именно мы проецируем трёхмерное пространство обратно в нашу плоскость? Очень просто:
Поворот
Рассмотрим частный случай поворота в пространстве XYZ – поворот относительно оси Z, проходящей перпендикулярно плоскости XY.
Предположим, что центр вращения совпадает с началом координат. Тогда при повороте системы координат XY в декартовой плоскости на некоторый произвольный угол w относительно центра вращения, координаты произвольной точки С в системе координат (x’,y’) можно выразить через координаты этой точки в изначальной системе координат (x, y):
x' = x cos w + y sin w,
y' = -x sin w + y cos w
Для выполнения операции поворота используется следующая матрица:
Если каждая точка отображается на точку в соответствии с уравнениями
где константы, то этот процесс называется переносом в трехмерном пространстве. Такой перенос может быть записан в матричной форме
Читатель, ознакомившийся с параграфом 3.6, может легко распознать, что первая, вторая и третья строки матрицы Т соответствуют отображениям бесконечно удаленных точек на координатных осях, а четвертая строка — отображению точки Последнее означает, что в однородных координатах точка является отображением точки начала координат О. Поворот вокруг координатных осей может быть описан матрицей без использования однородных координат. Ради краткости так и будем поступать, обращаясь к однородным координатам только в тех случаях, когда они действительно необходимы. Будем использовать правую координатную систему, считая вращение вокруг оси положительным, если оно соответствует
Рис. 3.18. Вращение в положительном направлении вокруг координатных осей
положительному направлению этой оси по правилу винта с правой резьбой. Это показано на рис. 3.18.
Рассмотрим поворот вокруг оси z на угол а и для сокращения обозначим Тогда можно записать
что непосредственно следует из уравнений (2.4) в параграфе 2.3.
Матрицу можно использовать для получения матриц определяющих поворот вокруг соответствующих осей, чисто формальным образом, то есть без применения картинки. Это делается путем циклических перестановок, получаемых заменой каждой из букв х, у, z на последующую, считая, что за буквой z следует буква х.
Матрица превратится в матрицу циклическим переносом каждой строки на одну позицию и затем выполнением аналогичной операции для столбцов:
Так же матрица преобразуется в “последующую” матрицу У:
Суммируя сказанное, получим следующие матрицы:
Для поворота точки вокруг оси х на угол а матрица используется следующим образом:
Матрицы применяются аналогично.
Как объяснялось в параграфе уравнения для преобразований могут интерпретироваться как изменения координат. Перенос точки на определенное расстояние вправо описывается теми же уравнениями, как перенос системы координат на такое же расстояние влево. На практике удобнее перемещать координатную систему вместо точки, но для этого требуется инвертирование матрицы. К счастью, инверсия матриц (уравнения (3.12)-(3.15)) может быть записана немедленно:
Теперь можно найти матрицу для поворота вокруг любой прямой линии, проходящей через точку начала координат О. Для определенности будем полагать, что поворот осуществляется вокруг вектора начало которого расположено в точке О. Тогда положительное направление вращения соответствует направлению вектора по правилу винта с правой резьбой. Как и ранее, поворот будем производить на угол а.
Если концевая точка вектора задана в ортогональных координатах, то сначала вычислим его сферические координаты (см. рис. 3.19)
Если то будем считать, что В противном случае если
Читателю, возможно, известно обратное вычисление
Теперь стратегия заключается в таком изменении системы координат, чтобы вектор (ось вращения) совпадал с новым направлением положительной полуоси z. Начнем с поворота осей х и у вокруг оси z на угол В соответствии с уравнением (3.19)
Ось х имеет положительное направление вектора Теперь повернем оси вокруг оси у на угол до совпадения оси с вектором рис. 3.19).
Обращаясь к уравнению (3.18), это условие запишем как
Фактический поворот вокруг вектора на угол а теперь можно выполнить как поворот вокруг осиг". Из уравнения (3.15) получим
К этому моменту достигнуто выполнение соотношения
Рис. 3.19. Сферические координаты
К сожалению, координаты относятся к самой последней системе координат, тоща как их необходимо выразить в исходной системе. Обозначим эти координаты в исходной системе через Переход к исходной системе инвертированных матриц (которые будут совпадать с матрицами и в обратном порядке для преобразования точки
Это означает, что полный поворот вокруг вектора на угол а вычисляется по следующей формуле:
Для последующего применения запишем
До сих пор обсуждалось решение задачи о повороте относительно вектора, привязанного к точке начала системы координат О. Теперь нужно устранить это последнее ограничение и поставить задачу определения поворота относительно вектора, начало которого расположено в любой произвольной точке
Для этого будем использовать вектор для вычисления матрицы в уравнении (3.20) таким же образом, как и ранее. Затем нужно выполнить три следующих шага:
1. Обращаясь к уравнению (3.16), выполним перенос из заданной точки в точку начала координат О, используя однородные координаты и следующую матрицу:
2. Теперь можем осуществить поворот относительно оси, проходящей через О, как и ранее, но матрицу из уравнения (3.20) необходимо расширить тривиальным образом, чтобы можно было использовать однородные координаты
В определённый момент у любого разработчика в области компьютерной графики возникает вопрос: как же работают эти перспективные матрицы? Подчас ответ найти очень непросто и, как это обычно бывает, основная масса разработчиков бросает это занятие на полпути.
Это не решение проблемы! Давайте разбираться вместе!
Будем реалистами с практическим уклоном и возьмём в качестве подопытного OpenGL версии 3.3. Начиная с этой версии каждый разработчик обязан самостоятельно реализовывать модуль матричных операций. Замечательно, это то, что нам нужно. Проведём декомпозицию нашей с вами нелёгкой задачи и выделим основные моменты. Немного фактов из спецификации OpenGL:
- Матрицы хранятся по столбцам (column-major);
- Однородные координаты;
- Канонический объём отсечения (CVV) в левосторонней системе координат.
Однородные координаты – это не очень хитрая система с рядом простых правил по переводу привычных декартовых координат в однородные координаты и обратно. Однородная координата это матрица-строка размерности [1x4]. Для того чтобы перевести декартову координату в однородную координату необходимо x, y и z умножить на любое действительное число w (кроме 0). Далее необходимо записать результат в первые три компоненты, а последний компонент будет равен множителю w. Другими словами:
— декартовы координаты
w – действительное число, не равное 0
— однородные координаты
Небольшой трюк: Если w равно единице, то всё что нужно для перевода, это перенести компоненты x, y и z и приписать единицу в последний компонент. То есть получить матрицу-строку:
Несколько слов о нуле в качестве w. С точки зрения однородных координат это вполне допустимо. Однородные координаты позволяют различать точки и вектора. В декартовой же системе координат такое разделение невозможно.
— точка, где (x, y, z) – декартовы координаты
— вектор, где (x, y, z) – радиус-вектор
Обратный перевод вершины из однородных координат в декартовы координаты осуществляется следующим образом. Все компоненты матрицы-строки необходимо разделить на последнюю компоненту. Другими словами:
— однородные координаты
— декартовы координаты
Главное что необходимо знать, что все алгоритмы OpenGL по отсечению и растеризации работают в декартовых координатах, но перед этим все преобразования производятся в однородных координатах. Переход от однородных координат в декартовы координаты осуществляется аппаратно.
Канонический объём отсечения или Canonic view volume (CVV) – это одна из мало документированных частей OpenGL. Как видно из рис. 1 CVV – это выровненный по осям куб с центром в начале координат и длиной ребра равной двойке. Всё, что попадает в область CVV подлежит растеризации, всё, что находится вне CVV игнорируется. Всё, что частично выходит за границы CVV, подлежит алгоритмам отсечения. Самое главное что надо знать — система координат CVV левосторонняя!
Рис. 1. Канонический объём отсечения OpenGL (CVV)
Левосторонняя система координат? Как же так, ведь в спецификации к OpenGL 1.0 ясно написано, что используемая система координат правосторонняя? Давайте разбираться.
Рис. 2. Системы координат
Как видно из рис. 2 системы координат различаются лишь направлением оси Z. В OpenGL 1.0 действительно используется правосторонняя пользовательская система координат. Но система координат CVV и пользовательская система координат это две совершенно разные вещи. Более того, начиная с версии 3.3, больше не существует такого понятия как стандартная система координат OpenGL. Как упоминалось ранее, программист сам реализует модуль матричных операций. Формирование матриц вращения, формирование проекционных матриц, поиск обратной матрицы, умножение матриц – это минимальный набор операций, входящих в модуль матричных операций. Возникает два логичных вопроса. Если объём видимости это куб с длиной ребра равной двум, то почему сцена размером в несколько тысяч условных единиц видна на экране? В какой момент происходит перевод пользовательской системы координат в систему координат CVV. Проекционные матрицы – это как раз та сущность, которая занимается решением этих вопросов.
Главная мысль вышеизложенного – разработчик сам волен выбрать тип пользовательской системы координат и должен корректно описать проекционные матрицы. На этом с фактами об OpenGL закончено и подошло время сводить всё воедино.
Одна из наиболее распространённых и сложно постигаемых матриц – это матрица перспективного преобразования. Так как же она связана с CVV и пользовательской системой координат? Почему объекты с увеличением расстояния до наблюдателя становятся меньше? Для того чтобы понять почему объекты уменьшаются с увеличением расстояния, давайте рассмотрим матричные преобразования трёхмерной модели шаг за шагом. Не секрет, что любая трёхмерная модель состоит из конечного списка вершин, которые подвергаются матричным преобразованиям совершенно независимо друг от друга. Для того чтобы определить координату трёхмерной вершины на двухмерном экране монитора необходимо:
- Перевести декартову координату в однородную координату;
- Умножить однородную координату на модельную матрицу;
- Результат умножить на видовую матрицу;
- Результат умножить на проекционную матрицу;
- Результат перевести из однородных координат в декартовы координаты.
Теперь переходим к шагу три. Здесь начинает работу видовое пространство. В этом пространстве координаты отсчитываются относительно положения и ориентации наблюдателя так, как если бы он являлся центром мира. Видовое пространство является локальным относительно мирового пространства, поэтому координаты в него надо вносить (а не выносить, как в предыдущем случае). Прямое матричное преобразование выносит координаты из некоторого пространства. Чтобы наоборот внести их в него, надо матричное преобразование инвертировать, поэтому видовое преобразование описывается обратной матрицей. Как же получить эту обратную матрицу? Для начала получим прямую матрицу наблюдателя. Чем характеризуется наблюдатель? Наблюдатель описывается координатой, в которой он находится, и векторами направления обзора. Наблюдатель всегда смотрит в направлении своей локальной оси Z. Наблюдатель может перемещаться по сцене и осуществлять повороты. Во многом это напоминает смысл модельной матрицы. По большому счёту так оно и есть. Однако, для наблюдателя операция масштабирования бессмысленна, поэтому между модельной матрицей наблюдателя и модельной матрицей трёхмерного объекта нельзя ставить знак равенства. Модельная матрица наблюдателя и есть искомая прямая матрица. Инвертировав эту матрицу, мы получаем видовую матрицу. На практике это означает, что все вершины в глобальных однородных координатах получат новые однородные координаты относительно наблюдателя. Соответственно, если наблюдатель видел определённую вершину, то значение однородной координаты z данной вершины в видовом пространстве точно будет положительным числом. Если вершина находилась за наблюдателем, то значение её однородной координаты z в видовом пространстве точно будет отрицательным числом.
Шаг четыре — это самый интересный шаг. Предыдущие шаги были рассмотрены так подробно намеренно, чтобы читатель имел полную картину о всех операндах четвёртого шага. На четвёртом шаге однородные координаты выносятся из видового пространства в пространство CVV. Ещё раз подчеркивается тот факт, что все потенциально видимые вершины будут иметь положительное значение однородной координаты z.
Рассмотрим матрицу вида:
И точку в однородном пространстве наблюдателя:
Произведём умножение однородной координаты на рассматриваемую матрицу:
Переведём получившиеся однородные координаты в декартовы координаты:
Допустим, есть две точки в видовом пространстве с одинаковыми координатами x и y, но разными координатами z. Другими словами одна из точек находится за другой. Из-за перспективного искажения наблюдатель должен увидеть обе точки. Действительно, из формулы видно, что из-за деления на координату z, происходит сжатие к точке начала координат. Чем больше значение z (чем дальше точка от наблюдателя), тем сильнее сжатие. Вот и объяснение эффекту перспективы.
В спецификации OpenGL сказано, что операции по отсечению и растеризации выполняются в декартовых координатах, а процесс перевода однородных координат в декартовы координаты производится автоматически.
Матрица (1) является шаблоном для матрицы перспективой проекции. Как было сказано ранее, задача матрицы проекции заключается в двух моментах: установка пользовательской системы координат (левосторонняя или правосторонняя), перенос объёма видимости наблюдателя в CVV. Выведем перспективную матрицу для левосторонней пользовательской системы координат.
- Угол обзора в радианах (fovy);
- Соотношение сторон (aspect);
- Расстояние до ближней плоскости отсечения (n);
- Расстояние до дальней плоскости отсечения (f).
Рассмотрим проекцию точки в пространстве наблюдателя на переднюю грань отсечения перспективного объёма видимости. Для большей наглядности на рис. 4 изображён вид сбоку. Так же следует учесть, что пользовательская система координат совпадает с системой координат CVV, то есть везде пользуется левосторонняя система координат.
Рис. 4. Проецирование произвольной точки
На основании свойств подобных треугольников справедливы следующие равенства:
Выразим yꞌ и xꞌ:
В принципе, выражений (2) достаточно для получения координат точек проекции. Однако для правильного экранирования трёхмерных объёктов необходимо знать глубину каждого фрагмента. Другими словами необходимо хранить значение компоненты z. Как раз это значение используется при тестах глубины OpenGL. На рис. 3 видно, что значение zꞌ не подходит в качестве глубины фрагмента, потому что все проекции точек умеют одинаковое значение zꞌ. Выход из сложившейся ситуации – использование так называемой псевдоглубины.
- Псевдоглубина рассчитывается на основании значения z;
- Чем ближе к наблюдателю находится точка, тем меньшеe значение имеет псевдоглубина;
- У всех точек, лежащих на передней плоскости объёма видимости, значение псевдоглубины равно -1;
- У всех точек, лежащих на дальней плоскости отсечения объёма видимости, значение псевдоглубины равно 1;
- Все фрагменты, лежащие внутри объёма видимости, имеют значение псевдоглубины в диапазоне [-1 1].
Коэффициенты a и b необходимо вычислить. Для того чтобы это сделать, воспользуемся свойствами псевдоглубины 3 и 4. Получаем систему из двух уравнений с двумя неизвестными:
Произведём сложение обоих частей системы и умножим результат на произведение fn, при этом f и n не могут равняться нулю. Получаем:
Раскроем скобки и перегруппируем слагаемые так, чтобы слева осталась только часть с а, а справа только с b:
Подставим (6) в (5). Преобразуем выражение к простой дроби:
Умножим обе стороны на -2fn, при этом f и n не могут равняться нулю. Приведём подобные, перегруппируем слагаемые и выразим b:
Подставим (7) в (6) и выразим a:
Соответственно компоненты a и b равны:
Теперь подставим полученные коэффициенты в матрицу заготовку (1) и проследим, что будет происходить с координатой z для произвольной точки в однородном пространстве наблюдателя. Подстановка выполняется следующим образом:
Пусть расстояние до передней плоскости отсечения n равно 2, а расстояние до дальней плоскости отсечения f равно 10. Рассмотрим пять точек в однородном пространстве наблюдателя:
Точка | Значение | Описание |
---|---|---|
1 | 1 | Точка находится перед передней плоскостью отсечения объёма видимости. Не проходит растеризацию. |
2 | 2 | Точка находится на передней грани отсечения объёма видимости. Проходит растеризацию. |
3 | 5 | Точка находится между передней гранью отсечения и дальней гранью отсечения объёма видимости. Проходит растеризацию. |
4 | 10 | Точка находится на дальней грани отсечения объёма видимости. Проходит растеризацию. |
5 | 20 | Точка находится за дальней гранью отсечения объёма видимости. Не проходит растеризацию. |
Умножим все точки на матрицу (8), а затем переведём полученные однородные координаты в декартовые координаты . Для этого нам необходимо вычислить значения новых однородных компонент и .
Обратите внимание, что однородная координата абсолютно верно позиционируется в CVV, а самое главное, что теперь возможна работа теста глубины OpenGL, потому что псевдоглубина полностью удовлетворяет требованиям тестов.
С координатой z разобрались, перейдём к координатам x и y. Как говорилось ранее весь перспективный объём видимости должен умещаться в CVV. Длина ребра CVV равна двум. Соответственно, высоту и ширину перспективного объёма видимости надо сжать до двух условных единиц.
В нашем распоряжении имеется угол fovy и величина aspect. Давайте выразим высоту и ширину, используя эти величины.
Рис. 5. Объём видимости
Из рис. 5 видно, что:
Теперь можно получить окончательный вид перспективной проекционной матрицы для пользовательской левосторонней системы координат, работающей с CVV OpenGL:
На этом вывод матриц закончен.
Пару слов о DirectX — основном конкуренте OpenGL. DirectX отличается от OpenGL только габаритами CVV и его позиционированием. В DirectX CVV — это прямоугольный параллелепипед с длинами по осям x и y равными двойке, а по оси z длина равна единице. Диапазон x и y равен [-1 1], а диапазон z равен [0 1]. Что касается системы координат CVV, то в DirectX, как и в OpenGL, используется левосторонняя система координат.
Для вывода перспективных матриц для пользовательской правосторонней системы координат необходимо перерисовать рис. 2, рис.3 и рис.4 с учётом нового направления оси Z. Далее расчёты полностью аналогичны, с точностью до знака. Для матриц DirectX свойства псевдоглубины 3 и 4 модифицируются под диапазон [0 1].
Четвёртая статья будет разбита на две, первая часть говорит про построение перспективного искажения, вторая про то, как двигать камеру и что из этого следует. Задача на сегодня — научиться генерировать вот такие картинки:
Читайте также: