Что такое полигон в графике
Перейти к содержимому

Что такое полигон в графике

  • автор:

Терминология, используемая в 3D-графике.

Pixel (пиксель) — минимальный элемент изображения на экране, который может быть сгенерирован компьютером.

Polygon (полигон) — минимальный компонент трехмерного изображения на экране, на которые оно разделяется для последующей обработки. Как правило, используются треугольные полигоны, из которых затем могут быть построены любые другие многоугольники.

Texture (текстура) — обычное (двумерное) изображение, используемое при построении трёхмерного изображения путём наложения его на плоскости полигонов.

Rendering (рендеринг) — В трехмерной графике — операция построения изображения из набора текстур и геометрических данных.

Anti-aliasing — (Full Scene Antilaiasing(FSAA), полноэкранное сглаживание, антиалиазинг). Метод устранения артефактов рендеринга в виде неровностей краев объектов и «зубцов» на линиях. Самый популярный способ его реализации — создание плавного перехода от цвета линии или края к цвету фона изображения.
В настоящее время встречаются следюущие разновидности AA:
SuperSampling AA (SSAA) — фактически, при использовании этого метода изображение рендерится с более высоким разрешением, а потом масштабируется до экранного. Самый простой и самый медленный метод, приводит к нежелательному размытию мелких деталей изображения, например — надписей.
MultiSampling AA (MSAA) — более быстрый и совершенный алгоритм сглаживания, когда для подсчёта результирующего цвета точки берутся не все её соседи, как в SSAA, а некое подмножество, выбираемое по специальной матрице, причём исключительно на границах полигонов. Реализация этого алгоритма варьирует в различных видеокартах, в общем случае он вызывает значительно меньшие потери в скорости, чем «честный» SSAA, но не сглаживает объекты, находящиеся внутри полигонов. (Типичный пример — прозрачные и полупрозрачные поверхности).
TemporalAA — разновидность MSAA с меняющейся через каждый кадр матрицей выборки, теоретически способен выдать более высокое качество изображения.
В связи с тем, что используемые в качестве игровых наиболее популярные LCD мониторы с диагональю 17″-19″ имеют физическое разрешение матрицы в 1280*1024, обладателям высокопроизводительных видеокарт для достижения наилучшего качества картинки рекомендуется выбирать максимально качественный режим FSAA, при котором скорость остаётся в пределах комфортной играбельности. Наибольший выигрыш от использования FSAA получают игры с 3D-графикой, обладающие относительно статичной картинкой — т.е. реал-тайм стратегии, ролевые игры и т.п.

Bilinear Filtering (Билинейная фильтрация) — Алгоритм обработки изображения при переводе пикселей отображаемых текстур в реальные пиксели изображения. По этому алгоритму четыре соседних пикселя в текстуре отображаются в один «усредненный» пиксель, реально изображаемый на дисплее. Выбор пикселя зависит от того, откуда обозревается изображение. Базовый алгоритм фильтрации текстур, применяемый в настоящее время.
Отсутствие фильтрации текстур (прямая выборка — point sampling) приводит к сильным искажениям изображения и в современных видеокартах не применяется.

Mip Mapping (MIP) — название произошло от фразы на латинском Multum in Parvo (много в одном). Многоуровневое отображение — используется как один из способов упаковки текстур изображения в памяти и быстрой отрисовки их на экране. Для реализации этого метода в памяти хранится несколько вариантов одной текстуры, но с различным разрешением и, в зависимости от расстояния между точкой обзора и объектом, выбирается текстура оптимального разрешения. Текстуры на ближайших к точке обзора поверхностях выводятся с высоким разрешением, на удаленных — с худшим разрешением.

Trilinear Filtering (трилинейная фильтрация) — комбинация билинейной фильтрации и mip mapping. При выполнении трилинейной фильтрации сначала по алгоритму билинейной фильтрации обрабатываются текстуры одного уровня, а затем выполняется сглаживание перехода (mip mapping) к другому уровню. Даёт более высокое качество изображения, нежели билинейная фильтрация, но приводит к некоторой потере чёткости.

Anisotropic Filtering (анизотропная фильтрация) — тип фильтрации, учитывающий при расчёте результирующего цвета пикселя третье измерение объекта, а количество используемых для расчёта исходных пикселей зависит от степени анизотропии, которую можно регулировать в настройках качества графики приложения или видеокарты. (Увеличение степени анизотропии улучшает качество картинки, но вызывает потери скорости). Это наиболее качественный тип фильтрации, применяемый в настоящее время, даёт более чёткую и реалистичную картинку по сравнению с трилинейной фильтрацией.(Особенно это касается поверхностей, находящихся под острым углом к зрителю, типичный пример — полы и стены коридоров в «стрелялках»)

Multi-texturing — мультитекстурирование. Наложение более одной текстуры на пиксель для отображения различных эффектов, в первую очередь — освещённости. Применяется практически во всех современных играх.

V-sync — Синхронизация вывода очередного кадра 3D изображения с выводом ближайшего кадра изображения из локальной памяти видеокарты в соответствии с установленной частотой кадров. При включенном V-sync происходит ожидание вывода на экран отрисованной 3D картинки до тех пор, пока не наступит время вывода очередного кадра в соответствии с частотой кадров монитора. Выключение синхронизации ускоряет вывод на экран 3D изображений, но возможно появление артефактов(дефектов) на выводимых изображениях, поэтому отключение V-Sync рекомендуется делать только для тестирования производительности.

Z-buffering (Z-буферизация) — механизм удаления невидимых поверхностей при рендеринге изображения, использующий буфер в памяти, в котором сохраняется значение глубины изображения (т.е. координата по оси z) для каждого пикселя изображения. У современных видеокарт для ускорения рендеринга применяются различные способы оптимизации работы с z-буфером — иерархическая организация, ранняя очистка, компрессия и т.п.

Fillrate(cкорость заполнения) — одна из основных единиц измерения производительности процессоров 3D видеокарт. Этот показатель отражает максимальное количество операций наложения текстур на пиксель, выполняемых видеокартой в течение одной секунды.

FPS(Frames Per Second, кадров в секунду) — основная интегральная единица измерения фактической производительности 3D ускорителей в конкретном приложении и видеорежиме. Показывает количество кадров, которые ускоритель способен отрисовать за секунду. На практике, этот показатель прямо связан с критерием играбельности (т.е. возможностью комфортной игры) для данной конфигурации (включая собственно игру и видеорежим).
На практике для большинства пользователей абсолютно комфортная играбельность для «шутеров» от первого лица достигается при среднем FPS более 60 и минимальном — более 45. Для стратегий в реальном времени и ролевых игр эти показатели можно снизить до 45 и 30, соответственно.

Frame Buffer(буфер кадра) — область памяти для временного хранения всех пикселей и их характеристик для одного кадра изображения. Размер буфера зависит как от количества пикселей изображения, так и от глубины цвета (или количества цветов).

Gamma Correction (гамма-коррекция) — механизм, позволяющий корректировать искажения яркости цвета на мониторе, исторически вызванные нелинейной зависимостью между подаваемым на ЭЛТ напряжением и яркостью. Гамма-коррекция может регулировать яркость в диапазоне от 0.3 до 4, где 1.0 — значение по умолчанию.

Vertex Shader — (вершинный или вертексный шейдер) — исполняемая графическим процессором программа, математически оперирующая вершинами объекта. Каждый полигон изображения имеет вершины — точки, в которых сходятся две его стороны. Каждая вершина описывается целым рядом параметров( координаты, цвет, текстура, освещенность). Вершинные шейдеры позволяют изменять эти параметры, не меняя самого типа данных, что позволяет, в первую очередь, создавать реалистичную анимацию, связанную с деформацией поверхностей.(Мимика, водные поверхности и т.п.)

