Использование библиотеки OpenGL. Моделирование

advertisement
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ
МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ ИНДУСТРИАЛЬНЫЙ УНИВЕРСИТЕТ
Д.Ю. Куприянов
Использование библиотеки OpenGL.
Моделирование трёхмерной сцены
Учебное пособие по дисциплине
«Компьютерная графика»
Москва 2012
УДК 004.7(075.8)
ББК 32.973.202
К92
Рецензенты:
С.К. Карцов, д.т.н., профессор (МГИУ)
Н.В. Лукьянова, к.т.н., доцент (МГИУ)
К92
Куприянов Д.Ю.
Иcпользование библиотеки OpenGL. Моделирова
ние трёхмерной сцены: учебное пособие по дис
циплине «Компьютерная графика». — М.: МГИУ,
2012. — 62 c.
ISBN 978-5-2760-2162-1
Учебное пособие содержит основы создания трехмерных
графических приложений с помощью библиотеки OpenGL. В
пособии рассматривается синтаксис команд библиотеки, типо
вая структура приложений, способы обработки событий, прин
ципы построения трехмерных сцен и формирования проекцион
ного изображения, конструирование и позиционирование трех
мерных геометрических объектов. Пособие предназначено для
студентов, обучающихся по специальности 010503 «Математи
ческое обеспечение и администрирование информационных си
стем» и направлению 230100 «Информатика и вычислительная
техника».
УДК 004.7(075.8)
ББК 32.973.202
ISBN 978-5-2760-2162-1
© МГИУ, 2012
© Куприянов Д.Ю., 2012
Содержание
Введение . . . . . . . . . . . . . . . . . . . . . . . . . .
1. Основы создания графических приложений с
помощью OpenGL . . . . . . . . . . . . . . . . . .
1.1. Синтаксис команд OpenGL . . . . . . . . . . .
1.2. Буфер кадра . . . . . . . . . . . . . . . . . . . .
1.3. Конвейер визуализации . . . . . . . . . . . . .
1.4. Создание анимации . . . . . . . . . . . . . . . .
1.5. Структура OpenGL приложения . . . . . . . .
2. Рисование геометрических объектов . . . . . . .
2.1. Геометрические примитивы и их свойства . . .
2.2. Определение геометрических примитивов . . .
2.3. Определение векторов нормали . . . . . . . . .
3. Создание трёхмерной сцены и её видового пред
ставления . . . . . . . . . . . . . . . . . . . . . . .
3.1. Системы координат . . . . . . . . . . . . . . .
3.2. Матричные преобразования координат . . . .
3.3. Модельно-видовые преобразования . . . . . .
3.4. Проекционные преобразования . . . . . . . . .
3.5. Трансформация порта просмотра . . . . . . . .
4. Формирование изображения в буфере кадра . .
4.1. Растеризация примитивов . . . . . . . . . . . .
4.2. Тестирование фрагментов . . . . . . . . . . . .
4.3. Маскирование данных в буфере кадра . . . . .
Список литературы и интернет-ресурсов . . . . . .
4
6
6
7
11
13
16
21
21
30
33
36
36
41
42
46
51
54
54
55
60
62
Введение
OpenGL — это программный интерфейс к графической
аппаратуре. Этот интерфейс состоит приблизительно из 250
отдельных команд (около 200 команд в самой OpenGL и еще
50 в библиотеке утилит), предназначенных для определения
объектов и выполнения операций, с помощью которых мож
но разработать интерактивное приложение, работающее с
трёхмерной графикой. Библиотека OpenGL позволяет стро
ить сложные трёхмерные модели при помощи небольшого
набора геометрических примитивов — точек, линий и мно
гоугольников (полигонов). Она спроектирована в виде неза
висимого интерфейса, который может быть реализован для
различного аппаратного обеспечения. По этой причине сама
OpenGL не включает ни функций для создания окон или за
хвата пользовательского ввода, ни высокоуровневых функ
ций для описания моделей трёхмерных объектов. Для этих
операций необходимо использовать соответствующие допол
нительные библиотеки.
Основные графические операции, которые выполняет
OpenGL для вывода изображения на экран, можно пере
числить следующим образом.
• Конструирование фигур из геометрических примитивов
и создание математического описания объектов.
• Позиционирование объектов в трёхмерном простран
стве и выбор точки наблюдения для осмотра получен
ной композиции.
• Вычисление цветовых характеристик объектов, кото
рые могут быть заданы непосредственно в приложении,
получены из расчёта условий освещённости или опре
деляться с помощью текстур, наложенных на объекты.
• Преобразование математического описания объектов и
ассоциированной с ними цветовой информации в пик
сели на экране — процесс растеризации.
5
Библиотека утилит GLU (OpenGL Utility Library) явля
ется надстройкой над OpenGL и использует её функции для
рисования более сложных объектов. Она включает в себя
большое количество функций, расширяющих базовый функ
ционал OpenGL и предоставляющих пользователю более
простой и мощный интерфейс трёхмерной графики. Среди
функций библиотеки GLU, входящих в состав средств для
моделирования трёхмерных сцен, можно выделить такие как
рисование дополнительных графических примитивов (сфе
ры, цилиндра, конуса, диска и др.), переключение между
экранными и мировыми координатами, создание текстур, ри
сование квадратичных поверхностей, составление мозаики
прямоугольных примитивов, интерпретация кодов ошибок
OpenGL, расширенный набор функций трансформации для
установки точек обзора и более простого управления каме
рой и др. Библиотека утилит GLU обычно поставляется вме
сте с библиотекой OpenGL.
Так как OpenGL не включает в себя никаких специаль
ных команд для работы с окнами и диалога с пользовате
лем, то для этих целей следует использовать специальные
переносимые библиотеки для взаимодействия приложения
с операционной системой и отображения информации с по
мощью оконной подсистемы. Наиболее популярной из них
является библиотека утилит приложений GLUT (OpenGL
Utility Toolkit). Она отвечает за системный уровень опе
раций ввода-вывода при работе с операционной системой.
Среди функций библиотеки GLUT можно выделить такие
как создание окна, управление окном, мониторинг за вводом
с клавиатуры и событиями мыши. Библиотека включает в се
бя возможность создания несложных всплывающих меню и
даже содержит функции для рисования ряда геометрических
примитивов, таких как куб, сфера, чайник и др. Существуют
и другие вспомогательные библиотеки OpenGL, такие как
glx, wgl, pgl, agl, fsg и glaux, назначение и полное описание
которых можно найти в соответствующих разделах докумен
тации.
1. Основы создания графических
приложений с помощью OpenGL
1.1. Синтаксис команд OpenGL
Для работы с командами OpenGL, так же как и для опи
сания библиотечных функций, используются собственные
типы данных, которые являются синонимами соответствую
щих типов языка С. В таблице 1.1 приведен перечень типов
данных OpenGL с указанием эквивалентных типов языка
C и характеризующего этот тип суффикса, используемого в
названии команд OpenGL.
Таблица 1.1. Типы данных в OpenGL
Тип данных в OpenGL Суффикс
Тип данных в C
GLbyte
b
signed char
GLshort
s
short
GLint, GLsizei
i
int или long
GLfloat, GLclampf
f
float
GLdouble, GLclampd
d
double
GLubyte, GLboolean
ub
unsigned char
GLushort
us
unsigned short
GLuint, GLenum
ui
unsigned int или long
Полное имя команды в OpenGL состоит из нескольких
частей и имеет вид:
{префикс}{НазваниеКоманды}{составной суффикс}(аргументы).
Префикс команды показывает, к какому заголовочному
файлу она относится. Название команды характеризует дей
ствие, которое она совершает. Составной суффикс несёт в
себе информацию о числе и типе передаваемых параметров.
Например, команда glVertex2i() описана как базовая в
библиотеке OpenGL, и использует в качестве параметров
два целых числа, а команда glColor3fv() использует в ка
Буфер кадра
7
честве параметра указатель на массив из трёх веществен
ных чисел. В таблице 1.2 приведены возможные значения
префиксов и суффиксов комманд OpenGL.
Таблица 1.2. Префиксы и суффиксы комманд OpenGL
Тип
префикс
Значение
gl, glu или glut
составной
суффикс
[1 2 3 4]
[b s i f d
ub us ui]
[v]
Описание
имя заголовочного файла
(библиотеки), в котором
описана команда
число аргументов команды
суффикс типа аргумента
символ, показывающий, что
в качестве параметров функ
ции используется указатель
на массив значений
Определения команд GL и библиотеки GLU находятся
в заголовочных файлах gl.h и glu.h соответственно. В отли
чие от стандартных библиотек, пакет GLUT нужно инстал
лировать и подключать отдельно. Все команды (процедуры
и функции) библиотеки GL начинаются с префикса gl, все
константы — с префикса GL_. Соответствующие команды и
константы библиотек GLU и GLUT аналогично имеют пре
фиксы glu (GLU_) и glut (GLUT_).
1.2. Буфер кадра
Буфер кадра в OpenGL играет роль хранилища изобра
жения. Он предназначен для создания в нем проекции трёх
мерной сцены, в которой были заранее размещены объекты.
Размеры буфера кадра соответствуют размерам проекции,
которая, в свою очередь, масштабируется при выводе в ок
но просмотра. Таким образом, каждая точка растеризован
ного изображения имеет отражение в буфере кадра, который
представляет собой набор прямоугольных массивов, содер
жащих цвет и другую вспомогательныю информацию.
8
Основы создания графических приложений с помощью OpenGL
В общем случае, буфер кадра представляет собой набор
основных и вспомогательных буферов. Основными являют
ся цветовые буферы, в которых непосредственно формиру
ется изображение, созданное из поступивших на конвеер
пиксельных данных и геометрических примитивов. Так, ес
ли включены все возможности буфера кадра, он включает в
себя:
• один или несколько цветовых буферов (в случае если
необходимо строить стерео- или анимационное изоб
ражение), которые содержат цветовое представление
растеризованных объектов;
• буфер глубины (z-буфер), который может хранить пре
образованные значения z-координат растеризованных
объектов и в автоматическом режиме использоваться
для удаления невидимых граней;
• буфер-накопитель (буфер-аккумулятор), который яв
ляется дополнительным цветовым буфером и может ис
пользоваться для создания эффектов накопления изоб
ражений;
• буфер трафарета (буфер маски), который позволяет
хранить вспомогательные числовые значения для каж
дого пиксела растеризованного объекта (фрагмента) и
предоставляет механизмы для организации тестов, ко
торые ограничивают вывод этих фрагментов в буфер
кадра.
Цветовой буфер содержит цветовое значение каждого
пиксела растра. В зависимости от выбранной цветовой мо
дели, он может содержать либо (r,g,b,a) —значение, либо ин
декс цвета в таблице, содержащей палитру цветов, опреде
лённых в приложении. Выбор режима должен производится
при инициализации экранного режима библиотеки GLUT в
функции glutInitDisplayMode().
В первом случае, функции необходимо передать пара
метр-константу GLUT_RGB или её синоним GLUT_RGBA, а во
втором — константу GLUT_INDEX. Реализация цветового бу
фера предполагает наличие нескольких битовых плоскостей
Буфер кадра
9
для каждой из компонент цветовой модели RGBA (или ин
декса палитры).
Для создания статического изображения бывает доста
точно одного цветового буфера. Этот режим использует
ся по умолчанию, но его можно указать явным образом,
выполнив инициализацию экранного режима с параметром
GLUT_SINGLE. В этом режиме единственный буфер использу
ется и для хранения мгновенного снимка проекции, и для его
последующего восстановления в случае, если изображение
потребуется отрисовать ещё раз. В случае, когда требуется
использовать два цветовых буфера — один для построения
предыдущего кадра и второй для следующего — необходимо
использовать константу GLUT_DOUBLE. Например, команда
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE)
указывает, что в приложении будут использоваться два цве
товых буфера с цветовым режимом RGBA.
Использование буферов маски и накопителя также тре
бует подключения при инициализации экранного режима.
Для этого необходимо использовать соответствующие кон
станты GLUT_STENCIL и GLUT_ACCUM.
Перед использованием каждого буфера его необходимо
очистить. Очистка буферов производится в два приёма. Сна
чала необходимо указать цвет (значение), которым будет
очищаться буфер, а затем вызвать функцию выполняющую
саму процедуру очистки. Далее приводится список функций,
которые позволяют задать значение для очистки соответ
ствующего буфера:
• glCLearColor(r,g,b,a) — буфера цвета в режиме
GLUT_RGBA;
• glClearIndex(index) — буфера цвета в режиме
GLUT_INDEX;
• glClearDepth(z) — буфера глубины;
• glClearAccum(r,g,b,a) — буфера-накопителя;
• glClearStencil(s) — буфера трафарета.
10
Основы создания графических приложений с помощью OpenGL
Очистку буферов рекомендуется производить параллель
но, одновременно передавая функции glClear() набор соот
ветствующих констант. Например, команда
glClear(GL_COLOR_BUFER_BIT | GL_DEPTH_BUFFER_BIT |
GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
очищает все составные буферы буфера кадра.
После очистки в буфере кадра можно создавать новое
изображение. При этом, формирование изображения в бу
фере кадра имеет особенности, связанные с сетевой архи
тектурой приложений OpenGL. Приложение, созданное с
помощью библиотеки OpenGL, может выполняться на од
ном компьютере (клиенте), а расчёты, связанные с форми
рованием изображения, производить на другом компьютере
(сервере). В этом случае, если каждая команда приложения
будет отправляться по сети отдельно от других, то сетевая
пересылка будет очень неэффективной. Поэтому, команды
клиента перед отправкой объединяются в сетевые пакеты,
по несколько команд в один пакет. При этом, незаполнен
ный пакет всегда ожидает поступления новых команд. Мо
жет случиться так, что изображение так и не будет сфор
мировано потому, что сетевой пакет не будет заполнен до
конца и отправлен на сервер.
В OpenGL существует две команды, позволяющие управ
лять отправкой сетевых пакетов. Первая команда glFlush()
позволяет отправить сетевой пакет в случае, если он не по
лон. Вызов команды glFlush() не является обязательным
тогда, когда клиент и сервер это один и тот же компьютер.
Однако, чтобы приложение корректно выполнялось всегда,
его всё-таки следует включать в программный код. Вторая
команда glFinish() выполняет те же самые действия по от
правке пакета, но при этом ожидает от аппаратуры или сети
подтверждения о том, что рисование в буфере кадра закон
чено.
Конвейер визуализации
11
1.3. Конвейер визуализации
Большинство реализаций OpenGL имеют сходный поря
док операций или этапов обработки данных, которые фор
мируют изображение в буфере кадра. Этот порядок называ
ется конвейером визуализации OpenGL (OpenGL rendering
pipeline). Основные этапы конвейера визуализации OpenGL
показаны на диаграмме (рис. 1.1), которая демонстрирует
порядок обработки пиксельных и геометрических данных.
Рис. 1.1. Конвейер визуализации OpenGL
12
Основы создания графических приложений с помощью OpenGL
Геометрические данные (вершины, линии и полигоны) про
ходят путь, включающий повершинные операции и сборку
примитивов, в то время как пиксельные данные (растровые
изображения, битовые карты и текстуры) до этапа растери
зации обрабатываются иначе. Этапы растеризации и опера
ций над фрагментами являются общими и завершающими
этапами конвеера OpenGL, которые выполняются с пик
сельными и геометрическими данными перед тем как те бу
дут помещены в буфер кадра. Кратко рассмотрим ключевые
этапы конвейера визуализации OpenGL.
• Все данные, геометрические или пиксельные, могут быть
сохранены в списках (display lists) для текущего или
последующего использования.
• Простейшие геометрические примитивы описываются
своими вершинами, а параметрические кривые и по
верхности, в свою очередь, могут быть определены с
помощью контрольных точек и полиномиальных функ
ций, называемых базисными функциями. Вычислите
ли OpenGL производят расчёты, связанные с опреде
лением положения составляющих поверхность вершин
по её контрольным точкам.
• Получение проекции геометрических объектов проис
ходит на этапе повершинных операций (per-vertex
operations) и сборки примитивов. Здесь к каждой
вершине заданного объекта применяются модельно-видовые и проекционные преобразования. После чего пре
образованные вершины соединяются между собой, об
разуя геометрический объект в проекции.
• Растеризация — процесс преобразования геометри
ческих и пиксельных данных во фрагменты. Каждый
фрагмент соответствует пикселю в буфере кадра и име
ет ассоциированные с ним значения цвета и глубины.
Фрагменты расчитываются на основе вершин, опреде
ляющих тот или иной объект, с учетом освещения и
свойств этого объекта, которые включают себя разме
Создание анимации
13
ры точек, толщину и шаблоны линий, модель закраски
и режим сглаживания.
• Фрагменты сохраняются в буфере кадра не сразу. Пе
ред тем как фрагмент помещается в буфер кадра он
проходит серию тестов, которые могут удалить его из
изображения. В дополнение к тестам над фрагментом
можно производить перечень операций, которые могут
его изменить. Механизмы тестирования и операций,
как правило, выключены по умолчанию, так как они
требуют выполнения более сложных расчётов, понижа
ющих производительность приложения.
• Придание определённых реалистичных свойств объек
там в OpenGL может быть выполнено путем наложе
ния на них текстурных изображений. После наложения
текстуры полученное представление геометрического
объекта растеризуется и помещается в буфер кадра.
1.4. Создание анимации
Создание анимационных изображений является неотъ
емлемой частью компьютерной графики. Плавность анима
ции как правило обеспечивается быстрой сменой кадров на
устройстве вывода, такой, чтобы глаз человека не успел
это заметить. Исходя из психофизиологических особенно
стей человеческого визуального восприятия, для создания
эффекта плавного движения скорость смены кадров должна
быть не менее 18 кадров в секунду. Так, например, в кино
театре видеофильмы воспроизводятся путем проецирования
серии изображений на экран со скоростью 24 кадра в секун
ду. Современные мониторы в среднем могут перерисовывать
изображение более 60 раз в секунду (хотя скорость может
достигать и 120 кадров в секунду). Очевидно, что чем боль
ше скорость смены кадров тем более плавным выглядит ани
мация. Однако, очень большая частота обновления может
находиться уже за гранью человеческого восприятия. Это
14
Основы создания графических приложений с помощью OpenGL
зависит, в основном, от индивидуальных особенностей кон
кретного человека. При этом считается, что при частотах
больших 120 кадров в секунду среднестатистический чело
век уже не способен ощутить разницу.
При построении анимационных изображений на компью
тере существует несколько проблем, которые приходится ре
шать разработчику приложений. Первая проблема заключа
ется в том, что непрерывное рисование в одиночном буфе
ре при построении сложных сцен может привести к нерав
номерному отображению отдельных объектов на экране по
времени. Это происходит потому, что при построении кадра
на компьютере требует определённого количества времени.
При этом способ визуализации в одиночном буфере име
ет свои недостатки, связанные с особенностью управления.
Рисование анимационного изображения в одиночном буфе
ре обычно происходит следующим образом. Сначала произ
водится очистка цветового буфера и в нем рисуется изоб
ражение, соответствующее первому кадру анимации. Затем
содержимое буфера вновь очищается, после чего рисуется
изображение второго кадра, и так далее. Если сложить ко
личество времени, которое требуется системе на отрисовку
кадра и очистку буфера, то может оказаться, что программа
выдаёт все более и более неприятные результаты по мере
того, как суммарное время приближается к 1/24 части се
кунды.
Предположим, что рисование изображения занимает
практически всю 1/24 часть секунды. Тогда объекты, ко
торые рисуются первыми, будут отображаться на экране
практически постоянно. При этом, те объекты, которые ри
суются в конце, будут похожи на приведение. Это связано
с тем, что практически сразу после их отображения про
грамма начнет стирать все изображение с экрана и, таким
образом, большую часть времени на месте этих объектов
будет отображаться пустой фон. Всё дело в том, что при
компьютерной анимации программное обеспечение воспро
изводит не готовые кадры, а рисует каждый из них, тратя на
Создание анимации
15
это время, и наблюдатель видит это. Эту проблему можно
решить с использованием двойной буферизации.
Режим двойной буферизации в OpenGL позволяет ис
пользовать в приложении два полноценных цветовых буфе
ра. Первый цветовой буфер, как правило, называют перед
ним (от англ. front). Построенное в нём изображение отоб
ражается в окне просмотра до тех пор, пока создаётся изоб
ражение во втором цветовом буфере, который называется
задним (от англ. back). Когда рисование кадра в заднем
буфере окончено, вывод в окно просмотра переключается
между этими двумя буферами, а именно тот, который только
что отображался на экране, теперь используется для постро
ения нового изображения, а содержимое второго начинает
выводиться в окно просмотра. При такой схеме создания
анимации наблюдатель никогда не видит частично нарисо
ванное изображение, так как в режиме двойной буфериза
ции каждый кадр показывается только после того, как он
полностью готов. И до тех пор, пока построение изображе
ний в кадре достаточно быстрое, наблюдатель не заметит
разницы между подобной техникой и той, где все кадры уже
готовы и просто показываются друг за другом. Переключить
буферы в OpenGL можно, например, с помощью функции
glutSwapBuffers().
Вторая проблема заключается в том, чтобы синхронизи
ровать время подготовки отдельных кадров с частотой об
новления экрана и при этом время отображения кадров в
среднем было одинаковым. Большинство реализаций функ
ции переключения буферов ожидают пока не завершится
процесс обновления экрана. Это гарантирует, что предыду
щий кадр будет отображен на экране полностью, а следую
щий — отрисован с самого начала. Необходимо отметить,
что до тех пор пока время создания отдельных кадров мень
ше времени обновления экрана анимация будет оставаться
гладкой. При этом время отображения кадров на экране бу
дет одинаковым и равно времени обновления экрана. Одна
ко, в ситуации, когда следующий кадр не успет создаться за
16
Основы создания графических приложений с помощью OpenGL
время одного обновления экрана, предыдущий кадр будет
нарисован на экране несколько раз. Такого рода задержка
приведёт к неравномерной анимации.
Для того, чтобы в ситуации, когда кадры подготавлива
ются программным обеспечением разное количество време
ни, после их показа используют небольшую паузу, которая
позволит сделать время их отображения на экране одинако
вым. При этом вводят такое понятие, как скорость смены
кадров (frames per second или сокр. fps). Скорость смены
кадров показывает, какое количество кадров в секунду от
рисовывается на экране. Несложно заметить, что скорость
смены кадров не может принимать произвольные значения,
так как она непосредственно связана с частотой обновления
экрана. Например, если система обновляет экран с частотой
60Hz (60 кадров в секунду), то анимация в такой системе мо
жет выполняться со скоростями 60 fps, 30 fps, 20 fps, 15 fps,
12 fps и так далее. При скорости смены кадров равной 60 fps
каждый кадр будет нарисован на экране 1 раз, при скорости
30 fps — 2 раза, а при скорости 20 fps — 3 раза.
1.5. Структура OpenGL приложения
Типичная программа, использующая OpenGL, начинает
ся с определения свойств и создания окна, в которое будет
выводится проекция трёхмерной сцены. Затем, с помощью
базовых функций, создаётся сама сцена, проекция которой
в итоге ассоциируется с окном вывода, после чего управ
ление программой передаётся функциям обработки событий
OpenGL.
Ниже приведен текст небольшой программы, которая ри
сует в центре окна красный квадрат. Несмотря на малый раз
мер, это полностью завершенная программа, которая долж
на компилироваться и работать в любой системе, поддержи
вающей OpenGL и GLUT.
Структура OpenGL приложения
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdlib.h>
/ * подключаем библиотеку GLUT * /
#include <gl/glut.h>
/ * начальная ширина и высота окна * /
GLint Width = 512 , Height = 5 1 2 ;
/ * размер квадрата * /
const i n t QuadSize = 2 0 0 ;
/ * эта функция управляет всем выводом на экран * /
void D i s p l a y ( void )
{
i n t l e f t , r i g h t , top , bottom ;
l e f t = ( Width – QuadSize ) / 2 ;
r i g h t = l e f t + QuadSize ;
bottom = ( Height – QuadSize ) / 2 ;
t o p = bottom + QuadSize ;
g l C l e a r C o l o r (0 , 0 , 0 , 1) ;
g l C l e a r (GL_COLOR_BUFFER_BIT) ;
g l C o l o r 3 u b (2 5 5 , 0 , 0) ;
g l B e g i n (GL_QUADS) ;
g l V e r t e x 2 f ( l e f t , bottom ) ;
g l V e r t e x 2 f ( l e f t , top ) ;
g l V e r t e x 2 f ( right , top ) ;
g l V e r t e x 2 f ( r i g h t , bottom ) ;
glEnd () ;
g l F i n i s h () ;
}
/ * Функция, вызываемая при изменении размеров окна * /
void Reshape ( GLint w, GLint h)
{
Width = w ;
Height = h ;
/ * устанавливаем размеры области отображения * /
g l V i e w p o r t (0 , 0 , w, h ) ;
/ * ортографическая проекция * /
glMatrixMode (GL_PROJECTION) ;
g l L o a d I d e n t i t y () ;
g l O r t h o (0 , w, 0 , h , – 1 . 0 , 1 . 0 ) ;
glMatrixMode (GL_MODELVIEW) ;
g l L o a d I d e n t i t y () ;
}
/ * Функция, которая обрабатывает сообщения от клавиатуры * /
void Keyboard ( unsigned char key , i n t x , i n t y )
{
#define ESCAPE ’ \ 033 ’
17
18
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
Основы создания графических приложений с помощью OpenGL
i f ( key == ESCAPE )
e x i t (0) ;
}
/ * Функция, которая обрабатывает сообщения от мыши * /
void Mouse ( i n t b u t t o n , i n t s t a t e , i n t x , i n t y )
{
switch ( b u t t o n )
{
case GLUT_LEFT_BUTTON:
i f ( s t a t e == GLUT_DOWN) g l u t I d l e F u n c ( s p i n D i s p l a y ) ;
break ;
case GLUT_RIGHT_BUTTON:
i f ( s t a t e == GLUT_DOWN) g l u t I d l e F u n c (NULL) ;
break ;
}
}
/ * Главный цикл приложения * /
main ( i n t argc , char * a r g v [ ] )
{
g l u t I n i t (& argc , a r g v ) ;
g l u t I n i t D i s p l a y M o d e (GLUT_RGB) ;
g l u t I n i t W i n d o w S i z e ( Width , Height ) ;
glutCreateWindow ( " Red s q u a r e example " ) ;
glutDisplayFunc ( Display ) ;
glutReshapeFunc ( Reshape ) ;
g l u t K e y b o a r d F u n c ( Keyboard ) ;
glutMouseFunc (Mouse ) ;
glutMainLoop () ;
}
Рассмотрим более подробно функцию main данного при
ложения. Она состоит из трёх частей:
- инициализации окна, в котором будет рисовать
OpenGL;
- настройки функций c обратным вызовом;
- главного цикла обработки событий.
Инициализация окна заключается в настройке базовых
свойств буфера кадра, начального положения и размеров, а
также указания заголовка окна. Команда glutInit() (стро
ка 79) производит начальную инициализацию самой биб
лиотеки GLUT, а glutInitDisplayMode() инициализирует
буфер кадра и настраивает полноцветный (непалитровый)
режим RGB. Команда glutInitWindowSize() используется
для задания начальных размеров окна. И, наконец, команда
glutCreateWindow() задаёт заголовок окна и визуализирует
само окно на экране.
Структура OpenGL приложения
19
В строках 83–86 регистрируются функции Display,
Reshape, Keyboard и Mouse, которые описываются выше
(строки 13–74) и будут вызываться, соответственно, при пе
рерисовке окна, изменении размеров окна, нажатии клавиш
на клавиатуре и мыши. Контроль всех событий и вызов за
регистрированных функций происходит внутри бесконечного
цикла функции glutMainLoop() (строка 87).
Все вызовы команд OpenGL, которые занимаются со
зданием изображения и выводом его в окно приложения,
происходят в обработчиках событий. Рассмотрим функцию
Display (строки 13–34), в которой находится код, отвечаю
щий за рисование в буфер кадра.
Вызов команды glClearColor(0, 0, 0, 1) задаёт чер
ный цвет в RGB и альфа-компоненту, равную 1, которы
ми должен быть инициализирован (очищен) буфер кадра.
Смысл и дополнительные возможности использования
альфа-компоненты будут рассмотрены чуть позже. Сама
инициализация буфера кадра выполняется в момент вызо
ва команды glClear() с параметром, который принуждает
очистить именно цветовой буфер (строка 23).
Команда glColor3ub(255, 0, 0) устанавливает красный
цвет пера, которым будут рисоваться все определённые по
сле вызова этой команды геометрические примитивы. Блок
команд (строки 26–31) определяет квадрат, задаваемый че
тырьмя вершинами с соответствующими координатами.
Функция Reshape (строка 37) вызывается каждый раз
при изменении размера окна или его позиции на экране.
Здесь параметр w соответствует новой ширине, а h — новой
высоте окна. Вызов glViewport() в теле Reshape() (строка
43) необходим для отсечения графического вывода по но
вым размерам окна и для настройки проекционной матрицы
в соответствии с новыми размерами порта просмотра.
Функция Keyboard (строка 54) вызывается, когда нажи
мается клавиша, имеющая ASCII-код. Этот код передаёт
ся функции обратного вызова в параметре key. В пара
метрах x и y передаётся позиция курсора мыши (в окне
20
Основы создания графических приложений с помощью OpenGL
приложения) в момент нажатия клавиши. Функция Mouse
(строка 63) вызывается при нажатии или отпускании кнопки
мыши. Здесь значение параметра button соответствует тому,
какая
кнопка
мыши
была
нажата/отпущена
(GLUT_LEFT_BUTTON — левая, GLUT_MIDDLE_BUTTON —
средняя или GLUT_RIGHT_BUTTON — правая). Параметр
state, в зависимости от состояния кнопки, может прини
мать значения GLUT_DOWN (нажата) или GLUT_UP (отпущена).
В параметрах x и y, как и в функции Keyboard, передаются
координаты курсора мыши в момент наступления события.
Результат выполнения программы представлен на рисун
ке 1.2.
Рис. 1.2. Скриншот программы «Red square»
2. Рисование геометрических
объектов
2.1. Геометрические примитивы и их свойства
OpenGL, в чистом виде, поддерживает рисование только
простых геометрических объектов (примитивов). Этими при
митивами являются точки, отрезки и многоугольники (поли
гоны). Поэтому, для того чтобы нарисовать сложный трёх
мерный объект, необходимо представить его в виде набо
ра доступных примитивов (см. рис. 2.1). При этом дополни
тельные библиотеки, например GLU, содержат ряд команд,
позволяющих отрисовать стандартные трёхмерные объекты:
сферу, куб, конус, пирамиду, тор и многие другие.
Рис. 2.1. Полигональная модель объекта
Положение каждого геометрического примитива описы
вается координатами его вершин в трёхмерном простран
стве. Необходимо отметить, что для этого используются не
обычные декартовы координаты — (x, y, z), а однородные ко
ординаты — (x, y, z, w), которые позволяют описывать один
22
Рисование геометрических объектов
и тот же объект в терминах масштабируемых проекционных
преобразований. При переходе от однородных координат к
декартовым каждая из компонент вектора (x, y, z) делится
на значение w. Обычно при определении координат верши
ны используют значение w = 1, но если необходимо, можно
использовать и другие значения, например значение w = 0
используют для того, чтобы задать точку на бесконечности
или вектор-направление.
При задании однородных координат вершины принима
ются следующие соглашения.
• Если заданы три из четырёх однородных координат
(x, y, z), то значение w принимается равным 1;
• Если заданы две из четырёх однородных координат
(x, y), то значения недостающих величин принимают
ся такими — (z = 0, w = 1).
Рассмотрим подробнее каждый из примитивов, описывая
его свойства.
Точка — самый простой из примитивов. Она определя
ется своим положением (положением вершины). Положение
вершины задаётся с помощью функции glVertex*(). У этой
функции несколько версий с разным количеством аргумен
тов. В наиболее полном варианте, в котором используется
четыре аргумента, функция требует определения четырёх од
нородных координат вершины. Например, команда
glVertex4f(0.0, 0.0, 0.0, 1.0)
задаёт вершину в начале декартовой системы координат.
К геометрическим свойствам точки можно отнести, пожа
луй, только её размер. Изменить размер точки можно с по
мощью функции glPointSize(size), где параметр size дол
жен принимать значения больше 0.0 (по умолчанию
size = 1.0). При визуализации точки на её растровое пред
ставление влияет модель сглаживания (антиалиасинг). При
выключенном механизме сглаживания и значении парамет
ра size, равном N, растровое представление проекции точки
будет будет набором пикселей NxN.
Геометрические примитивы и их свойства
23
В случае когда механизм сглаживания выключен, дроб
ные величины размера округляются до ближайшего целого
и точка рисуется в виде квадрата с соответствующей дли
ной стороны. Иначе, когда антиалиасинг включен, округле
ние не выполняется, а сама точка рисуется в виде циркуляр
ной группы пикселей (квадрат с закругленными углами). В
этом случае пиксели на границе обычно имеют цвет с по
ниженной интенсивностью для придания границе гладкого
вида.
Как правило, реализации OpenGL поддерживают точки
очень большого размера. Чтобы узнать минимальный и мак
симальный размеры для несглаженных точек, необходимо
вызвать функцию
glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE).
Аналогично можно запросить минимальный и максималь
ный размеры сглаженных точек, используя в той же команде
аргумент GL_SMOOTH_POINT_SIZE_RANGE. Допустимые разме
ры сглаженных точек дискретно меняются в диапазоне от
минимума до максимума. Вызвав эту команду с параметром
GL_SMOOTH_POINT_SIZE_GRANULARITY, можно узнать, с какой
точностью сглаженные точки отображаются на экране. Этот
параметр задаёт величину гранулирования — величину
минимального изменения размера точки внутри диапазона
от минимума до максимума, имеющую видимый эффект. На
пример, если вы устанавливаете размер точки равным 3.84
и величина гранулирования равна 0.1, то размер точки будет
округлен до 3.8.
Отрезок (линия) определяет ограниченный участок пря
мой (сегмент) и характеризуется своими вершинами: началь
ной и конечной. Отрезки в OpenGL представляются как со
вокупность вершин (концов отрезка) и линии, которая их
соединяет. На отрисовку отрезков влияют как свойства, ко
торые были заданы для точки (эти свойства используются
для рисования вершин), так и свойства, которые могут быть
отдельно заданы для линии, соединяющей вершины. Среди
24
Рисование геометрических объектов
них необходимо отметить толщину линии и её начертание
(стиль).
Толщину линии можно задать, используя функцию
glLineWidth(width). Ей в качестве аргумента передаётся
значение, задающее толщину линии в точках растра. Па
раметр width должен принимать значения больше 0.0 (по
умолчанию width = 1.0).
Так же как и при рисовании точки, на растровое пред
ставление отрезка влияет модель сглаживания (антиалиа
синг). Конкретная реализация OpenGL может ограничивать
толщину несглаженной линии. Получить диапазон поддер
живаемых значений толщины несглаженных линий можно с
помощью команды
glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE).
Аналогично можно запросить минимальное и максималь
ное значение толщины для сглаженных линий, а также вели
чину гранулирования. Для этого необходимо вызвать эту ко
манду с параметрами GL_SMOOTH_LINE_WIDTH_RANGE и
GL_SMOOTH_LINE_WIDTH_GRANULARITY соответственно.
Стиль рисования линий можно задавать с помощью шаб
лонов. Например, с помощью шаблонов можно определять
пунктирное или штриховое начертание линий. Расчёт шаб
лонов отрезков в приложении OpenGL по умолчанию вы
ключен. Поэтому, перед тем как задать шаблон рисования
линии, необходимо сначала включить механизм расчёта с
помощью команды glEnable(GL_LINE_STIPPLE).
Шаблон начертания линии определяется с помощью
функции
glLineStipple(factor, pattern).
Значение второго аргумента pattern — число, записанное
в шестнадцатеричном виде — интерпретируется как двоич
ная цепочка, т.е. серия из нулей и единиц, которая опреде
ляет растровое представление начертания линии. Единица
Геометрические примитивы и их свойства
25
означает, что соответствующая точка растра в отрезке бу
дет отрисовываться, а нуль, в свою очередь, — нет. Шаблон
применяется к отрезку, начиная от начальной точки и закан
чивая конечной. При этом начертание первой точки растра
задаёт именно младший (правый) бит двоичной цепочки, а
далее — от младшего бита к старшему (справа-налево).
Рис. 2.2. Применение шаблона при рисовании отрезка
Шаблон может быть растянут с учетом значения фак
тора повторения factor. При значении фактора, больше
го единицы, каждый бит шаблона повторяется factor раз,
т.е. применяется последовательно сразу к нескольким точ
кам растрового представления отрезка. Например, если в
шаблоне встречаются подряд три единицы, а затем два ну
ля ("00111"), то при значении factor, равном 3, к отрезку
будет применяться шаблон, состоящий из 9 единиц и 6 ну
лей ("000000111111111"). Необходимо отметить, что допу
стимые значения аргумента factor ограничены диапазоном
от 1 до 256.
Рисование отдельных отрезков отличается от рисования
ломаной линии, хотя при геометрическом задании этих при
26
Рисование геометрических объектов
митивов никакой разницы нет. Особенность заключается в
том, что при использовании шаблонов расчёт начертания ли
ний и сегментов ломаной выполняется по-разному. Длина
шаблона, как правило, не совпадает с длиной растрового
представления отрезка. Поэтому при наложении шаблона на
отрезок шаблон повторяется по длине отрезка столько раз,
сколько это необходимо. Когда заканчивается один отрезок
лишняя часть шаблона отбрасывается, и при отрисовке вто
рого отрезка шаблон начинает применяться сначала. Прин
цип отрисовки ломаной линии отличается от рисования от
дельных отрезков и заключается в том, что после отрисовки
первого сегмента ломаной шаблон продолжает применяться
ко второму сегменту, а не начинается с самого начала.
Рассмотрим пример использования шаблона. При рисо
вании линии с шаблоном, равным "0x3F07" (в двоичной си
стеме — "0011111100000111"), и фактором повторения, рав
ным единице, отрезок будет растеризован на экране следую
щим образом (по порядку): сначала 3 отрисованных пиксе
ля, затем 5 отсутствующих, 6 нарисованных и 2 отсутствую
щих. Если длина растеризованного отрезка больше 16 пик
селей, то начиная с 17-го пикселя шаблон будет применять
ся заново и так далее до конца отрезка. При рисовании с
фактором повторения равным двум шаблон растягивается и
отрезок растеризуется следующим образом: сначала 6 нари
сованных пикселей, затем 10 отсутствующих, 12 нарисован
ных и 4 отсутствующих.
Необходимо отметить, что при выключенном шаблониро
вании все отрезки рисуются так же, как и при включенном
механизме с шаблоном "0xFFFF" и фактором повторения,
равным единице.
Полигон (или многоугольник) — область, ограниченная
одной замкнутой ломаной. Геометрически полигон описыва
ется положением его вершин. При этом каждая новая вер
шина вместе с предыдущей задаёт сегмент ломаной (ребро
полигона). Последний сегмент границы задают первая и по
следняя вершины полигона.
Геометрические примитивы и их свойства
27
В общем случае полигоны могут быть достаточно слож
ными. Поэтому OpenGL накладывает определённые ограни
чения, выполнение которых гарантирует, что полигон будет
растеризован корректно. Во-первых, полигон должен быть
простым, т.е. его ребра не должны пересекаться. Во-вто
рых, полигон должен быть выпуклым, т.е. любой отрезок,
соединяющий две произвольные точки полигона (граничные
или внутренние), должен целиком лежать внутри этого поли
гона. Программисту необходимо самому следить за выпол
нением данных требований, потому что при попытке нарисо
вать невыпуклый полигон, результат может быть не таким
как ожидается.
Многие реальные поверхности состоят из непростых,
невыпуклых полигонов или полигонов с дырами. В библиоте
ке GLU имеется набор методов для разбиения сложного по
лигона на группу простых полигонов, удовлетворяющих тре
бованиям OpenGL. Такое разбиение называется тесселяци
ей. В процессе тесселяции каждый многоугольник модели
разбивается на заданное число связанных многоугольников,
которые выстраиваются в соответствии с общим направле
нием поверхности модели. Таким образом сначала можно
создать простую модель, а затем быстро и просто повысить
её детализацию.
У любого полигона есть две стороны (грани): лицевая
(или прямая) и задняя (или обратная). Каждая из этих гра
ней может обладать собственными свойствами, например
иметь разный цвет или свойства взаимодействия с источни
ками освещения. Такое представление вполне соответствует
действительности, так как многие реальные объекты снару
жи и изнутри выглядят по-разному. По принятому соглаше
нию, грань полигона считается лицевой, если её вершины
в порядке перечисления, начиная с первой, отображаются
на экране против часовой стрелки. Это соглашение можно
изменить вручную, указав OpenGL, какие грани считать ли
цевыми, а какие — обратными.
28
Рисование геометрических объектов
Команда glFrontFace(GLenum mode) позволяет задать
ориентацию лицевых граней. Параметр mode может прини
мать одно из значений: GL_CCW или GL_CW. Значение GL_CCW
(от англ. counterclockwise) используется по умолчанию и
соответствует тому, что лицевыми гранями будут считаться
грани с порядком обхода вершин в проекции против часовой
стрелки, а значение GL_CW (от англ. clockwise) — наоборот,
по часовой стрелке.
Необходимо отметить, что если сконструированная по
верхность, состоящая из непрозрачных полигонов постоян
ной ориентации, полностью замкнута, то ни одна из обрат
ных граней полигонов никогда не будет видна — они все
гда будут закрыты лицевыми гранями. Поэтому, если из
вестно, что такие конструкции всегда будут осматривать
ся только снаружи, то можно включить механизм удаления
нелицевых (обратных) граней. Это позволит увеличить ско
рость обработки и создания кадра библиотекой OpenGL,
так как она будет игнорировать их. Аналогично, если осмат
ривать замкнутую поверхность изнутри, наблюдателю будут
видны только обратные грани. В этом случае можно заста
вить OpenGL игнорировать прямые грани и отрисовывать
только обратные.
Для того чтобы включить механизм удаления лицевых
или нелицевых граней, необходимо вызвать команду
glEnable(GL_CULL_FACE), после чего с помощью команды
glCullFace(mode) определить, какие именно грани должны
игнорироваться и удаляться до их преобразования в экран
ные координаты. Параметр mode может принимать значения
GL_FRONT (для прямых граней), GL_BACK (для обратных гра
ней) или GL_FRONT_AND_BACK (для прямых и обратных граней
одновременно).
По умолчанию, лицевые и нелицевые грани растеризуют
ся одинаково — в виде заполненных внутренних областей,
задаваемых границами полигона. Режимом отображения ли
цевых и нелицевых граней полигона можно управлять с по
мощью функции glPolygonMode(face, mode). У этой функ
Геометрические примитивы и их свойства
29
ции два параметра. Первый параметр face указывает для
каких граней изменяется режим отображения. Он может
принимать значения GL_FRONT (для прямых граней), GL_BACK
(для обратных граней) или GL_FRONT_AND_BACK (для прямых
и обратных граней одновременно). Второй параметр mode
задаёт, собственно, сам режим отображения и может при
нимать следующие значения:
• GL_POINT — растеризация только вершин полигона;
• GL_LINE — растеризация вершин и ребёр полигона;
• GL_FILL — растеризация вершин, ребёр и внутренней
области полигона.
Например, если поставлена задача отобразить лицевые
грани в виде растеризованной внутренней области, а обрат
ные — в виде каркаса (набора ребёр), то перед заданием
такого полигона необходимо определить следующие режи
мы его отображения:
1
2
3
4
5
6
7
glPolygonMode (GL_FRONT, GL_FILL ) ;
glPolygonMode (GL_BACK, GL_LINE) ;
g l B e g i n (GL_POLYGON) ;
...
описание полигона
...
glEnd () ;
При растеризации внутренней области полигонов мож
но использовать шаблоны заливки. Если механизм наложе
ния шаблонов выключен, то используется сплошная залив
ка внутренней области полигона согласно цветовому режиму
или наперед заданным свойствам материала реагировать на
те или иные источники освещения.
Расчёт шаблонов при растеризации полигонов в прило
жении OpenGL, так же как и расчёт шаблонов отрезков, по
умолчанию выключен. Поэтому, перед тем как задавать шаб
лон рисования внутренних областей полигонов необходимо
сначала включить механизм расчёта с помощью команды
glEnable(GL_POLYGON_STIPPLE).
30
Рисование геометрических объектов
Сам шаблон рисования внутренних областей определяет
ся с помощью функции
glPolygonStipple(*mask).
Шаблон, по сути, является рисунком закраски полигона и
содержится в массиве — битовой карте размером 32 x 32,
на который ссылается указатель mask. Соглашения при ис
пользовании шаблона выбираются такие же, как и соглаше
ния при рисовании отрезков с шаблонами. А именно, едини
ца в определённой позиции битовой маски задаёт, что соот
ветствующий пиксель растровой развертки внутренней об
ласти полигона будет отрисован, а нуль — нет — пиксель
останется неотрисованным. Необходимо отметить, что ин
терпретация массива данных mask, так же как и способ на
ложения битовой карты на внутреннюю область полигона,
напрямую зависит от параметров, определяемых функцией
glPixelStore. Эти параметры имеют предопределённые зна
чения, которые можно посмотреть в документации, и могут
быть изменены согласно представлениям программиста пе
ред определением шаблона заливки полигона.
2.2. Определение геометрических примитивов
Определение геометрического примитива или последова
тельности примитивов одного типа производится внутри бло
ка glBegin(mode) и glEnd(). Оно включает в себя описа
ние координат вершин, задающих определённый примитив,
и, как правило, свойства вершин, такие как цвет и вектор
нормали к вершине. Описание свойств вершины необходимо
производить до задания её координат, иначе эти свойства бу
дут предписаны не ей, а следующей после описания свойств
вершине.
Параметр mode определяет тип примитива, который будет
описываться внутри блока (см. рис. 2.3). Он может прини
мать одно из значений, которые приведены в таблице 2.1
вместе с описанием самих примитивов.
Определение геометрических примитивов
Таблица 2.1. Описание примитивов OpenGL
GL_POINTS
GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP
GL_TRIANGLES
GL_TRIANGLE_STRIP
GL_TRIANGLE_FAN
GL_QUADS
GL_QUAD_STRIP
GL_POLYGON
точки (каждая вершина задаёт коор
динаты отдельной точки);
отдельные отрезки (каждая пара
вершин задаёт новый отрезок, при
этом, если задано нечётное количество
вершин, то последняя вершина игнори
руется);
ломаная (каждая следующая верши
на вместе с предыдущей задаёт отре
зок);
замкнутая ломаная (ломаная, у ко
торой последняя вершина соединяется
с первой);
отдельные треугольники (каждая
новая тройка вершин определяет от
дельный треугольник, при этом, если
задано количество вершин, не кратное
трём, то лишние вершины игнорируют
ся);
лента из треугольников (каждая
следующая вершина вместе с двумя
предыдущими задаёт треугольник);
веер из треугольников (каждые но
вые две вершины вместе с первой за
дают треугольник);
отдельные четырёхугольники (каж
дая новая четверка вершин определяет
четырёхугольник, при этом, если зада
но количество вершин, не кратное че
тырём, то лишние вершины игнориру
ются);
лента из четырёхугольников (каж
дая новая пара вершин с двумя преды
дущими задаёт четырёхугольник);
полигон (все вершины, описанные
внутри блока, задают выпуклый мно
гоугольник).
31
32
Рисование геометрических объектов
Рис. 2.3. Виды примитивов OpenGL
Необходимо отметить, что разные виды примитивов име
ют различную скорость визуализации на разных платфор
мах. Но при этом, для увеличения производительности, пред
почтительнее использовать примитивы, требующие передачи
меньшего количества информации на сервер, такие как, на
пример,
GL_TRIANGLE_STRIP,
GL_QUAD_STRIP
или
GL_TRIAGLE_FAN.
Определение векторов нормали
33
Например, чтобы нарисовать треугольник, растеризован
ный только своими вершинами разных цветов, необходимо
написать следующий код.
1 glPolygonMode (GL_FRONT_AND_BACK, GL_POINT) ;
2 g l B e g i n (GL_TRIANGLES) ;
3
/ * описание первой вершины красного цвета * /
4
g l C o l o r 3 f (1 . 0 , 0 . 0 , 0 . 0 ) ;
5
g l V e r t e x 3 f (0 . 0 , 0 . 0 , 0 . 0 ) ;
6
7
/ * описание второй вершины зеленого цвета * /
8
g l C o l o r 3 u b (0 , 255 , 0) ;
9
g l V e r t e x 3 f (1 . 0 , 0 . 0 , 0 . 0 ) ;
10
11
/ * описание третьей вершины синего цвета * /
12
g l C o l o r 3 f (0 . 0 , 0 . 0 , 1 . 0 ) ;
13
g l V e r t e x 3 f (1 . 0 , 1 . 0 , 0 . 0 ) ;
14 glEnd () ;
2.3. Определение векторов нормали
Вектором нормали (нормалью) называется вектор, ко
торый указывает в направлении, перпендикулярном поверх
ности. Для плоской поверхности это перпендикулярное на
правление одинаково во всех точках. Однако в случае изо
гнутой поверхности направление нормали может быть раз
ным в каждой точке поверхности.
В OpenGL вектор нормали может указываться как для
всего полигона, так и для каждой вершины. Для плоских по
верхностей, у которых вершины имеют одинаковую нормаль,
разумно задавать вектор нормали один раз, перед определе
нием вершин полигона. Для изогнутых поверхностей, верши
ны которых характеризуются различными векторами норма
ли, каждый вектор необходимо задавать перед определением
соответствующей вершины.
Векторы нормали объекта определяют его ориентацию в
пространстве – в частности, его ориентацию по отношению
к источникам света. Они непосредственно используются в
расчётах OpenGL машины для определения количества све
та, падающего на вершины объекта, а следовательно, влия
ют на закраску внутренних областей примитивов (метод за
34
Рисование геометрических объектов
краски Гуро). Необходимо отметить, что в каждой вершине
существует два вектора перпендикулярных поверхности, ко
торые указывают в противоположных направлениях. Одна
ко, по принятому в OpenGL соглашению, нормалью счита
ется тот из них, который указывает «наружу» поверхности
(а не «внутрь» неё).
Задать текущий вектор нормали можно с помощью функ
ции glNormal*(), которая имеет несколько версий с различ
ными суффиксами. Невекторная версия команды (без суф
фикса v) принимает три аргумента, определяющие x, y и
z-компоненты вектора, а векторная версия — один — ука
затель на массив, содержащий эти компоненты. При исполь
зовании версий функции glNormal*() с суффиксами b, s и
i значения компонент вектора линейно масштабируются до
диапазона [−1.0; 1.0] .
Приведём пример определения четырёхугольного поли
гона, в котором первой и второй вершине сопоставляются
собственные различные векторы нормали, а третьей и чер
вертой — одинаковый (общий). Поскольку текущий вектор
нормали всегда ассоциируется с вершинами, заданными по
сле его описания с помощью вызовов glVertex*(), то код
программы будет выглядеть следующим образом.
1 g l B e g i n (GL_POLYGON) ;
2
/ * описание вектора нормали первой вершины * /
3
g l N o r m a l 3 f v (n1 ) ;
4
g l V e r t e x 3 f v ( v1 ) ;
5
6
/ * описание вектора нормали второй вершины * /
7
g l N o r m a l 3 f v (n2 ) ;
8
g l V e r t e x 3 f v ( v2 ) ;
9
10
/ * описание общего вектора нормали третьей и четвертой вершины * /
11
g l N o r m a l 3 f v ( n34 ) ;
12
g l V e r t e x 3 f v ( v3 ) ;
13
g l V e r t e x 3 f v ( v4 ) ;
14 glEnd () ;
Так как нормали используются только для указания на
правления, их длина не имеет значения. В общем случае
векторы нормали могут быть произвольной длины. Однако,
так как в OpenGL используется модель освещения Блинна
Определение векторов нормали
35
Фонга, то для корректного расчёта освещённости в точке все
нормали должны быть преобразованы к векторам единичной
длины. Поэтому, до того как заданные вершины попадут для
обработки на конвейер и будут произведены расчёты, свя
занные с освещением, необходимо произвести нормализа
цию векторов.
Даже если в приложении задаются только нормализован
ные векторы нормалей, а этого всегда можно добиться поде
лив каждую из его компонент на длину вектора, некоторые
применяемые к объектам сцены преобразования (например,
масштабирование) могут изменить эти векторы и нарушить
нормализацию. Поэтому, если при построении сцены про
изводится масштабирование, используются специфические
матрицы преобразований или же задаются ненормализован
ные векторы нормалей, необходимо использовать автома
тическую нормализацию векторов после преобразований в
OpenGL.
Такие команды, как glEnable(GL_NORMALIZE) и
glEnable(GL_RESCALE_NORMAL), включают режим нормали
зации векторов. В режиме, включаемом первой командой,
сначала вычисляется длина вектора нормали, а затем каж
дый из компонентов вектора делится на эту величину. Такая
нормализация гарантирует, что результирующие нормали бу
дут иметь единичную длину.
Во втором случае вектора нормализуются путем деления
компонент вектора на величину изменения масштаба, взя
тую из матрицы модельного преобразования. Однако, такая
нормализация будет корректно работать только если выпол
нялось равномерное масштабировании объектов по всем на
правлениям, так как делитель выбирается общим для всех
компонент. Автоматическая нормализация или масштабиро
вание обычно требует дополнительных расчётов, которые
могут снизить быстродействие приложения (по умолчанию
оба механизма выключены), при этом равномерное масшта
бирование нормалей с помощью GL_RESCALE_NORMAL менее
расточительно в смысле производительности.
3. Создание трёхмерной сцены и её
видового представления
3.1. Системы координат
Главной целью машинной графики является создание дву
мерного представления трёхмерных объектов, так как боль
шинство графических устройств умеют выводить только
плоские изображения. OpenGL предоставляет механизм, ко
торый позволяет пользователю не думать о том, что именно
нужно сделать, чтобы на экране появилось двумерная кар
тинка описанной ранее сцены. Библиотека позволяет поль
зоваться средствами для создания трёхмерной сцены, опи
сания геометрии и положения объектов в трёхмерной (ми
ровой) системе координат, позиционирования камеры и на
стройки вида, описания способа проецирования, а после
чего самостоятельно создаёт двумерную проекцию сцены
(растр).
В OpenGL используются три основные системы коорди
нат: трёхмерная левосторонняя, трёхмерная правосторонняя
и двумерная оконная (см. рис. 3.1). Первые два вида систем
являются обычными трёхмерными декартовыми системами
координат и используются для описания сцены. Они отли
чаются друг от друга направлением оси Oz. В правосторон
ней системе координат она направлена на наблюдателя, а в
левосторонней – от него.
Правосторонняя система координат используется для
описания геометрии и положения объектов, к тому же в
ней осуществляются все модельно-видовые преобразования
объектов сцены. Левосторонняя система координат исполь
зуется при задании усечённого объёма видимости камеры и
определении плоскости, на которую будет проецироваться
сцена. Выбор левосторонней системы координат при про
ецировании не случаен, с ней удобнее работать, так как в
Системы координат
37
Рис. 3.1. Системы координат OpenGL
ней направление оси Oz совпадает в направлением обзора
камеры (камера направлена вдоль положительной части оси
Oz). И, наконец, двумерная оконная система координат ис
пользуется для задания области вывода (порта просмотра)
проекции сцены в окне приложения. Преобразование проек
ции для показа в окне приложения называется трансфор
мацией порта просмотра.
Итак, трёхмерные координаты объекта преобразуются в
позиции пикселей на экране серией операций. Последова
тельность преобразований, которую проходят вершины с мо
мента задания их объектных (мировых) координат до полу
чения оконных координат, показана на рисунке 3.2.
На основе мировых координат с помощью модельно
видовых преобразований вычисляются видовые координа
ты объектов. Видовое преобразование заключается в пози
ционировании камеры (или другими словами местоположе
ния наблюдателя) и задания её ориентации в пространстве.
Модельные преобразования комбинируются с видовыми. К
ним относятся афинные преобразования, такие как перенос,
поворот и изменение масштаба по координатным осям.
После модельно-видовых преобразований к координатам
применяется преобразование проекции и нормализация ко
ординат. Преобразование проекции определяет усечённый
объём видимости на сцене. Объекты вне этого объёма пол
38
Создание трёхмерной сцены и её видового представления
Рис. 3.2. Этапы преобразования координат объектов
ностью или частично отсекаются (отбрасываются) и, как ре
зультат, не рисуются в итоговой сцене. В приложении можно
задавать дополнительные плоскости отсечения для удаления
определённых объектов из сцены или для создания среза
объектов. После применения этих преобразований получа
ются усечённые координаты.
На основе усечённых координат вычисляются нормали
зованные координаты устройства. Для этого производит
ся перспективное деление — деление координат x, y и
z на величину w, после чего координата z отделяется от
первых двух и сохраняется в буфере глубины. После отсе
чения и перспективного деления z-координаты, так же как
Системы координат
39
и (x, y)-координаты, оказываются в диапазоне [−1.0; 1.0] .
Значение −1.0 соответствует ближней плоскости отсечения,
а 1.0 — дальней.
В заключение, нормализованные координаты с помощью
трансформации порта просмотра преобразуются в оконные
координаты. Сама трансформация задаёт новые размеры
проекции сцены (область вывода), что соответствует мас
штабированию, и позицию в окне просмотра, где её необхо
димо расположить.
Процесс трансформаций, используемых для создания
сцены, аналогичен получению фотографии с помощью ка
меры. Как показано на рисунке 3.3 для получения фотогра
фии (или сцены на экране монитора) необходимо выполнить
следующие процедуры.
1. Установить треногу и направить камеру на сцену (ви
довые преобразования).
2. Расположить фотографируемые объекты перед каме
рой (модельные преобразования).
3. Выбрать линзы для камеры и масштаб (проекционные
преобразования).
4. Определить, какого размера должна быть результиру
ющая фотография (трансформация порта просмотра).
5. Напечатать фотографию (вывести растеризованное
изображение на экран).
Заметим, что нумерация шагов соответствует порядку, в
котором необходимо задавать соответствующие преобразо
вания. При этом этот порядок не соответствует порядку, в
котором OpenGL производит математические вычисления
над вершинами объектов. Таким образом, в программном
коде видовые трансформации должны быть определены до
модельных, а задание проекционных преобразований и пре
образования порта просмотра можно выполнять в любой мо
мент до рисования объектов.
40
Создание трёхмерной сцены и её видового представления
Рис. 3.3. Аналогия преобразований OpenGL и процесса получе
ния фотографии
Матричные преобразования координат
41
3.2. Матричные преобразования координат
Каждому преобразованию координат вершин в OpenGL
соответствует собственная матрица преобразования. С точ
ки зрения линейной алгебры новые координаты объектов по
лучаются из старых, путем умножения вектора координат на
матрицу соответстующего преобразования справа
X 0 = X ∗ M.
В библиотеке определены три вида матриц преобразова
ния.
• Модельно-видовая матрица (GL_MODELVIEW). Эта мат
рица задаёт модельно-видовые преобразования коор
динат объектов. Эта матрица меняется в момент пози
ционирования и установки направления камеры, а так
же при применении аффинных преобразований к объ
ектам сцены.
• Матрица проецирования (GL_PROJECTION). Эта матри
ца задаёт преобразования проекции и изменяется в мо
мент, когда выбирается плоскость проецирования и за
даётся усечённый объём видимости объектов.
• Матрица текстуры (GL_TEXTURE). Эта матрица отвечает
за текстурные преобразования и изменяется при опре
делении свойств наложения текстурных объектов на
геометрические объекты.
Одномоментно можно использовать только один вид мат
рицы. Поэтому при определении преобразований необходи
мо следить за тем, чтобы в текущий момент был выбран нуж
ный, соответствующий этим преобразованиям, тип. Выбрать
соответствующую матрицу для работы и сделать её текущей
можно с помощью вызова команды glMatrixMode(mode),
где параметр mode определяет тип матрицы: GL_MODELVIEW,
GL_PROJECTION или GL_TEXTURE.
В OpenGL существует набор команд для работы с мат
рицами. Их можно поделить на две группы. Первая группа
42
Создание трёхмерной сцены и её видового представления
позволяет работать с матрицами непосредственно, а именно
установить в них требуемые значения компонент или полу
чить новую матрицу преобразования путём умножения ста
рой на матрицу, соответствующую новому преобразованию.
Перечислим некоторые из них:
• glLoadIdentity() — функция, которая устанавливает
текущую матрицу в единичную.
• glLoadMatrix*(array) — функция, которая позволя
ет загрузить в текущую матрицу значения компонент,
определённых в массиве array. Параметр array дол
жен являться указателем на массив длиной в 16 эле
ментов, а сама матрица заполняется по столбцам, сверху
вниз и слева-направо.
• glMultMatrix*(array) — функция, которая умножа
ет текущую матрицу на матрицу, описанную в массиве
array.
Вторая группа позволяет менять матрицы апосредован
но, путем задания новых аффинных преобразований с по
мощью функций сдвига, поворота или масштафирования,
а также определения перспективных или ортографических
преобразований в случае, если речь идет о преобразованиях
проекции.
3.3. Модельно-видовые преобразования
Видовое преобразование (позиционирование камеры)
Видовое преобразование аналогично позиционированию
и наведению камеры. По умолчанию, если в приложении не
задано видовое преобразование, камера расположена в на
чале мировой системы координат, в т. (0, 0, 0), направлена
вдоль отрицательной части оси Oz и ориентирована векто
ром верхнего направления (0, 1, 0) по оси Oy.
Модельно-видовые преобразования
43
Изменить положение и направление камеры можно с по
мощью команды
gluLookAt(xeye, yeye, zeye, xdir, ydir, zdir,
xup, yup, zup).
Первые три агрумента этой команды определяют новое поло
жение камеры, следующие три — направление, куда она бу
дет направлена, и наконец, последние три аргумента задают
верхнее направление камеры (ориентацию камеры), опреде
ляя тем самым поворот камеры вокруг вектора направления.
Вектор направления определяет какие объекты сцены будут
проецироваться в центр области вывода.
Команда gluLookAt() задаёт видовую матрицу и умножа
ет на неё текущую матрицу. Поэтому, во-первых, перед опре
делением видового преобразования необходимо убедиться,
что в настоящий момент в качестве текущей выбрана мо
дельно-видовая матрица. А во-вторых, инициализировать
эту матрицу единичной. В противном случае, видовое преоб
разование лишь продолжит цепочку предыдущих преобра
зований и камера будет позиционирована не так, как было
указано аргументами команды gluLookAt().
Необходимо отметить, что видовые трансформации мо
гут быть обратно симметричны аффинным преобразованиям.
Например, вместо того, чтобы передвигать камеру, можно
соответствующим образом передвинуть объекты сцены от
носительно камеры. При этом полученное изображение бу
дет таким же. Поэтому, в OpenGL не разделяют видовые и
модельные преобразования, которые задаются в общей мо
дельно-видовой матрице.
Модельные преобразования
OpenGL поддерживает три вида афинных преобразова
ний: сдвиг, поворот и масштабирование. С помощью компо
зиции этих преобразований можно задавать любое модель
ное преобразование объектов сцены. С точки зрения линей
44
Создание трёхмерной сцены и её видового представления
ной алгебры каждому афинному преобразованию соответ
ствует своя, характерная этому преобразованию матрица.
Для того, чтобы применить преобразование к объекту, необ
ходимо вектор координат объекта умножить на эту матри
цу справа. Композиция аффинных преобразований описы
вается серией матричных умножений. Однако, необходимо
помнить, что поскольку матричное умножение производится
справа и в общем случае оно не коммутативно, то приме
нять матричные преобразования следует в порядке обрат
ном желаемым аффинным преобразованиям. Так, например
если требуется произвести сдвиг, а затем поворот объекта
вокруг координатной оси, необходимо вектор координат объ
екта сначала умножить на матрицу поворота и уже только
потом на матрицу сдвига.
Перечислим функции, задающие аффинные преобразова
ния объектов в OpenGL.
• glTranslate*(x, y, z) — задаёт преобразование пе
реноса (сдвига) объекта на расстояние, соответствую
щее вектору (x, y, z). Это преобразование, как и сле
дующие два, меняет текущую матрицу, домножая её на
соответствующую матрицу, в данном случае, матрицу
сдвига.
• glRotate*(angle, x, y, z) — задаёт преобразование
поворота объекта против часовой стрелки на угол anglе
(в градусах) относительно вектора (x, y, z).
• glScale*(kx, ky, kz) — задаёт преобразование мас
штабирования объекта (сжатие или растяжение) по
осям координат Ox, Oy и Oz, умножая соответству
ющие координаты его вершин на значения kx, ky и kz
соответственно.
Еще одним способом применения аффинных преобразо
ваний к объектам является использование понятия локаль
ной системы координат. Локальная система координат
позволяет рассматривать афинные преобразования в систе
ме координат, привязанной к самому объекту. При таком
подходе можно забыть о фиксированной (глобальной миро
Модельно-видовые преобразования
45
вой) системе координат и считать, что текщий объект всегда
находится в начале локальной системы координат. В этом
случае оказывается, что матричные преобразования приме
няются к объектам в своём естественном порядке — после
перехода к новой локальной системе координат.
Подход, связанный с применением понятия локальной
системы координат, следует использовать при рисовании
сложных конструируемых объектов. Например, при модели
ровании скелета человека описание расположения пальцев
проще давать относительно положения руки человека или
даже кисти этой руки, совершая необходимые повороты и
переносы в каждой точке соединений (суставов). В таких
случаях, если думать о создании объекта в терминах фикси
рованной системы координат и обратного порядка преобра
зований, можно быстро запутаться. А в локальной системе
координат все вычисления становятся логичными и прозрач
ными.
OpenGL поддерживает механизм использования матрич
ных стеков для построения иерархических моделей, в ко
торых сложные объекты конструируются при помощи про
стых. Фактически, каждый раз когда меняется текущая мат
рица, меняется верхний элемент матричного стека. Необхо
димо отметить, что матричных стеков нескольно, один для
хранения модельно-видовых матриц, а другой — для проек
ционных матриц. Для работы со стеками в OpenGL суще
ствует две функции: glPushMatrix() и glPopMatrix().
С помощью этих команд можно сохранять и восстанавли
вать локальные матрицы. Команда glPushMatrix() сохраня
ет текущую матрицу и добавляет её копию на вершину мат
ричного стека. Другими словами вызов этой команды равно
силен фразе «запомнить, где мы сейчас находимся». Коман
да glPopMatrix() уничтожает верхнюю (текущую) матрицу
на стеке. Таким образом последние — локальные — пре
образования исчезают из схемы расчётов координат новых
объектов, что другими словами обозначает «вернуться туда,
где мы были».
46
Создание трёхмерной сцены и её видового представления
3.4. Проекционные преобразования
После всех модельно-видовых преобразований с объек
тами и камерой, для получения двумерной проекции, необхо
димо выполнить проекционное преобразование сцены. На
значение проекционного преобразования заключается в за
дании объёма видимости. Объём видимости, во-первых,
определяет тип проекции, а именно, то, каким образом объ
екты сцены проецируются на плоскость. А во-вторых, объ
ём видимости определяет местоположение плоскости про
екции, на которой будет получаться плоское изображение,
и геометрию сцены, вне которой объекты или их части бу
дут отсечены, то есть не попадут в зону обзора камеры, а
следовательно и в прямоугольник проекции.
Необходимо помнить, что любые преобразования в
OpenGL меняют текущую матрицу. Поэтому, перед тем как
задавать проекционные преобразования, необходимо убе
диться, что в настоящий момент выбрана и используется
именно матрица проекций.
1 glMatrixMode (GL_PROJECTION) ;
2 g l L o a d I d e n t i t y () ;
Обычно команды проекционных преобразований полностью
описывают отдельную транформацию. Поэтому, как прави
ло, не требуется комбинировать одну трансформацию с дру
гой. Вызов команды glLoadIdentity() гарантирует избежа
ние составных проекционных преобразований. При этом,
конечно же, пользователь библиотеки всегда может задать
произвольное преобразование проекции, воспользовавшись
такими командами, как glLoadMatrix*() и glMultMatrix*(),
которые меняют текущую матрицу напрямую.
Среди проекций, которые в OpenGL можно задать специ
альными командами, можно выделить две наиболее широко
используемые:
• перспективная (или центральная) проекция;
• ортографическая (или параллельная) проекция.
Проекционные преобразования
47
Перспективная проекция определяет область видимо
сти в виде усечённой пирамиды с прямоугольным основани
ем. Для получения плоского перспективного изображения
(линейной перспективы) какого-либо объекта из выбранной
точки пространства (центра перспективы) проводят лучи ко
всем точкам данного объекта. На пути лучей ставят плос
кость, на которой желают получить изображение. Эту плос
кость называют плоскостью проекции. В пересечении про
ведённых лучей с плоскостью проекции получают искомое
изображение объекта.
Перспективная проекция наиболее близка для понима
ния и представления человеку, так как человеческий глаз
воспринимает окружающий нас мир похожим образом. Дан
ный способ проецирования обычно используется при созда
нии анимации, визуальной симуляции и других реалистич
ных приложений. Этот вид проекции обладает следующими
свойствами.
• Объекты при удалении от наблюдателя, а следователь
но, и от плоскости проекции уменьшаются в проекции.
Это является следствием геометрического построения
проекции в виде усечённой пирамиды.
• Объекты в перспективной проекции не сохраняют свои
геометрические свойства, а именно не сохраняются про
порции размеров объектов, в том числе длин рёбер гра
ней полигональных моделей и величины углов между
ними. Перспективные изображения параллельных пря
мых пересекаются в так называемых точках схода, а
параллельных плоскостей в линиях схода.
Для задания перспективной проекции можно использо
вать одну из функций glFrustum() или gluPerspective().
Обе функции позволяют задать перспективное преобразова
ние проекции и изменяют матрицу преобразования. Разни
ца между этими функциями заключается в разных способах
определения области видимости — усечённой пирамиды.
48
Создание трёхмерной сцены и её видового представления
Функция glFrustum(left, right, bottom, top, near,
far) создаёт матрицу перспективной проекции и умножает
на неё текущую матрицу. Она имеет шесть параметров. Пер
вые четыре параметра (left, bottom) и (right, top) опре
деляют координаты левого нижнего и правого верхнего уг
лов прямоугольника видимости ближней отсекающей плос
кости, которая и является плоскостью проекции. Послед
ние два параметра near и far задают расстояние от точки
наблюдения до ближней и дальней отсекающих плоскостей
(они всегда должны быть положительными). На рис. 3.4 по
казан объём видимости и определяющие его параметры для
функции glFrustum().
Рис. 3.4. Объём видимости, задаваемый функцией glFrustum()
Вместо функции glFrustum() для задания перспективной
проекции можно использовать функцию gluPerspective().
Она создаёт объём видимости той же формы, но с помощью
других
параметров.
При
использовании
функции
gluPerspective(fovy, aspect, near, far), вместо опреде
ления координат углов прямоугольника видимости отсекаю
Проекционные преобразования
49
щей плоскости необходимо задавать угол визуального охва
та камеры в вертикальном направлении (параметр fovy) и
отношение ширины проекции к её высоте (параметр aspect).
Из рис. 3.5 видно, что этих двух параметров достаточно для
того, чтобы задать неусечённую пирамиду. Оставшиеся два
параметра near и far, так же как и при использовании функ
ции glFrustum(), необходимы, чтобы задать положение от
секающих плоскостей.
Рис.
3.5.
Объём
gluPerspective()
видимости,
задаваемый
функцией
Ортографическая проекция определяет область види
мости в виде прямоугольного параллепипеда. Ортографиче
ская проекция строится с помощью лучей, которые падают
перпендикулярно плоскости проекции и параллельны друг
другу. Таким образом, проводя аналогию с построением пер
спективной проекции, можно считать, что центр ортографи
ческой проекции бесконечно удалён от плоскости проекции.
В отличие от перспективной проекции, ортографическая
проекция устроена так, что при изменении расстояния до
наблюдателя геометрические размеры сечения объёма види
50
Создание трёхмерной сцены и её видового представления
мости не меняются. Такое постоение определяет следующие
свойства проекций объектов.
• При изменении расстояния от камеры до объектов сце
ны размеры их проекций не изменяются. В связи с
этим, в ортографической проекции сложно определить,
какой из двух одинаковых объектов расположен ближе
к наблюдателю, а какой — дальше.
• Объекты в ортографической проекции сохраняют свои
геометрические свойства, а именно сохраняются как
пропорции размеров объектов, так и величины опреде
ляющих модели углов.
Свойства ортографической проекции определяют область
её применения. Её используют в системах автоматизирован
ного проектирования, инженерной графике, при составлении
чертежей и других приложениях, где важны реальные раз
меры объектов относительно друг друга и точность отобра
жения углов между ними.
Для задания ортографической проекции в OpenGL опре
делена функция glOrtho(left, right, bottom, top, near,
far). Она создаёт матрицу ортографической проекции и
умножает на неё текущую матрицу. Функция имеет шесть
параметров. Первые четыре параметра (left, bottom) и
(right, top) определяют координаты левого нижнего и пра
вого верхнего углов прямоугольника видимости отсекающих
плоскостей. Последние два параметра near и far задают
z-координаты ближней и дальней отсекающих плоскостей.
Они могут быть положительными, отрицательными и даже
нулевыми, но при этом должны быть не равны между собой.
Если не используются никакие другие преобразования, то
направление проецирования совпадает с направлением оси
Oz, а направление обзора — с её отрицательным направле
нием. На рис. 3.6 показан объём видимости и определяющие
его параметры для функции glOrtho().
Трансформация порта просмотра
51
Рис. 3.6. Объём видимости, задаваемый функцией glOrtho()
Для специального случая, когда двумерное изображе
ние проецируется на экран, можно использовать функцию
gluOrtho2D(left, right, bottom, top) из библиотеки ути
лит. Она идентична функции glOrtho() и предполагает, что
все z-координаты объектов лежат в диапазоне
[−1.0; 1.0] . Поэтому, если при рисовании объектов исполь
зовать двумерные версии вершинных команд, ни один из
этих объектов не будет отсечен из-за своих z-координат,
так как они будут по умолчанию равны нулю.
3.5. Трансформация порта просмотра
Порт просмотра (область вывода) представляет из
себя прямоугольник пикселей в окне приложения, в который
переносится итоговое изображение проекции сцены. При ра
боте с портом просмотра используется двумерная оконная
система координат, в которой позиции пикселей на экране
задаются относительно нижнего левого угла созданного ок
на.
52
Создание трёхмерной сцены и её видового представления
Изображение, полученное в прямоугольнике видимости
на плоскости проекции, может быть перенесено в порт про
смотра без изменения размеров, а также равномерно или
неравномерно отмасштабированным по осям координат.
Преобразование проекции к размерам области вывода назы
вается трансформацией порта просмотра. Это преобра
зование можно задать с помощью функции glViewPort(x,
y, width, height). Аргументы этой функции задают сме
щение нижнего левого угла порта просмотра (x, y) в окон
ной системе координат и размеры области вывода, а именно
её ширину width и высоту height.
Описание размеров порта просмотра обычно произво
дится в функции, зарегистрированной с помощью команды
glutReshapeFunc(). Эта функция вызывается в момент ко
гда пользователь изменяет размеры окна приложения и по
лучает в качестве аргументов новые размеры в пикселах.
Приложение, в свою очередь, может использовать новые
размеры окна для изменения размеров порта просмотра или
оставить размеры такими же, изменив лишь позицию порта
просмотра в окне.
Для хранения оконных z-координат, как и для хранения
компонент цветов, используется диапазон [0.0; 1.0] . Значе
ние 0.0 соответствует ближней плоскости отсечения проек
ции, а 1.0 — дальней. А так как в буфере глубины значения
z-координат хранятся в диапазоне [−1.0; 1.0] , то для полу
чения оконных z-координат изпользуется линейное преоб
разование.
Однако, диапазон оконных z-координат можно изменить. Для этого можно воспользоваться фукцией
glDepthRange(znear, zfar), которая, с учетом масштаби
рования, позволяет сопоставить ближней и дальней плоско
стям отсечения новые значения znear и zfar соответствен
но. Перед преобразованием новый диапазон проверяется на
принадлежность диапазону [0.0; 1.0] , и, в случае если обна
руживается выход за границы, новые значения обрезаются
по границам используемого в оконных координатах диапа
Трансформация порта просмотра
53
зона. Необходимо отметить, что в OpenGL допускаются так
называемые «обратные» диапазоны, подобные [1.0; 0.0] , в
которых величина znear больше величины zfar.
4. Формирование изображения в
буфере кадра
4.1. Растеризация примитивов
После преобразования вершин примитивов в оконные ко
ординаты проводится процесс растеризации. На этапе рас
теризации выполняется вычисление цветов отдельных пик
селей, образующих растровое представление примитивов.
Растровое представление примитивов, как правило, вклю
чает в себя их вершины, границы и внутренние области,
если таковые имеются. Цвета отдельных пикселей расчиты
ваются на основе свойств примитивов, таких как размер то
чек, толщина линий, используемые шаблоны, модели залив
ки границ и внутренних областей, а также, если включены
соответствующие механизмы, с учётом освещения, накла
дываемых на объекты текстур, свойств наложения тумана
и сглаживания. Процесс растеризации требует выполнения
огромного объёма вычислений, для выполнения большин
ства которых используется процессор видеокарты. Конеч
ным результатом этапа растеризации является двухмерное
изображение (растр), выводимое в окно приложения.
Однако, после этапа растеризации данные сохраняются
в текущий цветовой буфер не сразу. После преобразова
ний, выполненных на этапе растеризации, геометрические
и пиксельные данные с учетом их растрового представления
формируются в фрагменты. Каждый фрагмент содержит в
себе информацию о координатах, соответствующих будуще
му положению пиксела, а также ассоциируется с цветовым
значением и z-координатой, которая хранится в буфере глу
бины. Фрагменты, перед тем как стать пикселами цветово
го буфера, проходят серию тестов. В дополнение к тестам,
к фрагменту можно применять определённый набор опера
ций, которые могут изменить его. В случае если фрагмент
Тестирование фрагментов
55
проходит все тесты, он становится пикселем, и цветовая ин
формация о нем записывается в текущий цветовой буфер
кадра.
Перечислим все виды тестов, которые может проходить
фрагмент до сохранения в буфер кадра:
• тест отреза (scissor test);
• альфа-тест (alpha test);
• тест глубины (depth test);
• тест трафарета (stencil test).
По умолчанию все тесты выключены, так как большинство
из них требуют выполнения сложных расчётов, понижаю
щих производительность приложения. Поэтому, для того
чтобы произвести то или иное тестирование, необходимо его
активировать командой glEnable(mode) с соответствующим аргументом GL_SCISSOR_TEST, GL_ALPHA_TEST,
GL_DEPTH_TEST или GL_STENCIL. Выключить отдельное те
стирование можно с помощью команды glDisable(mode), а
проверить активирован ли тест или нет — с помощью ко
манды glIsEnable(mode).
Тест глубины и тест трафарета требуют использования
вспомогательных буферов, которые подключаются при ини
циализации экранного режима. Перед проведением тестиро
вания, если необходимо, эти буферы следует очистить.
4.2. Тестирование фрагментов
Тест отреза
Тест отреза позволяет ограничить рисование фрагмен
тов вне ограниченной прямоугольной области окна — пря
моугольника отреза (scissorbox). По умолчанию размеры
прямоугольника отреза совпадают с размерами окна выво
да. Изменить его можно с помощью функции glScissor(x,
y, width, height). Значения параметров x и y определяют
положение нижнего левого угла прямоугольника внутри ок
на вывода в пикселах, а width и height — его ширину и
56
Формирование изображения в буфере кадра
высоту. Считается, что фрагмент проходит тест отреза, если
он лежит внутри заданного прямоугольника.
Узнать значения параметров x, y, width и height, опре
деляющие в данный момент прямоугольник отреза можно с
помощью команды glGetIntegerv(pname, *pval). Для это
го в качестве параметра pname этой функции необходимо
передать значение GL_SCISSOR_BOX. После вызова команды
значения параметров, определяющих прямоугольник отреза,
будут скопированы в массив по указателю pval.
Альфа-тест
Альфа-тест позволяет принимать или отвергать фраг
мент в зависимости от значения его альфа-компоненты. При
активации теста величина альфа-компоненты сравнивается
с некоторым заданным значением. В зависимости от резуль
тата этого сравнения фрагмент либо принимается (значение
GL_TRUE), либо отвергается (значение GL_FALSE).
Функция glAlphaFunc(func, value) позволяет задать
функцию сравнения и значение, с которым будет сравни
ваться альфа-компонента входящего фрагмента. Параметр
func может принимать одно из константных значений, пере
численных в табл. 4.1. Значение второго параметра value
усекается до диапазона [0.0; 1.0] , так как величина альфа
компоненты в цветовой схеме RGBA ограничена в OpenGL
этим же диапазоном.
Таблица 4.1. Функции сравнения
Название функции
GL_NEVER
GL_ALWAYS
GL_LESS
GL_LEQUAL
GL_EQUAL
GL_GEQUAL
GL_GREATER
GL_NOTEQUAL
Результат сравнения
всегда GL_FALSE
всегда GL_TRUE
GL_TRUE, если α < value
GL_TRUE, если α <= value
GL_TRUE, если α = value
GL_TRUE, если α >= value
GL_TRUE, если α > value
GL_TRUE, если α ! = value
Тестирование фрагментов
57
По умолчанию, если пользователь не переопределил ина
че, для сравнения используется функция GL_ALWAYS, а
параметр value установлен в значение 0. При этом, для
того, чтобы узнать используемые функцию сравнения и
сравниваемое значение, можно воспользоваться командой
glGetIntegerv()
и
получить
значение
параметра
GL_ALPHA_FUNC, а также — GL_ALPHA_TEST_REF.
Тест глубины
Главное назначения теста глубины заключается в исполь
зовании механизма удаления невидимых граней (поверхно
стей) с помощью z-буфера. При активировании теста глу
бины OpenGL для каждого фрагмента — образа объекта на
плоскости проекции, который попал в область видимости ка
меры — запоминает дистанцию между наблюдателем и про
образом этого фрагмента. После чего для нового фрагмента,
который попадает на ту же самую позицию прямоугольни
ка проекции, выполняется тест глубины. Если тест глубины
проходит, то цветовое значение нового фрагмента заменя
ет значение в цветовом буфере кадра, а его z-координата
заменяет соответствующее значение в буфере глубины. В
противном случае фрагмент отвергается.
По умолчанию, при проведении теста глубины исполь
зуется функция сравнения «меньше» (GL_LESS). Таким об
разом, если расстояние до нового фрагмента меньше, чем
расстояние до объектов, которые были растеризованы ра
нее в этой позиции, то этот фрагмент закрывает собой все
предыдущие объекты. В этом случае тест глубины прохо
дит и новый фрагмент замещает собой предыдущие. Это со
ответствует общему представлению об удалении невидимых
частей объектов и после визуализации всей сцены на экране
остаются только ничем не загороженные объекты.
Однако функцию сравнения теста глубины тоже мож
но изменить с помощью функции glDepthFunc(func). Един
ственный её аргумент может принимать значения, перечис
58
Формирование изображения в буфере кадра
ленные в таблице 4.1. У этой функции нет второго аргумен
та, так как сравниваемым значением всегда является зна
чение z-координаты, которое хранится в текущий момент в
буфере глубины.
Тест трафарета (маски)
Тест трафарета по своей природе похож на тест глубины.
Он позволяет принимать или отвергать фрагмент в зависи
мости от значения, которое хранится в буфере трафарета.
Трафарет — это двумерный массив данных, в котором мо
гут храниться целочисленные значения. Размер двумерного
массива буфера трафарета такой же, как и буфера глуби
ны, а именно: каждой (x, y)-позиции получившегося растра
соответствует отдельное значение в буфере трафарета.
Тест трафарета обычно организуется в два этапа. Сна
чала необходимо заполнить буфер трафарета, а потом ос
новываясь на его содержимом отрисовать объекты сцены,
фрагменты которых проходят тест трафарета на определён
ной функции сравнения. Буфер трафарета заполняется сле
дующим образом. Окно вывода делится на геометрические
зоны и каждой зоне присваиваете своё уникальное значение
в буфере трафарета. Функция сравнения, в свою очередь,
может использовать эти значения для того, чтобы принять
или отвергнуть входящий фрагмент.
Заполнение буфера трафарета происходит при помощи
двух функций:
glStencilFunc(func, value, mask);
glStencilOp(sfail, zfail, zpass);
Первая функция glStencilFunc() задаёт правило, по кото
рому будет определяться, пройден ли тест трафарета или
нет. Логическое выражение, определяющее правило тести
рования, всегда имеет следующий вид:
(value & mask) func (stencil & mask),
где параметры value, mask и func (см. табл. 4.1) задаются
Тестирование фрагментов
59
как параметры функции, а величина stencil — это значе
ние, которое хранится в соответствующей позиции буфера
трафарета.
Вторая функция glStencilOp() позволяет задать способ
заполнения буфера трафарета. Её параметры sfail, zfail
и zpass определяют операцию, которая будет применяться
к соответствующему значению буфера трафарета в одном из
перечисленных случаев:
• sfail — если тест трафарета завершился успешно;
• zfail — если тест трафарета и тест глубины отвергают
входящий фрагмент;
• zpass — если тест трафарета отвергает входящий фраг
мент, а тест глубины — принимает.
Эти параметры могут задавать одну из операций, перечис
ленных в таблице 4.2.
Таблица 4.2. Операции в буфере трафарета
Название
GL_KEEP
GL_ZERO
GL_INCR
GL_DECR
GL_INVERT
GL_REPLACE
Действие операции
сохранить значение
установить значение в нуль
увеличить значение на единицу
уменьшить значение на единицу
побитово инвертировать значение
заменить значение на значение параметра
value, заданного функцией glStencilOp()
По умолчанию, при активации теста трафарета использу
ется функция сравнения GL_ALWAYS, а значения переменных
value и mask равны нулю и маске из единиц соответственно.
Результаты операций инкремента и декремента всегда усе
каются до диапазона [0; 2s −1] , где s — количество битовых
плоскостей в буфере трафарета.
60
Формирование изображения в буфере кадра
4.3. Маскирование данных в буфере кадра
Перед записью данных в составляющие буфера кадра к
ним могут применяться операции маскирования. В случае
применения этих операций, перед тем как данные будут за
писаны в соответствующий буфер, над ними производится
операция побитового логического «И» с маской, которая
определяется специальной функцией OpenGL. Всего таких
функций четыре: две для цветового буфера, по одной для
каждого из цветовых режимов (RGBA и индексного), а так
же две функции для маскирования буферов глубины и трафа
рета. Прототипы этих функций приведены далее в порядке
их перечисления.
glColorMask(GLboolean r, GLboolean g, GLboolean b,
GLboolean a);
glIndexMask(GLuint mask);
glDepthMask(GLboolean flag);
glStencilMask(GLuint mask);
Маски типа GLboolean разрешают или запрещают запись
соответствующей компоненты в буфер. А именно, если па
раметр выставлен в значение GL_TRUE, то компонент будет
записан в буфер. В противном случае, когда используется
значение GL_FALSE, буфер будет использоваться в режиме
«только для чтения» и данные в него записываться не бу
дут. По умолчанию маски типа GLboolean заданы значени
ем GL_TRUE.
Маски типа GLuint разрешают или запрещают запись
не целой компоненты, а её соответствующего разряда или,
другими словами, битовой плоскости. В этом случае, если
в определённом разряде маски указано значение 1, то со
ответствующий разряд записывается в буфер, иначе — нет.
По умолчанию все биты маски типа GLuint выставлены в
значение 1.
Области применения маскирования буферов достаточно
разнообразны. Например, маскирование в цветовом RGBA
Маскирование данных в буфере кадра
61
режиме можно использовать для загрузки трёх разных изоб
ражений в красную, зелёную и синию битовые плоскости.
Маскирование в буфере глубины позволяет решить пробле
мы растеризации непрозрачных и полупрозрачных объектов
сцены, запрещая запись в буфер значений глубины полупро
зрачных объектов. А маскирование буфера трафарета позво
ляет эффективно использовать его разные битовые плоско
сти для одновременного хранения нескольких трафаретов.
Список литературы и интернет-ресурсов
[1] Хилл Ф. OpenGL. Программирование компьютерной
графики. Для профессионалов. — СПб.: Питер, 2002. —
1088 с.
[2] Роджерс Д., Адамс Дж. Математические основы ма
шинной графики. — М.: Мир, 2001. — 604 с.
[3] Роджерс Д. Алгоритмические основы машинной графи
ки. — М.: Мир, 1989. — 512 с.
[4] Порев В. Компьютерная графика: учебное пособие. —
СПб.: БХВ-Петербург, 2004. — 432 c.
[5] Шикин Е.В., Боресков А.В. Компьютерная графика.
Полигональные модели. — М.: Диалог-МИФИ, 2001.
— 464 c.
[6] http://www.opengl.org — Официальный сайт OpenGL.
[7] http://www.opengl.org.ru — Сайт, посвящённый про
граммированию с использованием OpenGL.
Download