Модуль 1 «Основы компьютерной геометрии» Лекция 6 «Геометрические примитивы OpenGL» к.ф.-м.н., доц. каф. ФН-11, Захаров Андрей Алексеевич, ауд.:930а(УЛК) моб.: 8-910-461-70-04, email: azaharov@bmstu.ru МГТУ им. Н.Э. Баумана 12 декабря 2014 г. Точки Чтобы задать координаты единственной точки, используется следующая функция OpenGL: glVertex*(); Здесь звёздочка (*) означает, что для данной функции необходимы индексные коды. Индексные коды обозначают размерность пространства, тип числовых данных, которые используются в качестве значений координат, и возможность представления координат в виде вектора. Функция glVertex должна находиться в программе между функциями glBegin и glEnd. Аргумент функции glBegin определяет тип графического примитива, который следует изобразить, а функция glEnd не требует аргументов. При выводе на экран точки аргументом функции glBegin является символьная константа GL_POINTS. Т.о., положение точки в OpenGL описывается так: glBegin(GL_POINTS); glVertex*(); glEnd(); Координаты точек в OpenGL могут задаваться в 2, 3 или 4-х измерениях. Четырёхмерное описание указывает на представление с помощью однородных координат, где однородный параметр h (четвертая координата) — это масштабный коэффициент для значений декартовых координат. Точки Далее нужно обозначить какой тип данных используется для описания числовых значений координат. Это осуществляется с помощью второго индексного кода функции glVertex. Типы числовых данных обозначаются следующими индексами: i(int), s(short), f(float), d(double). Наконец, значения координат glVertex могут перечисляться в явном виде, или для них может использоваться массив. Если применяется описание координат в виде массива, нам нужно прибавить третий индекс: v(vector). Например: glBegin(GL_POINTS); glVertex2i(50,100); glEnd(); Аналогичный результат можно получить при помощи задания координат точки в виде массива: int point1[] = {50,100}; glBegin(GL_POINTS); glVertex2iv(point1); glEnd(); Прямые линии (отрезки) Каждый отрезок прямой определяется координатами двух его концов. В пакете OpenGL координаты концов выбираются с помощью функции glVertex, точно также как это делалось для координат точки, т.е. между glBegin/glEnd. По умолчанию рисуются сплошные линии белого цвета. Набор прямолинейных отрезков, соединяющих каждую последующую пару точек-концов из перечня, задаётся с помощью константы примитива линии — GL_LINES. В общем случае это даст набор несоединенных между собой линий, если только некоторые значения констант не повторяются. Если задать только одну точку, то ничего изображаться не будет, а если число точек (концов отрезков) в списке будет нечётным, то последнее значение отбрасывается. glBegin(GL_LINES); glVertex2iv(p1); glVertex2iv(p2); glVertex2iv(p3); glVertex2iv(p4); glVertex2iv(p5); glEnd(); Прямые линии (отрезки) С помощью константы OpenGL GL_LINE_STRIP можно построить ломанную линию. В этом случае будет изображена последовательность соединённых между собой отрезков, которая начинается в первой точке перечня и заканчивается в последней. Один из способов создания изображения простой кривой — аппроксимировать её с помощью ломанной линии. Нужно всего лишь задать набор точек, расположенных на этой кривой, а затем соединить эти точки прямолинейными отрезками. Чем из большего числа отрезков будет состоять ломанная линия, тем более гладкой будет выглядеть кривая. Третий примитив прямой линии в OpenGL — это GL_LINE_LOOP, который даёт замкнутую ломанную линию. Примитивы символов Графические изображения часто содержат текстовую информацию, как подписи на графиках и таблицах, вывески на зданиях или надписи на машинах, а также общая информация в приложениях, связанных с моделированием и визуализацией. Растровый символ GLUT можно получить с помощью функции glutBitmapCharacter(font, character); Здесь параметру font присваивается значение символьной константы GLUT, которая указывает на определённый набор начертаний, а параметру character присваивается значение символа, который мы хотим изобразить, например ’A’. Возможны шрифты как с постоянной шириной символов, так и с пропорциональными промежутками. Моноширинный шрифт можно выбрать, присвоив параметру font значение GLUT_BITMAP_8_BY_13 или GLUT_BITMAP_9_BY_15 (цифровые параметры определяют расстояние и размер символов). А пропорциональный шрифт размером 10pt можно выбрать с помощью команд GLUT_BITMAP_TIMES_NEW_ROMAN_10 или GLUT_BITMAP_HELVETICA_10. Возможен шрифт Times Roman размером 12pt, а также шрифты Helvetica размером 12и 18pt. Символы изображаются в том цвете, который был задан до выполнения функции glutBitmapCharacter. Примитивы символов Каждый символ, созданный с помощью функции glutBitmapCharacter, изображается так, что начало координат (нижний левый угол) битового массива находится в текущем растровом положении. После того как битовый массив символа загружается в буфер кадра, к координате x текущего растрового поля добавляется смещение, равное ширине символа. В качестве примера изобразим текстовую строку, состоящую из 36 растровых символов: glRasterPos2i(x,y); for (k=0; k<36; k++) glutBitmapCharacter(GLUT_BITMAP_9_BY_15, text[k]); Чтобы задать координаты текущего растрового положения, применяется следующая процедура: glRasterPos* ( ); Параметры и индексы аналогичны тем, которые используются в функции glVertex. Текущее растровое положение задаётся во внешних координатах. По умолчанию значение текущего растрового положения совпадает с началом отсчёта (0,0,0) внешней системы координат. Примитивы символов Векторный символ изображается путём вызова следующей функции: glutStrokeCharacter(font, character); Для этой функции параметру font можно присвоить значение GLUT_STROKE_ROMAN, чтобы изобразить пропорциональный шрифт, или GLUT_STROKE_MONO_ROMAN, чтобы представить моноширинный шрифт. Текстовые строки, полученные с помощью векторных шрифтов, являются частью геометрического описания двух- или трёхмерных сцен, поскольку они состоят из геометрических линий. Ширина линии задаётся с помощью функции glLineWidth, а тип линии — с помощью функции glLineStipple. Т.е. их можно без каких-либо искажений сжимать, растягивать или преобразовывать каким-то другим способом. Однако они визуализируются медленнее, чем растровые шрифты. Функции многоугольников в OpenGL За одним исключением, все процедуры OpenGL описания многоугольников похожи на функции описания точек или ломанной линии. Функция glVertex используется для ввода координат одной вершины многоугольника, а весь многоугольник описывается с помощью списка вершин, расположенного между парой команд glBegin/glEnd. По умолчанию внутренняя часть многоугольника изображается одним цветом, который определяется по текущим установкам цвета. В OpenGL заполненная область должна задаваться в виде выпуклого простого многоугольника. Закрашенная область в виде одного многоугольника может задаваться с помощью одного только списка вершин, который должен содержать по меньшей мере три вершины. Конфигурации, содержащие отверстия во внутренней области многоугольника невозможно задать с помощью одного примитива (такие конфигурации можно описать с помощью двух наложенных друг на друга выпуклых многоугольников). Каждый многоугольник имеет две стороны: переднюю и заднюю. В OpenGL цвет заполнения и другие параметры можно задавать для каждой стороны отдельно. Передняя сторона многоугольника определяется вершинами, заданными в направлении против часовой стрелки. Существует шесть различных символьных констант, которые используются в качестве аргумента функции glBegin для описания многоугольников. Функции многоугольников в OpenGL С помощью константы OpenGL GL_POLYGON можно получить изображение одного закрашенного многоугольника. Предположим, что задан массив координат (x, y) шести точек: p1 , . . . p6 – вершин двухмерного многоугольника, заданных в направлении против часовой стрелки. Тогда код, формирующий данный многоугольник, имеет вид: glBegin(GL_POLYGON); glVertex2iv(p1); glVertex2iv(p2); glVertex2iv(p3); glVertex2iv(p4); glVertex2iv(p5); glVertex2iv(p6); glEnd(); В списке вершин многоугольника должно быть как минимум три вершины. В противном случае ничего изображено не будет. Функции многоугольников в OpenGL Если поменять порядок точек в списке вершин из предыдущего кода и воспользоваться другой константой примитива GL_TRIANGLES, то получится два отдельных закрашенных треугольника. glBegin(GL_TRIANGLES); glVertex2iv(p1); glVertex2iv(p2); glVertex2iv(p6); glVertex2iv(p3); glVertex2iv(p4); glVertex2iv(p5); glEnd(); В этом случае первые три точки обозначают вершины одного треугольника, следующие три точки задают следующий треугольник, и т.д. Для каждого закрашенного треугольника вершины задаются в направлении против часовой стрелки. Если количество заданных вершин не будет кратно трем, то последние две или одна точка просто не будут учитываться. Функции многоугольников в OpenGL Путём ещё одной перестановки вершин в списке и замены константы примитива на GL_TRIANGLE_STRIP (полоса треугольников) можно получить изображение соединённых треугольников. glBegin(GL_TRIANGLE_STRIP); glVertex2iv(p1); glVertex2iv(p2); glVertex2iv(p6); glVertex2iv(p3); glVertex2iv(p5); glVertex2iv(p4); glEnd(); У всех последовательных треугольников есть одна общая сторона с предыдущим треугольником. Первые три вершины должны указываться в направлении против часовой стрелки, если смотреть на переднюю (внешнюю) поверхность треугольника. После этого каждый последующий треугольник формируется в направлении против часовой стрелки. Функции многоугольников в OpenGL Ещё один способ построения набора соединённых треугольников — воспользоваться методом «веера», где все треугольники имеют одну общую вершину. Такое расположение треугольников можно получить с помощью константы примитива GL_TRIANGLE_FAN и такого порядка шести вершин: glBegin(GL_TRIANGLE_FAN); glVertex2iv(p1); glVertex2iv(p2); glVertex2iv(p3); glVertex2iv(p4); glVertex2iv(p5); glVertex2iv(p6); glEnd(); Вершины должны быть заданы в соответствующем порядке, чтобы можно было правильно определить переднюю и заднюю стороны каждого треугольника. Первое значение в списке (в данном случае, p1 ) — это вершина всех треугольников веера. Функции многоугольников в OpenGL OpenGL также предлагает средства описания двух типов четырёхугольников (многоугольников с четырьмя сторонами). С помощью константы примитива GL_QUADS и следующего списка из восьми вершин получится два отдельных закрашенных четырёхугольника: glBegin(GL_QUADS); glVertex2iv(p1); glVertex2iv(p2); glVertex2iv(p3); glVertex2iv(p4); glVertex2iv(p5); glVertex2iv(p6); glVertex2iv(p7); glVertex2iv(p8); glEnd(); Первые четыре точки определяют вершины одного четырёхугольника, следующие четыре точки описывают следующий четырёхугольник и т.д. Для каждого закрашенного четырёхугольника вершины задаются против часовой стрелки. Если число заданных вершин не будет кратно четырём, то лишние значения просто не будут приниматься во внимание. Функции многоугольников в OpenGL Переставив вершины в списке из предыдущего примера и заменив константу примитива на GL_QUAD_STRIP (полоса четырёхугольников), можно получить набор соединённых четырёхугольников: glBegin(GL_QUAD_STRIP); glVertex2iv(p1); glVertex2iv(p2); glVertex2iv(p4); glVertex2iv(p3); glVertex2iv(p5); glVertex2iv(p6); glVertex2iv(p8); glVertex2iv(p7); glEnd(); Вершины нужно перечислять таким образом, чтобы каждый многоугольник имел правильный порядок вершин (против часовой стрелки). Функции многоугольников в OpenGL Поскольку графические изображения очень часто содержат закрашенные прямоугольные области, пакет OpenGL предлагает специальную функцию изображения прямоугольника: glRect* (x1, y1, x2, y2); Один угол этого прямоугольника находится в точке с координатами (x1 , y1 ), а противоположный угол прямоугольника — в точке с координатами (x2 , y2 ). Суффиксы функции glRect обозначают тип данных и то, выражаются ли координаты в виде элементов массива. Эти индексы — i, s, f, d и v. Прямоугольник изображается таким образом. что его стороны параллельны координатным осям плоскости xy. Например: glRecti (50, 100, 200, 250); //задание в виде массива данных int vertex1 [] = {50, 100}; int vertex2 [] = {200, 250}; glRectiv (vertex1, vertex2); 250 200 150 100 50 50 100 150 200 В этом примере список вершин формируется по часовой стрелке. Во многих двумерных приложениях различие между передней и задней стороной многоугольника несущественно. Если требуется приписать передним и задним сторонам различные свойства, то нужно изменить порядок следования двух вершин. Функции многоугольников в OpenGL Во многих графических пакетах криволинейные поверхности изображаются с помощью аппроксимирующих плоских граней. Это объясняется тем, что уравнения плоскости линейны, а обработка линейных уравнений выполняется намного быстрее, чем обработка квадратных уравнений или уравнений других типов кривых. Поэтому OpenGL и другие пакеты предлагают примитивы сеток многоугольников, которыми можно аппроксимировать криволинейные поверхности. В пакете OpenGL для этой цели можно использовать такие примитивы, как полоса треугольников, веер треугольников или полоса четырёхугольников. Высококачественные графические системы, способные изображать миллион или больше закрашенных многоугольников (как правило, треугольников) в секунду, включая приложения, связанные с наложением текстур на поверхности и специальных эффектов освещения, имеют встроенные аппаратные средства визуализации многоугольников. Несмотря на то что корневая библиотека OpenGL принимает только выпуклые многоугольники, библиотека OpenGL Utility (GLU) предлагает функции, позволяющие работать с невыпуклыми многоугольниками. Существует набор стандартных процедур библиотеки GLU для мозаичного представления вогнутых многоугольников, которые позволяют преобразовать такие фигуры в набор треугольников и треугольные сетки. Затем их можно обрабатывать с помощью основных функций OpenGL.