Pixel Shader — (пиксельный шейдер) — исполняемая графическим процессором программа, оперирующая непосредственно с конкретными пикселями изображения для расчета эффектов. Применение пиксельных шейдеров позволяет более точно передавать рельеф и освещение сложных поверхностей (в первую очередь — металл, шерсть, волосы, кожа), нежели классические полигональные методы

Полигон (в 3D-графике)

Музыка

Полиго́н (от греч. πολύγωνος – многоугольный), единица геометрического построения вершин, рёбер и граней, которые определяют форму объекта в трёхмерной графике при объёмном моделировании. Полигонами обычно являются треугольники , четырёхугольники или другие простые выпуклые многоугольники.

Редакция массовой культуры и медиа

Опубликовано 28 июля 2023 г. в 17:42 (GMT+3). Последнее обновление 28 июля 2023 г. в 17:42 (GMT+3). Связаться с редакцией

Информация

Музыка

Области знаний: Компьютерные технологии

  • Научно-образовательный портал «Большая российская энциклопедия»
    Свидетельство о регистрации СМИ ЭЛ № ФС77-84198,
    выдано Федеральной службой по надзору в сфере связи, информационных технологий и массовых коммуникаций (Роскомнадзор) 15 ноября 2022 года.
    ISSN: 2949-2076
  • Учредитель: Автономная некоммерческая организация «Национальный научно-образовательный центр «Большая российская энциклопедия»
    Главный редактор: Кравец С. Л.
    Телефон редакции: +7 (495) 917 90 00
    Эл. почта редакции: secretar@greatbook.ru
  • © АНО БРЭ, 2022 — 2024. Все права защищены.
  • Условия использования информации. Вся информация, размещенная на данном портале, предназначена только для использования в личных целях и не подлежит дальнейшему воспроизведению.
    Медиаконтент (иллюстрации, фотографии, видео, аудиоматериалы, карты, скан образы) может быть использован только с разрешения правообладателей.
  • Условия использования информации. Вся информация, размещенная на данном портале, предназначена только для использования в личных целях и не подлежит дальнейшему воспроизведению.
    Медиаконтент (иллюстрации, фотографии, видео, аудиоматериалы, карты, скан образы) может быть использован только с разрешения правообладателей.

3D в играх: Полигоны

Полигон (Polygon) – это плоскость в евклидовом пространстве. Пространство имеет размерность три, соответственно, имеются три координаты: X, Y, Z. Условно их можно обозвать как длина, высота и глубина. В программном обеспечении для 3D нет единого стандарта относительно, так сказать, ориентации данных координат, хотя, как правило, координата X параллельна условном горизонту, т.е. это длина, а вот условной высотой может быть как Y так и Z. Соответственно, условной глубиной может быть как Z, так и Y. Но нам это не столь суть важно, примем для последующего материала представление, показанное ниже.

У полигона есть вершины, минимум три, максимум, теоретически, бесконечность. Практически максимум – много. Но в играх используются треугольные полигоны, т.е. полигон имеет три вершины. Почему именно три? Через три точки в пространстве можно провести только одну плоскость, что упрощает расчеты и позволяет избежать искажений (артефактов) на итоговой картинке связанных с тем, что вершины полигона могут лежать не в одной плоскости.

Однако в программах трехмерного моделирования принято пользоваться 4-х угольными полигонами, а вот 5-ти и больше, как правило, под запретом. Поэтому при моделировании приходится следить за тем, чтобы все 4 вершины полигона были в одной или почти одной плоскости. А как же игры? спросите вы. Полигон с 4-мя вершинами математически очень просто превратить в два треугольных с двумя общими вершинами, что и делается автоматически при экспорте в игровой движок.

На рисунке ниже показаны треугольный полигон и четырехугольный, условно разделенный на два треугольных. На изображении это прямоугольный треугольник и прямоугольный параллелограмм, но по факту полигон может иметь различные длины сторон, соответственно, и различные углы между ними. Хотя стараются, по возможности, все стороны делать приблизительно одинаковыми, это называется равномерная полигональная сетка.

Почему сетка? Из полигонов создаются трехмерные объекты для игр (и не только), и если рассматривать все эти полигоны издалека включив отображение сторон полигона, они же ребра, то все это похоже на сетку.

Вообще, полигон – это то, чего на самом деле не существует, это всего лишь математика. У него нулевая толщина. Это координаты трех (для игр, далее будем говорить только о треугольных полигонах, если не будет указано иное) точек в трехмерном пространстве. И то, как отображать эти несуществующие полигоны, — зависит исключительно от той или иной компьютерной программы. Как правило, существуют три основных вида отображения: каркасная сетка, полигональная сетка, «чисто» полигональное представление. На рисунке ниже даны все три вида.

При моделировании, тестировании и т.п. для закрашивания плоскости между вершинами полигона используется произвольный цвет, но зачастую это так называемый сермат, RGB 128,128,128. На нем удобно тестировать, в частности, освещение. Да и однородная заливка, в отличии от полновесных текстур, использует не так много ресурсов компьютера.

Приведенный выше рисунок показывает как, на самом деле, с помощью плоских поверхностей создаются объекты практически любой формы. Правило тут простое: чем меньше каждый полигон, тем более точно можно передать форму исходного объекта. Это как с мозаикой, про которую разговор был в первой части. Но и увеличивать до бесконечности количество полигонов тоже нельзя, так как это сказывается на производительности, ведь компьютеру желательно не менее 30 раз в секунду пересчитать положение каждой, по крайней мере видимой зрителю, вершины полигона. Но и эти расчеты лишь малая часть того, что видеокарта в сотрудничестве с ЦП делают для расчета финальной картинки. Но об этом мы поговорим в следующих частях.

Кроме координат вершин есть у полигона такое свойство как нормаль. Это вектор, перпендикулярный одной из сторон плоскости полигона. Так как это вектор, то у него есть направление. Это направление указывает лицевую сторону полигона, вернее его начало находится у лицевой стороны полигона. Нужна нормаль для наложения текстур и расчета освещенности. Так же хочу заметить, что математически полигон не имеет никакого отношения к тому, как он выглядит, т.е. к текстурам. Полигон отдельно, текстуры – отдельно.

Так вот, та сторона, из которой «выходит» вектор нормали — и определяет лицевую сторону полигона, поэтому с противоположной стороны полигон прозрачен при визуализации, вернее обратной стороны просто не существует. Именно поэтому когда игровой персонаж из-за некорректных коллизий проваливается сквозь текстуры, то почти все сверху кажется полупрозрачным – текстур, вернее обратной стороны у полигона не существует.
Ниже скриншот из игры Batman Arkham Knight.

То же можно наблюдать, если попасть туда, куда игровой персонаж попадать не должен по задумке разработчиков: за уступы скал, в закрытые помещения и т.п. И это не баг игрового движка, это его особенность, ну, и недосмотр при тестировании (или разработчики не отреагировали должным образом на отчет тестировщиков). Необходима такая особенность для оптимизации расчетов итоговой картинки, или, по-другому, рендеринга. Ведь совсем незачем тратить вычислительные ресурсы на то, что никто никогда увидеть не должен.

Внимательный читатель может спросить: а как же быть в том случае, если игровой персонаж должен зайти в помещение? Тогда необходимо, как и в реальном здании, придавать стенам (потолку, крыше, дверям и т.д.) толщину с помощью двух полигонов у которых нормали смотрят в противоположные стороны.

Ниже показан фрагмент модели здания со стеной, имеющей реальную толщину. Синим показано направление нормали у полигонов.

Для совсем тонких предметов, таких как лист бумаги, применяется другой подход и зависит он от конкретного игрового движка. Например, может применяться специальный двухсторонний материал или отключаться параметр Backface culling, и в этом случае объект будет выглядеть одинаково с обеих сторон. (Подробнее про Backface culling можно почитать тут: Полигоны, свободная камера и MGS 5)

Теперь поговорим о том, какие бывают трехмерные полигональные модели.

