2004 г.
OpenGL: раскрой глаза на трехмерную графику
Арсений Чеботарёв, Издательский Дом "КОМИЗДАТ"
Современный мир уже немыслим без трехмерной графики - в том числе интерактивной. В свое время было предпринято множество попыток создать универсальный язык описания трехмерных сцен. Самой удачной оказалась попытка компании Silicon Graphics - ее язык OpenGL получил всемирное признание
Многие из вас знают, что все самые красивые спецэффекты, а также почти вся промышленная 3D графика рендерится в формате OpenGL. Этот формат был разработан Silicon Graphics и в настоящее время стал стандартом де-факто для трехмерного моделирования. Я попытаюсь дать краткий обзор этой технологии, и во второй части статьи наш "заслуженный дельфиец" Миша Продан покажет пару примеров ее использования, разумеется, средствами Delphi.
Принципы
Основная идея OpenGL: графическая библиотека должна быть аппаратно независимой, но в то же время использовать аппаратные ускорители, если они доступны. Кроме того, этот язык с самого начала предусматривал механизм расширения и гибкости - по мере того как расширения становились "общепринятыми", они становились частью следующего релиза.
Для любого протокола, будь то сетевой протокол или язык описания сцен, важным является вопрос уровня абстракции - то есть того, на каком уровне работает данная система или протокол, что является входными данными и что выходными, какие компоненты будут взаимодействовать в качестве поставщиков и приемников данных. Говоря попросту, нужно определиться по вопросу "что мы делаем и чего не делаем".
Создатели OpenGL планировали свой язык с явным намерением создать "виртуальный графический акселератор", так чтобы примитивы OpenGL максимально соответствовали примитивам современных графических карт и требовали минимум кода для трансляции из одной системы команд в другую. Фактически большинство современных графических процессоров (обычно называемых видеокартами, хотя к видео они имеют лишь касательное отношение) напрямую воспринимают OpenGL как язык входного уровня без какой-либо (или с минимумом) трансляции.
OpenGL оперирует графическими примитивами "начального уровня", такими как точки трехмерного пространства (вертексы, вершины), отрезки прямых, выпуклые полигоны и растровые изображения. Поддерживаются аффинные и проективные преобразования, вычисление освещения. К "продвинутым" функциям можно отнести мэппинг текстур (натягивание битовых карт на трехмерные поверхности) и антиалиасинг (сглаживание цветовых переходов - как локальное, в рамках отдельного объекта, так и глобальное, по всей сцене). Предполагается, что приложение более высокого уровня будет выполнять операции, которых недостает в OpenGL,- например, декомпозицию невыпуклых полигонов.
С точки зрения программиста, OpenGL - это система вызовов процедур с передачей им параметров, то есть этот язык представляет собой Call Level API. Ключевым моментом с точки зрения производительности, особенно в сетевом окружении, является наличие двух режимов: пошагового и пакетного. Пакетный группирует команды описания объектов и режимы в пакет, называемый списком отображения (display list). Подобная техника применяется, например, при описании страниц в PostScript или, в несколько другом контексте, при создании хранимых процедур в SQL. Смысл ясен - инкапсуляция функциональности (в данном случае - объектов для последующего многократного использования). При этом можно провести несколько оптимизаций: предварительно один раз проверить синтаксис, а также кэшировать готовый объект при передаче по сети на целевой машине.
Недостаток списка отображения проявляется при частом внесении в него изменений - при этом, понятно, ни один метод оптимизации не будет работать. Для работы с такими "изменчивыми" объектами и сценами в OpenGL предусмотрен режим прямого отображения, когда каждое предложение интерпретируется в порядке поступления, не дожидаясь закрывающего список тега или какой-то подобной команды на отрисовку. В практике оба метода нашли широкое применение.
Архитектура
Более глубоко погружаясь в технологию OpenGL, мы обнаружим стройную и гармоничную архитектуру, общий вид которой показан на рисунке.
Точно так же, как процессор имеет два типа конвейеров - для целочисленных вычислений и чисел с плавающей точкой, OpenGL имеет два конвейера для пиксельных данных и вертексных операций, то есть для операций с векторными данными. Каждый из потоков обрабатывается отдельно, до тех пор пока это возможно - то есть до стадии мэппинга, когда пиксельные растры как фактуры "натягиваются" на плоскости и более сложные поверхности.
Первый этап - аппроксимация кривых и поверхностей вычислением полиномов от входных значений. Второй проход оперирует с примитивами типа точек, отрезков и полигонов - они преобразуются по правилам аффинных преобразований, совмещаются и сцена отсекается в подготовке к растрированию.
Растрирование в качестве результата создает список объектов (точек, отрезков и треугольников) в двумерной плоскости. Над отдельными объектами может быть выполнена операция раскрашивания, градиентной заливки или применения мэппинга, то есть наложения фактуры.
Готовые фрагменты окончательно обрабатываются перед тем, как они реально будут внесены в frame buffer. В частности, фрагменты сортируются зависимо от значений "глубины" - и эти значения сравниваются с известными на предмет рекомпозиции. Применение блендинга приводит к тому, что прозрачные фрагменты принимают цвет, состоящий из их собственного и цветов "ниже лежащих" фрагментов. Дополнительно может быть реализовано маскирование и другие эффекты.
Пиксельный процессор в ходе растрирования встраивает двумерные битовые фрагменты прямо в кадр. Часть готового кадра также может быть прочитана для повторного использования как массив пикселей - так что данные, отображаемые в буфере, могут стать частью других сцен.
Приоритеты
При реализации авторы ставили себе пять ориентиров, важных с точки зрения получаемых результатов.
- Производительность. С самого начала в OpenGL была заложена "крайне желательная" возможность отрисовки динамических сцен. Для получения нужных результатов в систему введено множество параметров, или, как говорят, режимов рисования. Если некоторый режим или комбинация режимов на данном оборудовании не в состоянии обеспечить интерактивного взаимодействия и необходимой частоты обновления сцены, то пользователь или сама программа должны быть в состоянии отключать так много дополнительных функций, сколько нужно для получения "живой" картинки.
- Ортогональность. По возможности все функции OpenGL являются ортогональными, то есть независимыми. Вы можете использовать их в произвольной комбинации, например использование мэппинга не ограничивает возможностей применения светотени.
- Полнота. Насколько это представляется возможным, OpenGL соответствует набору функций, предоставляемому современными аппаратными средствами графической акселерации. OpenGL старается избегать всего, что должно быть реализовано программно. С другой стороны, по крайней мере, гарантируется получение рабочей картинки, даже если производительность и не позволяет получить ее со всеми подробностями. То есть, если что-то работает на одной платформе, то этот же код будет работать и на другой - хотя, возможно, и с другим результатом.
- Интероперабельность. В сетевом окружении важно передавать данные между разными платформами. Поэтому OpenGL заранее ориентирован на работу в режиме клиент-сервер, даже если и клиент и сервер расположены на одном компьютере.
- Расширяемость. Поскольку OpenGL рассчитан на максимальное соответствие возможностям аппаратуры (а аппаратура, как известно, имеет тенденцию развиваться), то в OpenGL также встроены механизмы включения новых функций. С другой стороны, нестабильный интерфейс затрудняет жизнь разработчиков, поэтому новые возможности накапливаются достаточное время и применяются согласованно с выходом новой версии.
Функции
Ниже перечислены основные функции OpenGL, о некоторых из них уже упоминалось. По ним вы можете судить об уровне языка и используемых примитивах.
- Альфа-канал. Позволяет делать предметы прозрачными, уровень прозрачности от 0 до 100%.
- Антиалиасинг. Сглаживание цветовых переходов, более реалистическое изображение.
- Буфер аккумулятора. Дополнительный буфер для 2,5-мерных эффектов, спецэффектов и глобального сглаживания по всей сцене.
- Градиентная заливка (gouraud shading). Линейно-градиентная заливка полигонов и отрезков.
- Графические примитивы. В пространстве: точка, отрезок, полигон, битовое изображение или изображение в другом формате.
- Двойная буферизация. Применяется для сглаживания эффектов анимации, когда новое изображение строится на заднем плане и потом отображается целиком. При этом пользователь не видит самого процесса создания изображения в несогласованном состоянии, например различных "изнанок объектов", "дыр в пространстве", "граней мира" и подобных нежелательных деталей.
- Заливка и освещенность фактур. К фактурам применяются эффекты освещенности и затенения в зависимости от характеристик "материала". В версии 1.2 реализованы блики поверх текстур.
- Маскирование. Можно маскировать некоторые цвета по трафарету.
- Массивы вершин. Новая возможность версии 1.5 - для повышения производительности задавать вершины массивами, а не отдельно.
- Обратная связь. Данные после растрирования могут быть возвращены в приложение вместо передачи из/в frame buffer или параллельно с ней.
- Пересечения. Автоматическое определение того, пересекает ли тот или иной объект заданный пространственный регион.
- Пиксельные операции. Масштабирование и другие аффинные преобразования битовых образов.
- Поддиапазоны. Возможность работать с частью матрицы вершин; применяется как метод оптимизации.
- Полиноминальные операции. Поддержка неравномерных рациональных би-сплайнов для описания кривых поверхностей.
- Полноцветное отображение. Представление в режиме RGBA, то есть тремя цветами и значением альфа-канала. Начиная с версии 1.2, поддерживаются также схемы BGRA и схемы с упакованными цветами для быстрой обработки популярных типов графических файлов.
- Пространственные преобразования. Масштабирование, вращение и перемещение объектов в пространстве.
- Режим индексированных цветов. Представление цветов не RGB-триплетами, а индексами в таблице цветов. Применяется для сжатия размена изображений "по глубине цвета" и эффектов быстрой замены одного цвета другим.
- Режим прямой отрисовки. Рисование по мере поступления команд, без использования списков отображения.
- Смешивание цветов. Позволяет устанавливать несколько режимов наложения одного изображения на другое. С помощью этой операции, в частности, реализуется альфа-канал и другие эффекты.
- Список отображения (display list). Пакет описания объектов сцены для предварительного разбора и оптимизации кэширования.
- Текстуры. Наложение двухмерных изображений на объемные поверхности для придания сцене реализма. Начиная с версии 1.2, поддерживаются трехмерные текстуры. Кроме того, начиная с этой же версии, поддерживается уровень текстур, позволяющий загружать их только до определенной степени детализации - в целях экономии памяти текстур.
- Z-буффер. Понятие об удалении объектов и их частей от наблюдателя, часть алгоритма удаления скрытых поверхностей.
Ресурсы
Для быстрого и всестороннего "проникновения" идеями OpenGL вы должны (как минимум) детально изучить две книги, являющиеся "классикой жанра". Первая из них носит название Синей книги (на ее обложке действительно преобладает синий цвет) и представляет собой справочник по функциям и переменным.
Вторая книга (а возможно, по порядку прочтения и первая) - это OpenGL Programming Guide, так называемая Красная книга.
Красная книга снабжена примерами, на которые я бы советовал обратить самое пристальное внимание. К слову: указанные ссылки ведут к устаревшим версиям документации, не описывающим новые возможности. Новые версии доступны только в печатном виде. Можете попробовать найти их через сети P2P - однако результаты такого поиска предсказывать не берусь.