Главная страница

Планета знаний


Скачать 1.68 Mb.
НазваниеПланета знаний
АнкорOpenGL book
Дата14.06.2021
Размер1.68 Mb.
Формат файлаpdf
Имя файлаgltutorialcourse2.pdf
ТипКнига
#217321
страница2 из 9
1   2   3   4   5   6   7   8   9
1) В чем, по вашему мнению, заключается необходимость со- здания стандартной графической библиотеки?
2) Кратко опишите архитектуру библиотек OpenGL и органи- зацию конвейера.
3) В чем заключаются функции библиотек, подобных GLUT

или GLX? Почему они формально не входят в OpenGL?
4) Назовите категории команд (функций) библиотеки.

2.6. Контрольные вопросы

37 5) Почему организацию OpenGL часто сравнивают с конеч- ным автоматом?
6) Зачем нужны различные варианты команд OpenGL, отли- чающиеся только типами параметров?
7) Что можно сказать о количестве и типе параметров коман- ды glColor4ub()
?
glVertex3fv()
?

Глава 3.
Рисование геометрических объектов
3.1. Процесс обновления изображения
Как правило, задачей программы, использующей OpenGL,
является обработка трехмерной сцены и интерактивное отобра- жение в буфере кадра. Сцена состоит из набора трехмерных объ- ектов, источников света и виртуальной камеры, определяющей текущее положение наблюдателя.
Обычно приложение OpenGL в бесконечном цикле вызывает функцию обновления изображения в окне. В этой функции и сосредоточены вызовы основных команд OpenGL. Если исполь- зуется библиотека GLUT, то это будет функция с обратным вы- зовом, зарегистрированная с помощью вызова glutDisplayFunc().
GLUT вызывает эту функцию, когда операционная система ин- формирует приложение о том, что содержимое окна необходимо перерисовать (например, если окно было перекрыто другим).
Создаваемое изображение может быть как статичным, так и анимированным, т.е. зависеть от каких-либо параметров, изме- няющихся со временем. В этом случае лучше вызывать функ-
39