3D модели принято делить на низкополигональные (low poly) и высокополигональные (high poly). Как несложно догадаться – разница в количестве полигонов, причем разница не абсолютная, а, как и все в нашем мире, относительная.

Высокополигональной можно назвать модель с самодостаточной геометрией, т.е. когда даже мелкие детали (в разумных, конечно, пределах) смоделированы с помощью полигонов и при этом модель выглядит достаточно правдоподобно даже без текстур. Непосредственно в играх high poly модели, как правило, не применяются, однако они необходимы в процессе разработки 3D игры, о чем будет рассказано ниже.

Высокополигональные модели применяются для статического рендеринга, например интерьерной визуализации или предметной, когда необходимо фотореалистичное качество и большие разрешения итоговой картинки. Для получения такого изображения используются специальные программы рендеринга и просчет одного кадра может занимать несколько десятков часов, поэтому в играх это неприменимо.

Низкополигональные модели применяются в основном в играх или для анимации, когда визуальный реализм не имеет первостепенного значения. Есть два основных способа получения низкополигональной модели: непосредственное моделирование с прицелом на малое количество полигонов или упрощение высокополигональной модели. То же справедливо и для high poly моделей (если не брать в расчет 3D сканирование): их получают путем усложнения и добавления полигонов низкополигональной модели, либо моделируют сразу из расчета максимально необходимого количества полигонов. Какой подход применятся в том или ином случае зависит от многих факторов, как то: геометрии самой модели, применяемого программного обеспечения, наличия похожих моделей, принятого в студии-разработчике порядка разработки, предпочтений 3D-художника и т.п.

Хочу особо обратить внимание на то, что разделение на high poly и low poly очень и очень относительно. Зависит от многих факторов, и одна и та же модель может быть как низкополигональной, так и высокополигональной. Например, для игры для ПК была разработана высокополигональная модель персонажа, затем из нее получили низкополигональную, условно, уменьшив количество полигонов в два раза. А позже решили портировать игру на мобильные устройства. И количество полигонов, опять же, условно, для этого сократили в четыре раза по сравнению с изначальной моделью, или в два раза по сравнению с моделью для ПК-версии игры. Ведь мобильные устройства мало того, что не такие мощные, как ПК, так еще и размер экрана не позволяет различить мелкие детали, поэтому такая высокая детализация, как для полноразмерного монитора, там попросту не нужна.

Ниже показаны сферы одинакового диаметра с различным количеством полигонов.

Да и с каждым новым процессором, каждой новой видеокарточкой мощность компьютерного железа растет, соответственно, и в играх получается отображать в кадре все больше и больше полигонов без ущерба производительности.

Ниже представлена эволюция полигональной Лары Крофт.

Как же получают 3D модели? Традиционно для этого используется полигональное моделирование в специальных программных пакетах, как то: 3ds Max, Maya, Cinema 4D и т.п. В последнее время к разработке также подключают программы для так называемой трехмерной лепки, к примеру ZBrush и 3D-Coat. Ну, и с развитием технологии 3D сканирования, модели получают путем этого самого сканирования с последующей оптимизацией полигональной сетки.

Если используется традиционное полигональное моделирование, то 3D художнику необходимо от чего-то отталкиваться. Для этого используются так называемые референсы, или, по-другому, изображения того, что необходимо смоделировать. Это могут быть фотографии (для реальных предметов или персонажей), концепт-арты (для вымышленных), или даже существующие объекты. К примеру, необходимо смоделировать существующие кубики Lego. Самая в данном случае хорошая идея – иметь их под рукой и моделировать, непосредственно вертя в руках эти самые кубики.
Ниже изображение референса и 3D модели (без текстур).

Теперь вернемся к вопросу: а зачем при разработке 3D игры иметь в наличии сразу две модели одного и того же объекта — high poly и low poly? Высокополигональная модель непосредственно в игре использоваться не будет, но она необходима для такого важного процесса, как запекание текстур. Так называют подготовку целого ряда текстур или, как их еще называют, карт. Это обычные файлы изображений (с необычными, на первый взгляд, рисунками в них) цель которых как можно реалистичнее представить модель в игре и взять на себя, так сказать, часть геометрии. По сути – это создание 3D иллюзии там, где добиться этого полигонами сложно, неподъемно по производительности или просто нецелесообразно.

Возьмем, к примеру, старую доску. В ней может быть множество трещин и мелких сколов. В реальности это перепад высот (или глубин) материала самой доски. И, по-хорошему, полигоны также должны повторять эти перепады. Но тогда модель может оказаться настолько высокополигональной, что никакое железо не потянет это количество полигонов. Поэтому для начала моделируют как можно более подробную высокополигональную модель этой самой доски, а затем с помощью специальных программ генерируют (запекают) карты высот, затенения и т.п. для имитации этих самых трещин и сколов на низкополигональной модели.

Ниже упрощенно схематически показано как смешивание запеченных с high poly модели карт, в сочетании с текстурой дерева, в результате дает реалистичное изображение.

Ведь как мы различаем эти самые трещинки и сколы? По изменению яркости и цвета текстуры дерева благодаря тому, что свет по-разному отражается от неровностей поверхности, а кое-где и отбрасывает тень. К тому уже, в трещинах, как правило, накапливается грязь и т.п. Все это и позволяет нам отличить старую древесину от новой. Вот эти эффекты и запекаются в файлы изображений. Такой прием позволяет создавать довольно реалистичные объекты с использованием значительно меньшего количества полигонов. При отображении объекта в игре эти карты накладываются друг на друга по определенным алгоритмам, что и создает иллюзию наличия геометрии, которой на самом деле то и нет.

Правда, тут есть один недостаток: эффект лучше всего действует когда наблюдатель расположен под углом 90° относительно полигонов с такой вот имитацией дополнительной геометрии. С уменьшением или увеличением угла обзора «обман» становится виден все отчетливее и отчетливее. Но это неизбежная плата за возможность приблизить картинку к реализму, не превратив игру в неиграбельную.

Это мы забежали немного вперед, затронув тему текстур, но без этого трубно было бы объяснить необходимость на один игровой объект делать как минимум две модели.

Если уж мы говорим про 3D в играх, то следует обязательно упомянуть такую технологию, как LOD, сокращенно от Level of Detail, она же уровень детализации. Суть ее сводится к тому, что в зависимости от того, насколько тот или иной объект близко располагается от игрового персонажа, или какой процент высоты экрана занимает, то используются модели с различного рода детализацией. Опять же, все ради оптимизации. Чем ближе к виртуальной камере тот или иной объект, тем более детализированная модель подставляется. На практике часто количество таких моделей ограничивается тремя: для переднего, среднего и заднего планов. И обозначаются, как правило: LOD_0, LOD_1, LOD_2. Это все варианты низкополигональной игровой модели.

Различные варианты полигональной детализации могут создаваться как в сторонних программах, так и автоматическими игровыми движками (очевидно, не всеми). Причем автоматически в сторону упрощения геометрии, т.е. загруженный в игровой движок вариант по умолчанию воспринимается как LOD_0. Иногда в играх, особенно с большими открытыми мирами, можно заметить как изначально угловатый и неказистый объект вдруг скачкообразно «похорошел» — это произошла замена на более высокополигональный LOD.

Вот, наверно, вкратце и все про полигоны. Если остались необозначенные или нераскрытые моменты – пишите про это в комментариях.

По идее, далее необходимо продемонстрировать этот самый процесс полигонального моделирования, дабы наглядно показать, как это делается. И пример должен быть не очень простой, ради показа хотя бы нескольких приемов моделирования, но и не очень сложный, чтобы не нагонять сон на зрителя. Я думал-думал над предметом моделирования и пришел к выводу, что модель керосиновой лампы неплохо впишется в данную концепцию. Но, дабы не утомлять читателя, решил собственно процесс моделирования вынести в третью, или, если хотите, в 2,5 часть повествования, которую надеюсь опубликовать в ближайшем будущем.

