Вершины и примитивы
Вершины и
примитивы
Определение атрибутов вершины
Под вершиной понимается точка в трехмерном пространстве, координаты которой
можно задавать следующим образом:
- 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, при этом
можно указывать два (x,y) или три (x,y,z) значения, а для остальных переменных в
этих случаях используются значения по умолчанию: z=0, w=1. Как уже было сказано
выше, число в названии команды соответствует числу явно задаваемых значений, а
последующий символ – их типу.
Координатные оси расположены так, что точка (0,0) находится в левом нижнем
углу экрана, ось x направлена влево, ось y- вверх, а ось z- из экрана. Это
расположение осей мировой системы координат, в которой задаются координаты
вершин объекта, другие системы координат будут рассмотрены ниже.
Однако чтобы задать какую-нибудь фигуру одних координат вершин недостаточно,
и эти вершины надо объединить в одно целое, определив необходимые свойства. Для
этого в OpenGL используется понятие примитивов, к которым относятся точки,
линии, связанные или замкнутые линии, треугольники и так далее. Задание
примитива происходит внутри командных скобок:
- 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 последовательно задаются вершины выпуклого многоугольника.
Для задания текущего цвета вершины используются команды
- void glColor[3 4][b s i f](GLtype components)
- void glColor[3 4][b s i f]v(GLtype components)
Первые три параметра задают R, G, B компоненты цвета, а последний параметр
определяет alpha-компоненту, которая задает уровень прозрачности объекта. Если в
названии команды указан тип ‘f’ (float), то значения всех параметров должны
принадлежать отрезку [0,1], при этом по умолчанию значение alpha-компоненты
устанавливается равным 1.0, что соответствует полной непрозрачности. Если указан
тип ‘ub’ (unsigned byte), то значения должны лежать в отрезке [0,255].
Разным вершинам можно назначать различные цвета и тогда будет проводиться
линейная интерполяция цветов по поверхности примитива.
Для управления режимом интерполяции цветов используется команда void
glShadeModel(GLenummode) вызов которой с параметром GL_SMOOTH включает
интерполяцию (установка по умолчанию), а с GL_FLAT отключает.
Например, чтобы нарисовать треугольник с разными цветами в вершинах,
достаточно написать:
GLfloat BlueCol[3]={0,0,1};
glBegin(GL_TRIANGLE);
glColor3f(1.0, 0.0, 0.0); //красный
glVertex3f(0.0, 0.0, 0.0);
glColor3ub(0,255,0); //зеленый
glVertex3f(1.0, 0.0, 0.0);
glColor3fv(BlueCol); //синий
glVertex3f(1.0, 1.0, 0.0);
glEnd();
Для задания цвета фона используется команда void glClearColor(GLclampf
red, GLclampf green, GLclampf blue, GLclampf alpha). Значения должны находиться
в отрезке [0,1] и по умолчанию равны нулю. После этого вызов команды void
glClear(GLbitfield mask) с параметром GL_COLOR_BUFFER_BIT устанавливает
цвет фона во все буфера, доступные для записи цвета (иногда удобно использовать
несколько буферов цвета).
Кроме цвета аналогичным образом можно определить нормаль в вершине, используя
команды
- void glNormal3[b s i f d](type coords)
- void glNormal3[b s i f d]v(type coords)
Задаваемый вектор может не иметь единичной длины, но он будет нормироваться
автоматически в режиме нормализации, который включается вызовом команды
glEnable(GL_NORMALIZE).Команды
- void glEnable(GLenum mode)
- void glDisable(GLenum mode)
производят включение и отключение того или иного режима работы конвейера
OpenGL. Эти команды применяются достаточно часто, и их влияние будет
рассматриваться в конкретных случаях.
Вообще, внутри командных скобок glBegin() и glEnd() можно производить вызов
лишь нескольких команд, в которые входят glVertex..(), glColor..()glNormal..(),
glRect..(), glMaterial..() и glTexCoord..().
Последние две команды будут рассматриваться ниже, а с помощью команды void
glRect[s i f d]( GLtype x1, GLtype y1, GLtype x2, GLtype y2 ), void
glRect[s i f d]v( GLtype *v1, GLtype *v2 ) можно нарисовать прямоугольник
в плоскости z=0 с координатами противоположных углов (x1,y1) и (x2,y2), либо
набор прямоугольников с координатами углов в массивах v1 и v2.
Кроме задания самих примитивов можно определить метод их отображения на
экране, где под примитивами в данном случае понимаются многоугольники.
Однако сначала надо определить понятие лицевых и обратных граней.
Под гранью понимается одна из сторон многоугольника, и по умолчанию лицевой
считается та сторона, вершины которой обходятся против часовой стрелки.
Направление обхода вершин лицевых сторон можно изменить вызовом команды void
glFrontFace(GLenum mode) со значением параметра mode равным GL_CW, а
отменить- с GL_CCW.
Чтобы изменить метод отображения многоугольника используется команда void
glPolygonMode(GLenum face, Glenum mode)
Параметр mode определяет, как будут отображаться многоугольники, а параметр
face устанавливает тип многоугольников, к которым будет применяться эта команда
и может принимать следующие значения:
- GL_FRONT для лицевых граней
- GL_BACK для обратных граней
- GL_FRONT_AND_BACK для всех граней
Параметр mode может быть равен:
- GL_POINT при таком режиме будут отображаться только вершины
многоугольников.
- GL_LINE при таком режиме многоугольник будет представляться набором
отрезков.
- GL_FILL при таком режиме многоугольники будут закрашиваться текущим
цветом с учетом освещения и этот режим установлен по умолчанию.
Кроме того, можно указывать, какой тип граней отображать на экране. Для этого
сначала надо установить соответствующий режим вызовом команды
glEnable(GL_CULL_FACE), а затем выбрать тип отображаемых граней с помощью
команды void glСullFace(GLenum mode)
Вызов с параметром GL_FRONT приводит к удалению из изображения всех лицевых
граней, а с параметром GL_BACK- обратных (установка по умолчанию).
Кроме рассмотренных стандартных примитивов в библиотеках GLU и GLUT описаны
более сложные фигуры, такие как сфера, цилиндр, диск (в GLU) и сфера, куб,
конус, тор, тетраэдр, додекаэдр, икосаэдр, октаэдр и чайник(в GLUT).
Автоматическое наложение текстуры предусмотрено только для фигур из библиотеки
GLU (создание текстур в OpenGL будет рассматриваться ниже).
Например, чтобы нарисовать сферу или цилиндр, надо сначала создать объект
специального типа GLUquadricObj с помощью команды
GLUquadricObj* gluNewQuadric(void)
а затем вызвать соответствующую команду:
- void gluSphere(GLUquadricObj * qobj, GLdouble radius, GLint slices,
GLint stacks)
- void gluCylinder(GLUquadricObj * qobj, GLdouble baseRadius, GLdouble
topRadius, GLdouble height, GLint slices, GLint stacks)
где параметр slices задает число разбиений вокруг оси z, а stacks – вдоль оси
z.
Более подробную информацию об этих и других командах построения примитивов
можно найти приложении.
Важно отметить, что для корректного построения перечисленных примитивов
необходимо удалять невидимые линии и поверхности, для чего надо включить
соответствующий режим вызовом команды glEnable(GL_DEPTH_TEST).
Массивы вершин
Если вершин много, то чтобы не вызывать для каждой команду glVertex..(),
удобно объединять вершины в массивы, используя команду
void glVertexPointer( GLint size, GLenum type,
GLsizei stride, void *ptr )
которая определяет способ хранения и координаты вершин. При этом size
определяет число координат вершины (может быть равен 2, 3, 4), type определяет
тип данных (может быть равен GL_SHORT, GL_INT, GL_FLOAT, GL_DOUBLE). Иногда
удобно хранить в одном массиве другие атрибуты вершины, и тогда параметр stride
задает смещение от координат одной вершины до координат следующей; если stride
равен нулю, это значит, что координаты расположены последовательно. В параметре
ptr указывается адрес, где находятся данные.
Аналогично можно определить массив нормалей, цветов и некоторых других
атрибутов вершины, используя команды
- void NormalPointer(GLenum type, GLsizei stride, void*pointer)
- void ColorPointer(GLintsize, GLenum type, GLsizei stride, void
*pointer)
Для того, чтобы эти массивы можно было использовать в дальнейшем, надо
вызвать команду
void glEnableClientState(GLenum array)
с параметрами GL_VERTEX_ARRAY, GL_NORMAL_ARRAY, GL_COLOR_ARRAY
соответственно. После окончания работы с массивом желательно вызвать команду
void glDisableClientState(GLenum array)
с соответствующим значением параметра array.
Для отображения содержимого массивов используется команда
void glArrayElement(GLint index)
которая передает OpenGL атрибуты вершины, используя элементы массива с
номером index. Это аналогично последовательному применению команд вида
glColor..(…), glNormal..(…), glVertex..(…) c соответствующими параметрами.
Однако вместо нее обычно вызывается команда
void glDrawArrays(GLenum mode,
GLint first, GLsizei count)
рисующая count примитивов, определяемых параметром mode, используя элементы
из массивов с индексами от first до first+count-1. Это эквивалентно вызову
команды glArrayElement() с соответствующими индексами.
В случае если одна вершина входит в несколько примитивов, то вместо
дублирования ее координат в массиве удобно использовать ее индекс.
Для этого надо вызвать команду
void glDrawArrays(GLenum mode, GLsizei count,
GLenum type, void *indices)
где indices– это массив номеров вершин, которые надо использовать для
построения примитивов, type определяет тип элементов этого массива:
GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT, а count задает их
количество.
Списки изображений
Если нужно несколько раз обращаться к одной и той же группе команд,эти
команды можно объединить в так называемый список изображений (display list) и
вызывать его при необходимости. Для того, чтобы создать новый список изображений
надо поместить все команды, которые должны в него войти между командными
скобками:
- void glNewList(GLuint list, GLenum mode)
- void glEndList()
Для различения списков используются целые положительные числа, задаваемые при
создании списка значением параметра list, а параметр mode определяет режим
обработки команд, входящих в список:
- GL_COMPILE команды записываются в список без выполнения
- GL_COMPILE_AND_EXECUTE команды сначала выполняются, а затем
записываются в список
После того, как список создан, его можно вызвать командой
void glCallList(GLuint list)
указав в параметре list идентификатор нужного списка. Чтобы вызвать сразу
несколько списков, можно воспользоваться командой
void glCallLists(GLsizei n,
GLenum type, const GLvoid *lists)
вызывающей n списков с идентификаторами из массива lists, тип элементов
которого указывается в параметре type. Это могут быть типы GL_BYTE,
GL_UNSIGNED_BYTE, GL_SHORT, GL_INT, GL_UNSIGNED_INT >и некоторые другие. Для
удаления списков используется команда
void glDeleteLists(GLint list, GLsizei range)
которая удаляет списки с идентификаторами ID из диапазона list <=ID<=
list+range-1.