40
Глава 3. Рисование геометрических объектов цию обновления самостоятельно. Например, с помощью коман- ды glutPostRedisplay()
. За более подробной информацией можно обратиться к главе 10.
Приступим, наконец, к тому, чем занимается типичная функ- ция обновления изображения. Как правило, она состоит из трех шагов:
€ очистка буферов OpenGL;
€ установка положения наблюдателя;
€ преобразование и рисование геометрических объектов.
Очистка буферов производится с помощью команды:
void g l C l e ar C ol or ( clampf r , clampf g , clampf b ,
clampf a )
void g l C l e a r ( b i t f i e l d buf )
Команда glClearColor устанавливает цвет, которым будет за- полнен буфер кадра. Первые три параметра команды задают
R,G и B компоненты цвета и должны принадлежать отрезку
[0, 1]
. Четвертый параметр задает так называемую альфа ком- поненту (см. п. 7.1). Как правило, он равен 1. По умолчанию цвет  черный (0,0,0,1).
Команда glClear очищает буферы, а параметр buf определя- ет комбинацию констант, соответствующую буферам, которые нужно очистить (см. главу 7). Типичная программа вызывает команду g l C l e a r (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
для очистки буферов цвета и глубины.
Установка положения наблюдателя и преобразования трех- мерных объектов (поворот, сдвиг и т.д.) контролируются с помо- щью задания матриц преобразования. Преобразования объектов и настройка положения виртуальной камеры описаны в главе 4.

3.2. Вершины и примитивы
41
Сейчас сосредоточимся на том, как передать в OpenGL опи- сания объектов, находящихся в сцене. Каждый объект является набором примитивов OpenGL.
3.2. Вершины и примитивы
В OpenGL вершина (vertex) является атомарным графиче- ским примитивом и определяет точку, конец отрезка, угол мно- гоугольника и т.д. Все остальные примитивы формируются с помощью задания вершин, входящих в данный примитив. На- пример, отрезок определяется двумя вершинами, являющимися концами отрезка.
С каждой вершиной ассоциируются ее атрибуты. В число основных атрибутов входят положение вершины в пространстве,
цвет вершины и вектор нормали.
3.2.1. Положение вершины в пространстве
Положение вершины определяются заданием ее координат в двух-, трех-, или четырехмерном пространстве (однородные ко- ординаты). Это реализуется с помощью нескольких вариантов команды glVertex
:
void glVertex [ 2 3 4 ] [ s i f d ] ( type coords )
void glVertex [ 2 3 4 ] [ s i f d ] v ( type * coords )
Каждая команда задает четыре координаты вершины: x, y,
z, w. Команда glVertex2*
получает значения x и y. Координата z в таком случае устанавливается по умолчанию равной 0, ко- ордината w  равной 1.
glVertex3*
получает координаты x, y, z и заносит в координату w значение 1.
glVertex4*
позволяет задать все четыре координаты.
Для ассоциации с вершинами цветов, нормалей и текстур- ных координат используются текущие значения соответствую- щих данных, что отвечает организации OpenGL как конечного

42
Глава 3. Рисование геометрических объектов автомата. Эти значения могут быть изменены в любой момент с помощью вызова соответствующих команд.
3.2.2. Цвет вершины
Для задания текущего цвета вершины используются коман- ды void glColor [ 3 4 ] [ b s i f ] ( GLtype components )
void glColor [ 3 4 ] [ b s i f ] v ( GLtype components )
Первые три параметра задают R, G, B компоненты цвета,
а последний параметр определяет коэффициент непрозрачности
(так называемая альфа-компонента). Если в названии команды указан тип ѕfї (oat), то значения всех параметров должны при- надлежать отрезку [0,1], при этом по умолчанию значение аль- фа-компоненты устанавливается равным 1.0, что соответствует полной непрозрачности. Тип ѕubї (unsigned byte) подразумевает,
что значения должны лежать в отрезке [0,255].
Вершинам можно назначать различные цвета, и, если вклю- чен соответствующий режим, то будет проводиться линейная ин- терполяция цветов по поверхности примитива.
Для управления режимом интерполяции используется ко- манда void glShadeModel (GLenum mode)
вызов которой с параметром
GL_SMOOTH
включает интерполя- цию (установка по умолчанию), а с
GL_FLAT
 отключает.
3.2.3. Нормаль
Определить нормаль в вершине можно, используя команды void glNormal3 [ b s i f d ] ( type coords )
void glNormal3 [ b s i f d ] v ( type coords )

3.3. Операторные скобки GLBEGIN / GLEND
43
Для правильного расчета освещения необходимо, чтобы век- тор нормали имел единичную длину. В OpenGL существует специальный режим, при котором задаваемые нормали будут нормироваться автоматически. Его можно включить командой glEnable(GL_NORMALIZE)
Режим автоматической нормализации должен быть включен,
если приложение использует модельные преобразования растя- жения/сжатия, так как в этом случае длина нормалей изменя- ется при умножении на модельно-видовую матрицу.
Однако применение этого режима уменьшает скорость ра- боты механизма визуализации OpenGL, так как нормализация векторов имеет заметную вычислительную сложность (взятие квадратного корня). Поэтому лучше сразу задавать единичные нормали.
Отметим, что команды void glEnable (GLenum mode)
void g l D i s a b l e (GLenum mode)
производят включение и отключение того или иного режима ра- боты конвейера OpenGL. Эти команды применяются достаточно часто, и их возможные параметры будут рассматриваться в каж- дом конкретном случае.
3.3. Операторные скобки glBegin / glEnd
Мы рассмотрели задание атрибутов одной вершины. Однако чтобы задать атрибуты графического примитива, одних коорди- нат вершин недостаточно. Эти вершины надо объединить в одно целое, определив необходимые свойства. Для этого в OpenGL
используются так называемые операторные скобки, являющи- еся вызовами специальных команд OpenGL. Определение при- митива или последовательности примитивов происходит между вызовами команд

44
Глава 3. Рисование геометрических объектов void glBegin (GLenum mode)
void glEnd ( void )
Параметр mode определяет тип примитива, который задается внутри и может принимать следующие значения:
GL_POINTS  каждая вершина задает координаты некото- рой точки.
GL_LINES  каждая отдельная пара вершин определяет от- резок; если задано нечетное число вершин, то последняя вершина игнорируется.
GL_LINE_STRIP  каждая следующая вершина задает от- резок вместе с предыдущей.
GL_LINE_LOOP  отличие от предыдущего примитива толь- ко в том, что последний отрезок определяется последней и первой вершиной, образуя замкнутую ломаную.
GL_TRIANGLES  каждые отдельные три вершины опреде- ляют треугольник; если задано не кратное трем число вер- шин, то последние вершины игнорируются.
GL_TRIANGLE_STRIP  каждая следующая вершина за- дает треугольник вместе с двумя предыдущими.
GL_TRIANGLE_FAN  треугольники задаются первой вер- шиной и каждой следующей парой вершин (пары не пере- секаются).
GL_QUADS  каждая отдельная четверка вершин определя- ет четырехугольник; если задано не кратное четырем число вершин, то последние вершины игнорируются.
GL_QUAD_STRIP  четырехугольник с номером n опреде- ляется вершинами с номерами 2n ? 1, 2n, 2n + 2, 2n + 1.
GL_POLYGON  последовательно задаются вершины вы- пуклого многоугольника.

3.3. Операторные скобки GLBEGIN / GLEND
45
Например, чтобы нарисовать треугольник с разными цветами в вершинах, достаточно написать:
GLfloat BlueCol [ 3 ] = { 0 , 0 , 1 } ;
glBegin (GL_TRIANGLES) ;
g l C o l o r 3 f ( 1 . 0 , 0 . 0 , 0 . 0 ) ; /* красный */
g l V e r t e x 3 f ( 0 . 0 , 0 . 0 , 0 . 0 ) ;
glColor3ub ( 0 , 2 5 5 , 0 ) ; /* зеленый */
g l V e r t e x 3 f ( 1 . 0 , 0 . 0 , 0 . 0 ) ;
g l C o l o r 3 f v ( BlueCol ) ; /* синий */
g l V e r t e x 3 f ( 1 . 0 , 1 . 0 , 0 . 0 ) ;
glEnd ( ) ;
Как правило, разные типы примитивов имеют различную скорость визуализации на разных платформах. Для увеличения производительности предпочтительнее использовать примити- вы, требующие меньшее количество информации для передачи на сервер, такие как
GL_TRIANGLE_STRIP
,
GL_QUAD_STRIP
,
GL_TRIAGLE_FAN
Кроме задания самих многоугольников, можно определить метод их отображения на экране. Однако сначала надо опреде- лить понятие лицевых и обратных граней.
Под гранью понимается одна из сторон многоугольника, и по умолчанию лицевой считается та сторона, вершины которой обходятся против часовой стрелки. Направление обхода вершин лицевых граней можно изменить вызовом команды void glFrontFace (GLenum mode)
со значением параметра mode равным
GL_CW
(clockwise), а вер- нуть значение по умолчанию можно, указав
GL_CCW
(counter- clockwise).
Чтобы изменить метод отображения многоугольника исполь- зуется команда void glPolygonMode (GLenum face , Glenum mode)

46
Глава 3. Рисование геометрических объектов
Параметр mode определяет как будут отображаться много- угольники, а параметр face устанавливает тип многоугольников,
к которым будет применяться эта команда и может принимать следующие значения:
GL_FRONT  для лицевых граней;
GL_BACK  для обратных граней;
GL_FRONT_AND_BACK  для всех граней.
Параметр mode может быть равен:
GL_POINT  отображение только вершин многоугольников;
GL_LINE  многоугольники будут представляться набором отрезков;
GL_FILL  многоугольники будут закрашиваться текущим цветом с учетом освещения, и этот режим установлен по умолчанию.
Также можно указывать какой тип граней отображать на экране. Для этого сначала надо установить соответствующий ре- жим вызовом команды glEnable (GL_CULL_FACE)
, а затем вы- брать тип отображаемых граней с помощью команды void glCullFace (GLenum mode)
Вызов с параметром
GL_FRONT
приводит к удалению из изображения всех лицевых граней, а с параметром
GL_BACK

обратных (установка по умолчанию).
Кроме рассмотренных стандартных примитивов в библиоте- ках GLU и GLUT описаны более сложные фигуры, такие как сфера, цилиндр, диск (в GLU) и сфера, куб, конус, тор, тетра- эдр, додекаэдр, икосаэдр, октаэдр и чайник (в GLUT). Автома- тическое наложение текстуры предусмотрено только для фигур из библиотеки GLU (создание текстур в OpenGL будет рассмат- риваться в главе 6).

3.4. Дисплейные списки
47
Например, чтобы нарисовать сферу или цилиндр, надо снача- ла создать объект специального типа
GLUquadricObj с помощью команды
GLUquadricObj* gluNewQuadric ( void ) ;
а затем вызвать соответствующую команду:
void gluSphere ( GLUquadricObj * qobj ,
GLdouble radius ,
GLint s l i c e s ,
GLint s t a c k s )
void gluCylinder ( GLUquadricObj * qobj ,
GLdouble baseRadius ,
GLdouble topRadius ,
GLdouble height ,
GLint s l i c e s ,
GLint s t a c k s )
где параметр slices задает количество разбиений вокруг оси z, а stacks
 вдоль оси z.
Более подробную информацию об этих и других командах построения примитивов можно найти в приложении А.
3.4. Дисплейные списки
Если мы несколько раз обращаемся к одной и той же группе команд, то их можно объединить в так называемый дисплей- ный список (display list) и вызывать его при необходимости. Для того, чтобы создать новый дисплейный список, надо поместить все команды, которые должны в него войти, между следующими операторными скобками:
void glNewList ( GLuint l i s t , GLenum mode)
void glEndList ( )

48
Глава 3. Рисование геометрических объектов
Для различения списков используются целые положитель- ные числа, задаваемые при создании списка значением парамет- ра list
. Параметр mode определяет режим обработки команд,
входящих в список:
GL_COMPILE  команды записываются в список без выпол- нения;
GL_COMPILE_AND_EXECUTE  команды выполняют- ся, а затем записываются в список.
После того, как список создан, его можно вызвать командой void g l C a l l L i s t ( GLuint l i s t )
указав в параметре list идентификатор нужного списка.
Чтобы вызвать сразу несколько списков, можно воспользо- ваться командой void g l C a l l L i s t s (
GLsizei n , GLenum type ,
const GLvoid * l i s t s )
вызывающей n списков с идентификаторами из массива lists,
тип элементов которого указывается в параметре type. Это мо- гут быть типы
GL_BYTE
,
GL_UNSIGNED_BYTE
,
GL_SHORT
,
GL_INT
,
GL_UNSIGNED_INT
и некоторые другие. Для удале- ния списков используется команда void g l D e l e t e L i s t s ( GLint l i s t , GLsizei range )
которая удаляет списки с идентификаторами ID из диапазона list ? ID ? list + range ? 1
Пример:
glNewList (1 , GL_COMPILE) ;
glBegin (GL_TRIANGLES) ;
g l V e r t e x 3 f ( 1 . 0 f , 1. 0 f , 1 . 0 f ) ;
g l V e r t e x 3 f ( 1 0 . 0 f , 1. 0 f , 1 . 0 f ) ;
g l V e r t e x 3 f ( 1 0 . 0 f , 10.0 f , 1 . 0 f ) ;

3.5. Массивы вершин
49
glEnd ( ) ;
glEndList ( )
g l C a l l L i s t ( 1 ) ;
Дисплейные списки в оптимальном (скомпилированном) ви- де хранятся в памяти сервера, что позволяет рисовать прими- тивы в такой форме максимально быстро. В то же время боль- шие объемы данных занимают много памяти, что влечет, в свою очередь, падение производительности. Такие большие объемы
(больше нескольких десятков тысяч примитивов) лучше рисо- вать с помощью массивов вершин.
3.5. Массивы вершин
Если вершин много, то, чтобы не вызывать для каждой ко- манду glVertex
, удобно объединять вершины в массивы, исполь- зуя команду void glVertexPointer ( GLint s i z e , GLenum type ,
GLsizei s t r i d e , void* ptr )
которая определяет способ хранения и координаты вершин.
При этом size определяет число координат вершины (может быть равен 2, 3, 4), type определяет тип данных (может быть равен
GL_SHORT
,
GL_INT
,
GL_FLOAT
,
GL_DOUBLE
). Иногда удобно хранить в одном массиве другие атрибуты вершины, тогда па- раметр stride задает смещение от координат одной вершины до координат следующей; если stride равен нулю, это значит, что координаты расположены последовательно. В параметре ptr ука- зывается адрес, где находятся данные.
Аналогично можно определить массив нормалей, цветов и некоторых других атрибутов вершины, используя команды void glNormalPointer ( GLenum type , GLsizei s t r i d e ,

50
Глава 3. Рисование геометрических объектов void
* p o i n t e r )
void g l C o l o r P o i n t e r ( GLint s i z e , GLenum type ,
GLsizei s t r i d e , void * p o i n t e r )
Для того, чтобы эти массивы можно было использовать в дальнейшем, надо вызвать команду void g l E n a b l e C l i e n t S t a t e (GLenum array )
с параметрами
GL_VERTEX_ARRAY
,
GL_NORMAL_ARRAY
,
GL_COLOR_ARRAY
соответственно.
После окончания работы с массивом желательно вызвать ко- манду void g l D i s a b l e C l i e n t S t a t e (GLenum array )
с соответствующим значением параметра array
Для отображения содержимого массивов используется ко- манда void glArrayElement ( GLint index )
которая передает OpenGL атрибуты вершины, используя эле- менты массива с номером index. Это аналогично последователь- ному применению команд вида glColor
,
glNormal
,
glVertex c соот- ветствующими параметрами. Однако вместо нее обычно вызы- вается команда void glDrawArrays (GLenum mode , GLint f i r s t ,
GLsizei count )
рисующая count примитивов, определяемых параметром mode
,
используя элементы из массивов с индексами от first до first +
count ? 1
. Это эквивалентно вызову последовательности команд glArrayElement()
с соответствующими индексами.
В случае, если одна вершина входит в несколько примитивов,
вместо дублирования ее координат в массиве удобно использо- вать ее индекс.
Для этого надо вызвать команду

3.6. Контрольные вопросы
51
void glDrawElements (GLenum mode , GLsizei count ,
GLenum type , void* i n d i c e s )
где indices
 это массив номеров вершин, которые на- до использовать для построения примитивов,
type опреде- ляет тип элементов этого массива:
GL_UNSIGNED_BYTE
,
GL_UNSIGNED_SHORT
,
GL_UNSIGNED_INT
, а count задает их количество.
Важно отметить, что использование массивов вершин поз- воляет оптимизировать передачу данных на сервер OpenGL, и,
как следствие, повысить скорость рисования трехмерной сцены.
Такой метод определения примитивов является одним из самых быстрых и хорошо подходит для визуализации больших объемов данных.
3.6. Контрольные вопросы
1) Что такое функция обратного вызова и как функции об- ратного вызова могут быть использованы для работы с

1   2   3   4   5   6   7   8   9


написать администратору сайта