А на этом пока все, до новых встреч!

Введение в компьютерную графику

Как известно, трехмерная графика формируется из треугольных полигонов (face). Они имеют различные материалы, параметры освещенности и текстуры (изображения).

Иллюстрация построения трехмерной графики из треугольных полигонов

Современная видеокарта способна рисовать на экране 200-500 тысяч треугольных полигонов за один кадр с частотой 60 кадров в секунду и выше независимо от размера этих полигонов. Быстродействие видеокарты определяется не размером полигонов, а их количеством. Поэтому при отображении трехмерной графики в реальном времени стоит задумываться об оптимизации моделей.

Обычно для этого используют низкополигональные модели (до 50 тысяч полигонов на модель).

Обмен данными между центральным процессором (CPU) и видеокартой (GPU)

Самым тонким местом при отображении трехмерной графики является шина обмена данными между центральным процессором и видеокартой. Данная шина более быстрая в сторону передачи данных от центрального процессора на видеокарту, и обычно на порядки более медленная в обратную сторону. Причины понятны – обычно большие объемы данных нужно передавать на видеокарту, а не обратно.

Однако и прямая передача данных на видеокарту обладает ограниченной пропускной способностью. Поэтому чем меньше данных будет передаваться по этой шине во время рисования кадра изображения, тем быстрее будет производиться отображение трехмерной графики.

Для оптимизации обмена данными между центральным процессором и видеокартой разработчики трехмерной графики предварительно загружают на видеокарту текстуры и координаты трехмерных объектов.

Вершины

Видеокарта персонального компьютера позволяет на аппаратном уровне рисовать на экране треугольные полигоны, заданные тремя трехмерными координатами (x,y,z). Координаты вершин обычно задают в формате float.

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

Координаты полигонов задаются в предварительно заданной системе координат. Программист трехмерной графики задает сначала локальную систему координат, а затем в ней формирует координаты вершин рисуемых полигонов.

Иллюстрация принципа формирования координат точек в локальной системе координат объекта

Следует отметить, что в трехмерной графике также задаются координаты камеры, откуда производится обзор сцены. Видеокарта автоматически пересчитывает координаты из текущей локальной системы координат в систему координат камеры, а затем в экранные координаты вершин.

Нормали

Кроме координат вершин полигонов задаются координаты нормалей к каждой вершине треугольного полигона. Нормаль – это перпендикуляр к касательной плоскости в точке вершины. В общем случае ориентация всех трех вершин трехмерного полигона может быть различна, что создает эффект выпуклости или вогнутости полигона. На самом деле полигон не изгибается, его границы остаются ровными линиями, соединяющими вершины. Он лишь закрашивается так, как будто он имеет изгиб. Так, например, полигон сферы имеет три нормали, каждая из которых направлена в своем направлении. Видеокарта рассчитывает освещенность в каждой вершине и интерполирует освещенность в промежуточных точках полигона. В результате сфера на экране выглядит как сфера, а не как правильный многогранник, даже при небольшом числе полигонов.

Иллюстрация эффекта выгнутости треугольного полигона путем задания ему трех несонаправленных нормалей.

Координаты каждой нормали (Nx, Ny, Nz) также задаются в локальной системе координат. Длина вектора нормали должна быть равна 1.

Материалы

Цвет и параметры блеска, которые используются для закраски полигона, задаются с помощью материалов. Текущий материал, используемый при рисовании, устанавливается программистом перед рисованием очередной группы полигонов. Блеск материала определяется путем расчета ориентации полигона к источнику света.

Источники света

Источники света задаются программистом предварительно перед рисованием всей трехмерной сцены. Источники света определяются координатами положения в пространстве, направлением и конусом свечения (если это точечный источник света), а также цветом излучения. Видеокарта автоматически производит расчет освещенности рисуемых полигонов в зависимости от материала полигонов.

Текстуры

Для закраски полигона могут использоваться текстуры (изображения). Изображения всех текстур загружается в память видеокарты на этапе загрузки сцены. Каждой загруженной текстуре присваивается дескриптор (порядковый номер) текстуры. При рисовании текстуры процессор указывает лишь этот дескриптор.

Важное требование – размер изображения текстуры по длине и ширине должен быть кратен степени 2. Т.е. подходят размеры 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048. Некоторые современные видеокарты (отнюдь не все!) поддерживают расширение ARB_texture_non_power_two, позволяющее отображать текстуры, размер которых не кратен степени 2. Однако для совместимости со всеми видеокартами желательно все же использовать текстуры, кратные степени 2.

Желательно чтобы размер текстуры был не более, чем 512×512. Использование текстур больших размеров связан с дополнительными ограничениями скорости отрисовки таких текстур, т.к. видеокарта размещает такую текстуру в разные аппаратные блоки.

Текстурные координаты

Чтобы разместить текстуру на объекте у каждой вершины полигона задаются текстурные координаты (u,v). Текстурная координата u вершины – это нормированная координата X в системе координат изображения, соответствующая данной вершине. А координата v – это координата Y в системе координат изображения. Нормировка производится длиной и шириной изображения соответственно по u и по v.

Иллюстрация использования текстурных координат

Следует отметить, что видеокарта за один проход рендера позволяет накладывать на один полигон сразу несколько текстур, каждая из которых может содержать свои текстурные координаты, однако в нашем случае механизм мультитестурирования рассмотрен не будет.

Индекс-буферы

Для оптимизации объема данных, передаваемых на видеокарту от центрального процессора, уменьшения объема памяти, необходимого для хранения информации о трехмерной фигуре, а также для ускорения рисования используют технологию индекс-буферов.

Заметьте, что, например, у сферы каждая вершина используется как минимум 4 смежными полигонами, а полюса 32 сегментной сферы – аж 32 полигонами! Зачем каждому их этих полигонов хранить и всякий раз при рисовании пересчитывать координаты этой общей вершины? Можно отдельно хранить и расчитывать координаты всех вершин, нормалей и текстурных координат (вертекс-буфер), а отдельно хранить последовательность соединения этих вершин полигонами (индекс-буфер).

В индекс-буфере хранится массив номеров вершин. Размер массива всегда кратен 3. Каждые три числа их этого массива формируют номера вершин для рисования одного полигона.

Иногда индекс буфер рассматривают как массив из n троек чисел. Каждую такую тройку номеров вершин называют face.

Пояснение понятия индекс-буфера

Отсечение невидимых поверхностей. Z-буфер

При выводе полигонов на экран видеокарта отсекает невидимые полигоны, применяя технологию Z-буфера. Технология подразумевает, что помимо буфера с цветом пикселей экрана (цветового буфера) будет организован буфер с координатами глубины (Z-буфер). Z-буфер имеет ту же размерности, что и цветовой буфер, а каждый его пиксель соответствует пикселю экрана. В буфере глубины хранится экранная глубина пикселя. Изначально во все пиксели буфера глубины записывается значение бесконечной глубины. Перед выводом каждый пиксель полигона проверяет собственную глубину с глубиной, записанной в Z-буфере в тех же координатах. Если записанная в Z-буфер глубина меньше, чем глубина пикселя полигона, то данный пиксель не рисуется ни в цветовом буфере, ни в буфере глубины. Иначе пиксель отображается в цветовом буфере, а в буфер глубины записывается глубина нарисованного пикселя.

Иллюстрация отсечение невидимых поверхностей с помощью технологии Z-буфера.

