Визуализация с использованием OpenGL
Картинка, отображаемая на экране монитора, представляет собой двумерный массив простейших элементов изображения (растр пикселов). Каждый из пикселов отображается на экране в соответствующем положении
определённым цветом заданным в системе RGB. Графические библиотеки операционных систем, которые используются в основном для формирования пользовательского интерфейса, предоставляют функции именно для
формирования растрового изображения (отображение двумерных графических примитивов, определения свойств карандаша и кисти, заливки цветом областей растра и т.п.) При визуализации, моделировании и проектировании
этих возможностей явно недостаточно. В-первую очередь, хотелось бы работать не с целочисленными индексами растра, а с моделью в трёхмерной системе координат. Во-вторых, требуются эффективные операции
для реалистического отображения сложных объектов с учетом свойств материалов и освещения. В-третьих, необходимы эффективные операции для организации интерактивного взаимодействия со сценой в целом и с
каждым объектом в отдельности. Такие возможности предоставляет нам библиотека OpenGL.
Модель формирования изображения в OpenGL
В библиотеке OpenGL формирование изображения осуществляется на основе объектно-ориентированной модели, в которую входят следующие независимые сущности:
- предметы, которые составляют моделируемое пространство (сцену);
- источник(и) освещения;
- наблюдатель.
Так же как и в реальном мире, предметы существуют и не зависят от того, с какой стороны на них смотрит наблюдатель и каким образом они освещены. Их положение определяется в глобальной системе координат,
а свойства (например, цвет, прозрачность, шероховатость) определяются дополнительными атрибутами. Аналогично определяется положение и свойства источников освещения, которые тоже входят в состав сцены,
но в отличие от предметов не отражают свет, а излучают его.
Роль наблюдателя может играть любой прибор способный формировать и регистрировать изображение (глаз, камера и т.п.) В библиотеке OpenGL и сама сцена, и её изображение является не реальным, в виртуальным.
В качестве наблюдателя используется модель камеры, которая отображает синтезированное изображение в некоторой области (порт вывода) на экране монитора. Угол зрения наблюдателя всегда ограничен, поэтому
изображение будет иметь конечные размеры, определённые отсекающей рамкой. Изменяя положение и другие характеристики наблюдателя можно формировать изображение любой части сцены в любом масштабе.
Конвейерная архитектура OpenGL
Какова бы ни была модель формирования изображения, реализованная в библиотеке, в конечном итоге всё равно отображается растровая картинка. Процесс формирования растрового изображения в библиотеке OpenGL
имеет конвейерную архитектуру, которая заключается в том, чтобы разбить требуемые действия на независимые операции, выполняемые одновременно в отдельном программном или аппаратном блоке. Именно эта архитектура
позволяет добиться высокой эффективности при решении сложных графических задач. Архитектура конвейера OpenGL представлена на рисунке:
В библиотеке OpenGL используется два типа данных:
- векторные данные (вершины, линии, многоугольники);
- растровые данные (пикселы, растровые изображения, битовые массивы).
Простейшим векторным примитивом библиотеки OpenGL является вершина. Это простейший векторный объект, который может быть отображён на экране (в виде точки). Все остальные векторные объекты должны быть
представлены в виде набора вершин. Простейшим растровым примитивом библиотеки OpenGL является пиксел. Это простейший растровый объект, который может быть отображен на экране. Все остальные растровые объекты
представляют собой массивы пикселов.
Прежде чем отобразиться на экране векторные и растровые данные проходят разные пути по конвейеру визуализации.
Evaluators (вычислители)
При отображении векторные геометрические примитивы должны быть представлены в виде (иногда достаточно большого) набора вершин. Однако многие полезные и часто используемые кривые и поверхности могут быть
описаны гораздо меньшим количеством параметров. Вычислители позволяют по простому описанию геометрического примитива сформировать набор вершин. При этом можно определять количество вершин и тем самым
управлять качеством и производительностью визуализации.
Результатом прохождения вычислителя является сцена представленная только в виде простейших геометрических примитивов - вершин.
Per-Vertex Operations and Primitive Assembly (обработка вершин и сборка примитивов)
На этом этапе над каждой вершиной выполняются операции необходимые для ее последующего отображения: выполняются преобразования системы координат, проецирование координат на двумерную поверхность экрана,
определение цвета вершины с учётом свойств материала и освещение, наложение текстур и т.п. Также в зависимости от режима осуществляется сборка примтивов в линии и многоугольники. На этом шаге выполняется
отсечение невидимых линий и вершин. Они могут просто не попадать в порт и глубину вывода, а могут закрываться другими примитивами.
Результатом этого этапа является полное описание геометрических примитивов и их свойств, размещённых в пространстве в соответствии с установленной системой координат, и готовое к этапу дискретизации.
Pixel Operations (операции над пикселами)
Пикселы из массивов, размещённых в системной памяти распаковываются, преобразуются из формата хранения в формат отображения. При необходимости они масштабируются или смещаются. Результат фиксируется
и также направляется на этап дискретизации или сохраняется в памяти как текстура.
Texture Assembly (сборка текстур)
Для создания реалистичных изображений в OpenGL используются текстуры, которые можно наложить на любой геометрический примитив, приведя в соответствие каждой из его вершин координаты текстуры. Текстура
представляет собой растровое изображение, размеры которого для повышени производительности должны быть кратны степени 2. В памяти может храниться несколько вариантов текстуры разного размера, которые
выбираются в зависимости от масштаба или степени детализации (mipmapping). Готовая текстура потоянно хранится в памяти и просто копируется непосредственно в буфер кадра.
Rasterization (дискретизация)
В процессе дискретизации из векторных и растровых данных создаются фрагменты. Каждый фрагмент соответствует пикселу буфера кадра. На этом этапе формируются заполненные цветом многоугольники, пунктирные
линии заданной ширины, точки заданного размера, применяется сглаживание (antialiasing).
Fragment Operations (операции над фрагментами)
Перед тем как полученые значения цвета каждого пиксела будут действительно размещены в буфере кадра каждый фрагмент может быть дополнительно обработан. Во-первых, именно на этом этапе присходит наложение
текстур из памяти. Затем выполняются операции вырезания, определения прозрачности, наложения шаблонов и масок, логические операции и наложение изображений, моделирование тумана. Только после этого обработанные
фрагменты помещаются в активный буфер кадра, пикселам которого присваиваются значения окончательно отображаемого цвета.
Framebuffer (буфер кадра)
Буфер кадра - это область памяти, которая представляет собой пиксельный массив отображаемый на экране. Библиотека OpenGLможет работать сразу с несколькими буферами кадров (пока один буфер отображается
другие уже формируются). Это позволяет добиться эффективного формирования и визуализации анимационных кадров.
Пиксельное изображение из буфера кадра может быть считано в системную память, обработано как обычный массив, упаковано, сохранено и снова использовано.
Display List (дисплейные списки)
Практически все данные, описывающие векторные или растровые примитивы могут быть сохранены в дисплейном списке и использованы повторно. Это существенно ускоряет процесс формирования сложных растровых
примитивов и экономит память.
Механизм состояний в OpenGL
Для определения атрибутов отображения, включения и выключения различных режимов библиотека OpenGL использует понятие состояния. Если установить какие-либо атрибуты и режимы, то они будут использоваться
до тех пор пока не будут отменены или изменены. Атрибуты изменяются специальнами командами (например установка цвета glColor), а управление режимами осущетсвляется с помощью команд glEnable и glDisable,
которые вызываются с различными аргументами. Каждый атрибут и режим имеет значение по умолчанию. Текущее значение атрибута моно определить с использованием команд типа glGet. Полезным инструментом при
создании сложных изображений является стек атрибутов, в котором можно сохранить значения атрибутов с использованием команды glPushAttrib(), после этого изменять атрибуты формировать примитивы, а затем
вернут состояние атрибутов обратно с помощью glPopAttrib().
Формирование векторных примитивов
Для формирования графических примитивов используются функции glBegin и glEnd, которые как открывающая и закрывающая скобки заключают в себя список вершин.
//Функция визуализации
void Draw(void)
{
//Очистка буфера кадра
glClear(GL_COLOR_BUFFER_BIT);
//Установка цвета отображения
glColor3d(1.0, 1.0, 1.0);
//Начать формирование примитива
glBegin(GL_LINES);
//Установка вершин
glVertex2d(.0, .0); // 0
glVertex2d(-1., -1.); // 1
glVertex2d(-1., .0); // 2
glVertex2d(-1., 1.); // 3
glVertex2d(.0, 1.); // 4
glVertex2d(1., 1.); // 5
glVertex2d(1., .0); // 6
glVertex2d(1., -1.); // 7
glVertex2d(.0, -1.); // 8
//Завершить формирование примитива
glEnd();
}
Вершины отображаются тем или иным способом в зависимости от константы заданной в качестве параметра функции glBegin:
GL_POINTS
|
Каждая вершина отображается отдельной точкой. Вершина n определяет точку n. N точек отображается.
|
|
GL_LINES
|
Каждая пара вершин образует отрезок линии. Вершины 2n – 1 и 2n определяют отрезок n. N/2 отрезков отображается. |
|
GL_LINE_STRIP
|
Отображает отрезки, связывающие последовательно все вершины. Вершины n и n+1 определяют отрезок n. N – 1 отрезков отображается. |
|
GL_LINE_LOOP
|
Отображает отрезки, связывающие последовательно все вершины + отрезок, связывающий последнюю вершину с первой. Вершины n и n+1 определяют отрезок n. Последний отрезок определяют вершины
N и 1. N отрезков отображается. |
|
GL_TRIANGLES
|
Каждая тройка вершин составляет независимый треугольник. Вершины 3n – 2, 3n –1, и 3n определяют треугольник n. N/3 треугольников отображается. |
|
GL_TRIANGLE_STRIP
|
Отображает связанную группу треугольников. Для не четной n, вершины n, n + 1, и n + 2 определяют треугольник n. Для четного n, вершины n + 1, n, и n + 2 определяют треугольник n. N
– 2 треугольников отображается. |
|
GL_TRIANGLE_FAN
|
Отображает связанную группу треугольников. Вершины 1, n + 1, и n + 2 определяют треугольник n. N – 2 треугольников отображается. |
|
GL_QUADS
|
Каждая группа из четырех вершин представляет собой независимый четырехугольник. Вершины 4n – 3, 4n – 2, 4n – 1, и 4n определяет четырехугольник n. N/4 четырехугольников отображается.
|
|
GL_QUAD_STRIP
|
Отображает связанную группу четырехугольников. Вершины 2n – 1, 2n, 2n + 2, и 2n + 1 определяют четырехугольник n. N четырехугольников отображается. |
|
GL_POLYGON
|
Отображает многоугольник. Вершины с 1 по N определяют многоугольник. |
|