На рисунке цветовой буфер совмещен с Z-буфером. Цифра в ячейках показывает значение, записанное в пиксель Z-буфера. Цвет ячейки позывает значение цвета, записанное в пиксель цветового буфера. Полигоны 1, 2, 3 выводились на экран в указанной последовательности. При этом глубина всех пикселей полигона 1 равна 5 условных единиц глубины, глубина всех пикселей полигона 2 равна 9, а глубина всех пикселей полигона 3 равна 2. Заметьте, что при попытке нарисовать полигон 2 в пикселях, перекрытых полигоном 1, рисование не производилось, т.к. в буфере глубины в данных пикселях было значение (5) меньше, чем глубина (9) пикселей полигона 2. При рисовании полигона 3 проверка глубины во всех случаях завершилась успехом, и все пиксели полигона были отрисованы в цветовом буфере.

В общем случае глубина полигона в разных его точках различна, она линейно интерполируется от вершины к вершине.

Отсечение невидимых поверхностей по технологии CULL_FACE

В трехмерной графике трехмерные фигуры обычно представляют собой закрытые объемы. При этом обычно число полигонов с передней стороны таких объектов примерно совпадает с числом полигонов с задней стороны объекта. Повернутые к камере задней своей стороной полигоны никогда не бывают видимы.

Определить какой стороной повернут к камере полигон до его рисования на экране достаточно просто. Поэтому число рисуемых полигонов можно раза в два сократить (повысив быстродействие) путем предварительного просчета их ориентации к камере.

Передняя и задняя сторона полигона определяется по направлению обхода вершин. По умолчанию, передней стороной полигона (независимо от направления нормалей) является та, с которой вершины обходятся против часовой стрелки. Однако для видеокарты можно установить и обратный порядок обхода вершин.

Отсечение невидимых полигонов, повернутых к камере своей задней стороной, называется технологией CULL_FACE или технологией односторонних полигонов.

Создание трехмерных моделей

Разработка моделей с помощью трехмерных редакторов

Координаты полигонов можно задавать непосредственно в коде программы, однако такой способ рисования очень не удобен. Попробуйте задать координаты 100000 треугольных полигонов вручную!

Поэтому трехмерную сцену рисуют в каком-либо 3D-редакторе, экспортируют в файл, который потом загружаются через графический движок. В качестве такого редактора удобно использовать 3D Studio MAX.

При подобном подходе встает вопрос об описании формата хранения трехмерной сцены.

Соображения о формате хранения трехмерной сцены

Удобно, чтобы информация о трехмерной сцене хранилась не в виде разрозненного массива координат полигонов, а в виде организованной структуры, разделенной, как минимум, на трехмерные объекты.

В большинстве случаев трехмерные геометрические объекты не изменяют своей формы, а лишь вращаются и перемещаются по сцене. Поэтому удобно задать у каждого объекта некую локальную систему координат, связанную с данным объектом. А уже в этой системе координат формировать координаты вершин и нормалей полигонов, образующих поверхность данного трехмерного объекта. В таком случае координаты вершин и нормалей будут оставаться неизменными, а будет перемещаться лишь локальная система координат. В этом случае удобно будет передать координаты вершин в память видеокарты и больше не трогать.

Порядки обхода вершин (индекс-буферы) удобно сгруппировать по материалам. Если у объекта один материал, то и индекс-буфер тоже будет один. Если у объекта несколько материалов, то для каждого материала удобно создать отдельный индекс-буфер, который будет хранить информацию о полигонах, рисуемых этим материалом.

Удобно хранить геометрический объект (mesh) отдельно от трехмерного объекта, задающего его систему координат. В этом случае, если на сцене несколько одинаковых объектов, отличающихся лишь положением в пространстве, то под каждый такой объект можно создать отдельный трехмерный объект и поместить в него указатель на один и тот же геометрический объект (mesh). Это значительно сократит память для хранения таких объектов.

Кроме того, трехмерный объект может использоваться не только для геометрического объекта, но и использоваться как система координат источника света или камеры. По этим двум причинам удобно, чтобы трехмерный объект был отделен от геометрического объекта.

Трехмерные объекты удобно организовать в виде дерева. Каждый объект будет содержать ссылки на родительский объект, первый дочерний объект, а также на предыдущий и следующий объект одного уровня иерархии. Тем самым будут определяться привязки объектов друг к другу. Совместно с объектом удобно хранить его название, заданное в трехмерном графическом редакторе. По этому имени затем можно производить поиск объекта на трехмерной сцене для управления им.

Для формирования локальной системы координат трехмерного объекта удобно использовать матрицы преобразования 4×4. Причем у каждого объекта удобно хранить две матрицы преобразования: локальную матрицу преобразования (matrix), которая определяет положение и ориентацию объекта относительно своего родительского объекта, а также мировую матрицу преобразования (worldMatrix), которая определяет положение объекта в мировой системе координат. Мировые матрицы преобразования будут автоматически рассчитываться перед рисованием очередного кадра на основе информации локальных матриц преобразования.

В силу того, что обычно несколько объектов трехмерной сцены используют один и те же текстуры, удобно организовать менеджер текстур, который позволяет загружать каждую текстуру лишь один раз, а затем использовать ссылки на нее. Такому менеджеру текстур удобно написать метод запроса текстуры с указанным названием (именем файла), а менеджер просмотрит уже загруженные текстуры и вернет указатель на текстуру, если она уже загружена в память видеокарты. Если же текстура с указанным названием еще не загружена, то данный метод ее загрузит. Такой менеджер текстур существенно упростит процесс разработки трехмерного приложения.

Рекомендуемый формат описания 3D-сцены

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

Во главе структуры стоит объект CScene. В нет размещена ссылка на все трехмерные объекты (objects) и ссылка на менеджер текстур.

Менеджер текстур (CTextureManager) хранит ссылки на все текстуры сцены, позволяет их загружать с диска, следя за тем чтобы одна и та же тестура не загружалась дважды.

Текстуры организованы в виде двунаправленной очереди. Каждая текстура имеет ссылку на следующую текстуру очереди (next) и предыдущую текстуру очереди (prev).

Изображения текстур всегда загружаются в память видеокарты и храняться только там. В каждом классе CTexture имеется дескриптор handle, указывающий на текстуру в памяти видеокарты.

Трехмерные объекты (CObject3D) организованы в виде дерева. Свойства child и parent указывают соответственно на дочерний (привязанный) и родительский объект. Свойство next и prev указывают на объекты одного уровня иерархии.

Объект может содержать ссылку на камеру (camera), источник света (light) или на геометрический объект (mesh). Кроме того объект хранит локальную матрицу преобразования (matrix) и мировую матрицу преобразования (worldMatrix).

Локальная матрица преобразования является первопричинной матрицей. Она загружается из файла экспорта. Матрица содержит информацию о преобразовании координат от текущего объекта в родительский объект.

Мировая матрица преобразования используется только на этапе прорисовки сцены (render). Первый этапом прорисовки с помощью метода object->UpdateMatrix() производится пересчет мировых матриц всех объектов по информации, хранимой в локальных матрицах преобразования (matrix).

Кроме всего прочего объект хранит свое название, которое ему дали в 3D Studio MAX.

Камеры сцены содержат поле FOV — угол обзора. Также имеется ссылка объект, к которому принадлежит камера. Объект сцена (CScene) содержит ссылку на активную камеру сцены. Камер на сцене может быть несколько, но только одна активная. По умолчанию активная камера задается в 3D Studio MAX.

Источники света содержат информацию о цвете (color) источника, признак включения/выключения (onOff), а также прочие свойства источников света. Также класс CLight содержит указатель на объект, к которому он принадлежит.

Геометрические объекты (CMesh) имеют ссылку на один или несколько индексных буферов, а также на вершины, нормали и текстурные координаты (вертексный буфер). Вертексный буфер для совместимости с возможностью загрузки информации на видеокарту организован специальным образом — выделяется единый непрерывный блок памяти для хранения в нем вершинных координат, нормалей и текстурных координат. Таким образом ссылка vertices указывает на начало этого буфера, а normals и texCoords указывают на части этого буфера. Количество вершин, нормалей и текстурных координат одинаково и равно vCount.

Вертексный буфер может быть размещен в памяти видеокарты. В этом случае дескриптор handle класса CMesh указывает на данный буфер в видеопамяти. При загрузке вертексного буфера в память видеокарты из памяти центрального процессора он удаляется (ссылки vertices, normals, texCoords указывают на NULL).

Индексный буфер содержит информацию о порядке соединения вершин полигонами (face). Каждый индексный буфер отвечает за нанесение на объект одного из материалов.

Индексный буфер содержит ссылку на массив индексов indices из count элементов (count всегда кратно 3). Каждая тройка индексов из этого массива содержит номера вершин, соединив которые образуется очередной трехмерный полигон. Все полигоны одного индексного буфера имеют один и тот же материал. Также индексный буфер содержит ссылки на материал (material) и следующий индексный буфер (next), если он есть у данной mesh. Последний индексный буфер данного mesh указывает на NULL.

Индексный буфер может быть загружен в память видеокарты. В этом случае дескриптор handle класса CIndexBuffer указывает на данный буфер в памяти видеокарты. Если индексный буфер находится в памяти видеокарты, то массив индексов из памяти центрального процесса удаляется, а указатель indices класса CIndexBuffer указывает на NULL.

Материал (CMaterial) содержит 4 цвета материала (ambientColor — цвет в тени, diffuseColor — основной цвет объекта, specularColor — цвет блика, emmisionColor — цвет самосвечения), параметр глянца (glossiness), свойство twoSided сообщающее OpenGL о необходимости использования двухсторонних материалов. Также материал содержит указатель на текстуру (если она есть) и матрицу преобразования данной текстуры (matrix). Матрица преобразования текстуры содержит информацию о повороте, повторе и смещения текстуры.

Рекомендуемый формат описания 3D-сцены. В скачиваемом файле представлен код заголовочного файла (h-файла), в котором может быть описана 3D-сцена. Студент должен самостоятельно создать соответствующий cpp-файл, в котором будут реализованы предлагаемые функции и методы классов, заголовок которых описан в h-файле.

#ifndef glUnitH #define glUnitH #include #include  #include  #include #pragma comment (lib, "opengl32.lib") #pragma comment (lib, "glu32.lib") #pragma warning( disable: 4996 ) #pragma warning( disable: 4244 ) #pragma warning( disable: 4312 ) #pragma warning( disable: 4311 ) typedef struct < float x,y,z; > VECTOR; typedef struct < float u,v; > TEXCOORD; typedef struct < float r,g,b,a; > COLOR3D; // MATRIX A;  typedef union < struct < VECTOR rowX; float zero0; VECTOR rowY; float zero1; VECTOR rowZ; float zero2; VECTOR position; float one; > rows; > MATRIX; class CTextureManager; class CMesh; class CObject3D; class CScene; class CTexture < public: >; class CTextureManager < public: CTexture *Get(const char *texName); >; class CMaterial < public: >; class CIndexBuffer < public: >; class CMesh < public: void SetVCount(int n, bool isNormals, bool isTexCoord); >; class CLight < public: void Render(int n); >; class CCamera < public: CCamera(CObject3D *obj); ~CCamera(); >; class CObject3D < public: CObject3D *next,*prev,*child,*parent; CObject3D *id; void SetParent(CObject3D *newParent); void UpdateTransform(); void RenderLights(int *n); void Render(); CObject3D *FindObject(const char *Name); void Translate(float x, float y, float z); void RotateX(float a); void RotateY(float a); void RotateZ(float a); >; void MatrixMul(MATRIX &res, const MATRIX &M1, const MATRIX &M2); class CScene < public: void UploadToGPU(); >; #endif 

Код инициализации OpenGL

Предлагаемый код инициализации OpenGL:

 #include #include  . dc = DC; width = w; height = h; PIXELFORMATDESCRIPTOR PFD; memset(&PFD,0,sizeof(PFD)); PFD.nSize=sizeof(PFD); PFD.nVersion = 1; PFD.dwFlags=PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER; PFD.iPixelType = PFD_TYPE_RGBA; PFD.iLayerType = PFD_MAIN_PLANE; PFD.cColorBits = 32; PFD.cDepthBits = 32; int f = ChoosePixelFormat(dc, &PFD); if (!f) ; SetPixelFormat(dc, f, &PFD); rc = wglCreateContext(dc); wglMakeCurrent(dc, rc); glViewport(0,0,width, height); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glAlphaFunc(GL_GREATER,0.3f); glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); ; >

Предлагаемый код деинициализации 3D:

 child) child; textureManager.Clear(); wglMakeCurrent(0, 0); if (rc) < wglDeleteContext(rc); rc = 0; >>

Пример рисования в OpenGL. Простой, но медленный код

Далее приводится пример рисования произвольных фигур в OpenGL. Данный код медленный, поэтому использовать его не рекомендуется.

// рисование произвольных фигур из треугольников // установить матрицу преобразования glMatrixMode( GL_MODELVIEW); // режим матриц сцены glPushMatrix(); // запоминить текущую матрицу glMultMatrixf( obj->worldMatrix.m[0] ); // домножить на матрицу объекта material->Bind(); // установить материал glBegin(GL_TRIANGLE); // начать создание фигуры // треугольник 1 glNormal3f( normal1A.x, normal1A.y, normal1A.z); // нормаль точки 1 glTexCoord2f( texCoord1A.x, texCoord1A.y, texCoord1A.z); // текст. координаты точки 1 glVertex3f( vertex1A.x, vertex1A.y, vertex1A.z); // вертекст точки 1 glNormal3f( normal1B.x, normal1B.y, normal1B.z); // нормаль точки 2 glTexCoord2f( texCoord1A.x, texCoord1A.y, texCoord1B.z); // текст. координаты точки 2 glVertex3f( vertex1B.x, vertex1B.y, vertex1B.z); // вертекст точки 2 glNormal3f( normal1C.x, normal1C.y, normal1C.z); // нормаль точки 3 glTexCoord2f( texCoord1C.x, texCoord1C.y, texCoord1C.z); // текст. координаты точки 3 glVertex3f( vertex1C.x, vertex1C.y, vertex1C.z); // вертекст точки 3 // треугольник 2 glNormal3f( normal2A.x, normal2A.y, normal2A.z); // нормаль точки 1 glTexCoord2f( texCoord2A.x, texCoord2A.y, texCoord2A.z); // текст. координаты точки 1 glVertex3f( vertex2A.x, vertex2A.y, vertex2A.z); // вертекст точки 1 glNormal3f( normal2B.x, normal2B.y, normal2B.z); // нормаль точки 2 glTexCoord2f( texCoord1A.x, texCoord2A.y, texCoord2B.z); // текст. координаты точки 2 glVertex3f( vertex2B.x, vertex2B.y, vertex2B.z); // вертекст точки 2 glNormal3f( normal2C.x, normal2C.y, normal2C.z); // нормаль точки 3 glTexCoord2f( texCoord2C.x, texCoord2C.y, texCoord2C.z); // текст. координаты точки 3 glVertex3f( vertex2C.x, vertex2C.y, vertex2C.z); // вертекст точки 3 . glEnd(); glPopMatrix(); // восстановить матрицу

Данный способ рисования подходит для рисования отдельных небольших объектов, например, для рисования различных эффектов, 3D-элементов интерфейса, наложенного поверх трехмерного изображения и т.п.

Рекомендуемый быстрый способ рисования трехмерных фигур

Данный способ рисования трехмерных фигур подразумевает наличие вертексных-буферов и индекс-буферов, поэтому наиболее подходит для предлагаемой структуры 3D-сцены. Кроме того, т.к. вся геометрия загружается в GPU одним массивом (одной командой), данный код позволяет сократить число обращений к видеокарте, а, следовательно, ускорить прорисовку.

Данный способ позволяет обращаться к видеокарте с использованием индекс-буфера, что в некоторых случаях позволяет сократить объем данных, передаваемых видеокарте. Меньше данных — быстрее отрисовка.

В коде приводится закомментированный вариант, использующий аппаратные индекс-буферы и вертексные буферы. Данные вариант предназначен для продвинутых студентов, скачавших библиотеки расширение OpenGL (см. ниже). Аппаратные буферы предварительно должны быть созданы и загружены в память видеокарты. Это позволяет вообще избавиться от необходимости передачи какие-либо массивов данных в видеокарту во время отрисовки кадра, что позволяет в несколько раз повысить скорость прорисовки кадра.

CMaterial defMaterial; // материал по умолчанию // отрисовка индекс-буфера void CIndexBuffer::Render(CMesh *mesh) < if (material) material->Bind(); // установить материал индекс-буфера, если есть else < // установить материал по умолчанию, установив ему цвет объекта defMaterial.ambientColor = mesh->wireColor; defMaterial.deffuseColor = mesh->wireColor; defMaterial.Bind(); > if (!handle) < // отрировать, если индекс-буфер не загружен в GPU if (isOpenGL11) < // быстрая прорисовка glDisableClientState(GL_INDEX_ARRAY); glDrawElements(GL_TRIANGLES, сount, GL_UNSIGNED_INT, indices); >else < // если нет поддержки быстрой прорисовки. Прорисовка первым методом: int i; glBegin(GL_TRIANGLES); for(i = 0; i < count; i++) < if(mesh->hasNormals) glNormal3fv(&mesh->normals[ indices[i] ].x); if(mesh->hasTexCoord) glTexCoord2fv(&mesh->texCoords[ indices[i] ].u); glVertex3fv(&mesh->vertices[indices[i] ].x); > glEnd(); > > else <  // отрировать, если индекс-буфер загружен в GPU // (в случае использования расширения openGL) /* glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, handle); glEnableClientState(GL_INDEX_ARRAY); glIndexPointer(GL_UNSIGNED_INT, 0, 0); glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, NULL); glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); */  > > void CMesh::Render(CObject3D *obj) < // установить матрицу преобразования glMatrixMode( GL_MODELVIEW); // режим матриц сцены glPushMatrix(); // запоминить текущую матрицу glMultMatrixf( obj->worldMatrix.m[0] ); // домножить на матрицу объекта if (isOpenGL11) < // если есть поддержка быстрой прорисовки // установить вертекс-буфер glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT); // применить вертексные буфера if (!handle) < // если геометрия не загружена в GPU glVertexPointer(3, GL_FLOAT, 0, vertices); glEnableClientState(GL_VERTEX_ARRAY); if (hasNormals) < glNormalPointer(GL_FLOAT, 0, normals); glEnableClientState(GL_NORMAL_ARRAY); >if (hasTexCoords) < glTexCoordPointer(2, GL_FLOAT, 0, texCoords); glEnableClientState(GL_TEXCOORD_ARRAY); >> else <  // если геометрия была загружена в GPU /* int offset = 0; // установить текущий аппаратный буфер glBindBufferARB(GL_ARRAY_BUFFER, handle); // установить в OpenGL смещение вертекс-буфера glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, (void*)offset); offset += (int)sizeof(VECTOR) * vCount; // установить в OpenGL смещение буфера нормалей if (hasNormals) < glNormalPointer(GL_FLOAT, 0, (void*)offset); glEnableClientState(GL_NORMAL_ARRAY); offset += (int)sizeof(VECTOR) * vCount; >// установить в OpenGL смещение буфера текстурных координат if (hasTexCoords) < glTexCoordPointer(2, GL_FLOAT, 0, (void*)offset); glEnableClientState(GL_TEXCOORD_ARRAY); >*/  > > // установить направление обхода if (ccw) glCullFace(GL_FRONT); else glCullFace(GL_BACK); // разрешить Z-буфер (мало ли кто его запретил) glDepthMask(TRUE); glEnable(GL_DEPTH_TEST); // рисуем индекс-буферы CIndexBuffer *ibuf = indexBuffers; while(ibuf) < ibuf->Render( this ); ibuf = ibuf->next; > // деинициализация аппаратного буфера if (handle) glBindBufferARB(GL_ARRAY_BUFFER, 0); // вернуть прошлые атрибуты if (isOpenGL11) glPopClientAttrib(); glPopMatrix(); // восстановить матрицу >

Следует отметить, что быстрая прорисовка будет работать не на всех видеокартах. Поэтому перед его использованием следует проверить совместимость видеокарты.

Далее приводится код проверки совместимости видеокарты:

bool isOpenGL11 = false; const char * extensions = (const char *) glGetString ( GL_EXTENSIONS ); if (strstr(extensions, "GL_EXT_draw_range_elements")) < // проверка пойдена isOpenGL11 = true; >

Экспорт моделей из 3D Studio MAX

С помощью данного скрипта на языке MAX Script можно экспортировать модели из 3D Studio MAX в простой и удобный бинарный формат.

Описание формата файла экспорта.

    Один раз за сеанс работы 3D Studio MAX запустите скрипт cg.ms.

Первый пункт можно не выполнять, если файл cg.ms скопировать в подпапку scripts/startup в директории 3D Studio MAX. В этом случае данный скрипт попадет в автозагрузку 3D Studio MAX.

Шаблон кода загрузчика cg-файлов

Приведенный ниже пример кода поможет написать загрузчик cg-файла:

  unit3D.h"; .  *fileName) < FILE *f;   *body;  f = fopen(fileName, "; fseek(f, 0, SEEK_END); // указатель чтения из файла на конец файла bodySize=( if (bodySize < 4) < fclose(f); ; > body = (*)malloc( bodySize ); fread( body, bodySize, 1, f); fclose(f); int objectCount; CObject3D **objects = NULL;  CObject3D *obj = NULL; CMesh *mesh = NULL; CIndexBuffer *indexBuffer = NULL; CMaterial *material = NULL;      memcpy(&tagName, body + pos, 4); pos+=4; if (strcmp(tagName, "; > memcpy(&tagName, body + pos, 4); pos+=4; memcpy(&tagSize, body + pos, 4); pos+=4; tagPos = pos;   bodySize)   obj =   obj->SetName( buf ); memcpy(&obj->id, body + pos, 4); pos+=4; memcpy(&obj->parent, body + pos, 4); parent не действительный указатель. //т.к. мы прочитали в 32-битный адрес объекта 32-битный //индентификатор объекта. Но другого специального буфера //для временного хранения идентификатора родительского объекта //у нас нет, в то время как сам указатель obj->parent //нам на этапе чтения файла не нужен. //После чтения всего файла необходимо будет связать объекты, //используя считанные в поле obj->parent идентификаторы. pos+=4; memcpy(&obj->matrix.rows.row0, body + pos, 12); pos+=12; memcpy(&obj->matrix.rows.row1, body + pos, 12); pos+=12; memcpy(&obj->matrix.rows.row2, body + pos, 12); pos+=12; memcpy(&obj->matrix.rows.position, body + pos, 12); pos+=12; > mesh = mesh; . > >  int n; memcpy(&n, body + pos, 4); pos+=4; indexBuffer->SetCount(n); memcpy(indexBuffer->indices, body + pos, n *  ")==0) < material = material; . > >  ")==0) <    material->texture = scene->textureManager.Get(buf); memcpy(&material->matrix.rows.row0, body + pos, 12); pos+=12; memcpy(&material->matrix.rows.row1, body + pos, 12); pos+=12; memcpy(&material->matrix.rows.row2, body + pos, 12); pos+=12; memcpy(&material->matrix.rows.position, body + pos, 12); pos+=12; > >  int n; memcpy(&n, body + pos, 4); pos+=4; mesh->SetVCount(n, mesh->normals != texCoord != memcpy(mesh->vertices, body + pos, n *  int n; memcpy(&n, body + pos, 4); pos+=4; mesh->SetVCount(n, texCoord != memcpy(mesh->normals, body + pos, n *  int n; memcpy(&n, body + pos, 4); pos+=4; mesh->SetVCount(n, mesh->normals != memcpy(mesh->texCoords, body + pos, n *  ")==0) < . > . > . > . > pos = tagPos + tagSize; > free(body); parent) < CObject3D *searchID = objects[i]->parent; objects[i]->parent = NULL; id == searchID) < objects[i]->SetParent( objects[j] ); parent) objects[i]->SetParent( scene->objects ); > free(objects); ; >

Ниже предстален код, позволяющий считывать тип данных STRING. Данная функция используется в загрузчике, поэтому она должна быть объявлена выше, чем функция загрузчика.

  *body,     ) ( p++; > str[i] = 0; *pos = p + 1; 

Для продвинутых студентов: расширения OpenGL

Студентам, которые хотят изучить возможности OpenGL сверх программы курса "Компьютерная графика", предлагается установить расширения OpenGL.

Данный архив сдедует распаковать в папку с проектом, подключить с помощью директивы #include бибиотеку glext.h и libExt.h, а также включить в проект файл libExt.cpp

Расширения OpenGL позволяют использовать мультитекстурирование, конвеерную обработку текстур, сжатые текстуры, вертекстые и текстурные шейдеры, аппаратные индексные и вертексные буферы и прочие расширенные возможности OpenGL.

Все расширения OpenGL можно разбить:

  • общие расширения, поддерживаемые всеми производителями видеокарт (функции и константы этого расширения имеют окончание _EXT или _ARB, например GL_COMBINE_EXT);
  • на расширения, поддерживаемые картами nVidia (расширения имеют окончание _NV);
  • на расширения, поддерживаемые картами ATI (расширения имеют окончание _ATI).

Рекомендуется использовать только общие расширения (_EXT или _ARB). В этом случае разработанное приложение будет работать на всех современных видеокартах.

Полное описание расширений можно найти в google.

Проверка поддержки аппаратных вертекс-буферов и индекс-буферов

#include #include #include #include "libExt.h" #include "glExt.h" . initExtensions(); if (isExtensionSupported("GL_ARB_vertex_buffer_object")) < // имеется поддежка аппаратных буферов >

Пример загрузки индекс-буфера в GPU

GLuint handle; // переменная, объявленная в классе CIndexBuffer . glGenBuffersARB(1, &handle); // создать один буфер и записать его дескриптор в handle glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, handle); // сделать буфер текущим // передать данные из массива indices в аппаратный буфер в GPU glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 4 * indexCount, indices, GL_STATIC_DRAW_ARB); // сделать текущим пустой буфер glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); // освободить память от индексов, т.к. они записаны в GPU, и в CPU они больше не нужны free(indices); indices = NULL;

Пример загрузки вертексов, нормалей и текстурных координат в GPU

Как уже отмечалось выше, следует так организовать память под хранение вертексов, нормалей и текстурных координат, чтобы они все хранились в одном блоке памяти, зарезервированного функцией malloc. В этом большом буфере памяти сначала должен быть записан массив всех вертексов, сразу же за ним массив всех нормалей (если они есть), а сразу же за ним массив всех текстурных координат (если они есть). Таким образом, указатель vertices будет указывать на начало этого блока памяти.

Сформированный таким образом буфер передается на видеокарту в виде одного блока, в котором есть и вертексы, и нормали и текстурные координаты.

GLuint handle; // переменная, объявленная в классе CMesh . // определить размер вертекс-буфера в зависимости от наличия нормалей и текст. координат int sz = sizeof(VECTOR) * vCount; if (hasNormals) sz+=sizeof(VECTOR) * vCount; if (hasTexCoords) sz+=sizeof(TEXCOORD) * vCount; glGenBuffersARB(1, &handle); // создать один буфер и записать его дескриптор в handle glBindBufferARB(GL_ARRAY_BUFFER_ARB, handle); // сделать буфер текущим // передать данные из буфера vertices (в нем же нормали и текстурные координаты) в аппаратный буфер в GPU glBufferDataARB(GL_ARRAY_BUFFER_ARB, sz, vertices, GL_STATIC_DRAW_ARB); // сделать текущим пустой буфер glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); // освободить память от буфера, т.к. массив уже записан в GPU, и в CPU он больше не нужен free(vertices); vertices = NULL; normals = NULL; texCoords = NULL;

Следует отметить, что константу GL_STATIC_DRAW_ARB имеет смысл заменить на GL_DYNAMIC_DRAW_ARB, если подразумевается дальнейшая манипуляция координатами геометрии, например, в случае с Skin.

Удаление аппаратного буфера

if (handle) glDeleteBuffersARB(1, &handle);

Пример простейшей программы под windows для Microsoft Visual C++

Если разрабатывается проект на Microsoft Visual C++, то следует сначала создать минимальную программу под Windows, в которую будет внедрен код отрисовки. Пример такой программы приведен ниже:

#include // #include "glUnit.h" // подключение Вашего модуля с графикой //----- глобальные переменные ----- HINSTANCE instance; // дескриптор приложения HWND gWnd; // дескриптор главного окна HDC gDC; // дескриптор холста (device context) главного окна //---- на каждое событие назначим функцию обработки данного события ---- // событие создания окна void OnCreate(HWND wnd) < gWnd = wnd; gDC = GetDC(wnd); // получить device context окна // инициализация графики //. >// событие изменение размеров окна void OnResize() < // получить новые размеры окна RECT r; GetClientRect(gWnd, &r); // установить размер окна для графики r.right x r.bottom //. >// холостой ход приложения (отсутствие событий) void OnIdle() < // Отрисовать кадры // . >// Событие закрытие приложения. void OnClose() < // деинициализация графики // . ReleaseDC(gWnd, gDC); >// Событие нажатия клавиши на клавиатуре void OnKeyDown(short key) < switch(key) < case VK_ESCAPE: // клавиша ESC CloseWindow(hWnd); break; >> //----- функция обработки событий главного окна ---------- LRESULT CALLBACK WindowProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam) < switch(msg) < case WM_CREATE: OnCreate(wnd); break; case WM_SIZE: OnResize(); break; case WM_KEYDOWN: OnKeyDown((short)wParam); break; case WM_CLOSE: OnClose(); DestroyWindow(wnd); PostQuitMessage(0); break; >return DefWindowProc(wnd, msg, wParam, lParam); > //--------------- главная функция приложения ------------------ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) < // запомнить декриптор приложения instance = hInstance; // зарегистрировать класс главного окна WNDCLASS cls; cls.cbClsExtra = 0; cls.cbWndExtra = 0; cls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); cls.hCursor = LoadCursor(0, IDC_ARROW); cls.hIcon = 0; cls.hInstance = instance; cls.lpfnWndProc = WindowProc; cls.lpszClassName = "MyClass"; cls.lpszMenuName = NULL; cls.style = 0; RegisterClass(&cls); // создать главное окно HWND wnd = CreateWindowEx(0, cls.lpszClassName, "Моя первая программа", WS_OVERLAPPEDWINDOW, 0, 0, 800, 600, 0, 0, instance, 0); ShowWindow(wnd, SW_SHOW); // главный цикл приложения MSG msg; while(1) < if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) // есть событие для приложения? < // Обработать событие if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); >else < // нет события - холостой ход OnIdle(); >> // выход return 0; >;

Следует отметить, что данная программа для простоты не использует Unicode-кодировку. Для этого следует перед компиляцией в свойствах проекта (меню "Project | xxx Properties. ") отключить UNICODE-кодировку (В списке "Configuration Properties -> General" найти свойство "Character Set" и установить его равным "Use Multi-Byte Character Set").

Демонстрационный проект

Демострационный проект, позволяющий воспроизводить 3D-графику средствами OpenGL с применением аппаратных буферов:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *