Основные возможности OpenGL

OpenGL это графическая библиотека, которая содержит набор функций для работы с двумерной и трехмерной графикой. OpenGL является стандартной библиотекой в большинстве 32-х разрядных операционных системах. Она присутствует во всех версиях Windows. Это означает, что программы, использующие OpenGL, могут без больших усилий быть перенесены на разные платформы, что является одним из плюсов этой библиотеки.

OpenGL – универсальная, гибкая, популярная графическая библиотека. Она сочетает многочисленные современные достижения в области компьютерной графики с относительной легкостью изучения. OpenGL остается основным “конкурентом” для таких графических библиотек, как DirectX.

Библиотека OpenGL была разработана фирмой Silicon Graphics, Inc. (SGI) еще в эпоху структурного программирования. Поэтому ее применение в чем-то похоже на вызов функций Windows API. Методы объектно-ориентированного программирования в ней не применяются вовсе.

Итак, применение OpenGL сводится к описанию структур данных и вызову функций, которые обрабатывают эти данные.

В состав библиотеки входят три файла: opengl32.dll, glu32.dll, и glaux.dll. Первые два из этих файлов формируют основу библиотеки. Третий, glaux.dll, считается дополнительным. OpenGL может работать как на основе центрального процессора, так и с помощью аппаратного графического ускорителя, т.е. микросхем встроенных в плату видеоадаптера. Аппаратное ускорение для трехмерной графики осуществляется с помощью устанавливаемого клиентского драйвера (Installable Client Diver, ICD) и мини драйвера (Mini-Client Driver, MCD).

Здесь будут рассмотрены далеко не все возможности OpenGL. В основном темы курса посвящены вопросам настройки различных методов и параметров освещения и отражения света от поверхностей трехмерных объектов. От решения этой проблемы во многом зависит качество воспроизведения трехмерной сцены. Второй темой, на которой остановимся более подробно, будет наложение текстур на поверхности трехмерных объектов.

Вот некоторые из возможностей, которые включает в себя библиотека OpenGL:

· Рисование графических примитивов, таких как отрезки, треугольники, многоугольники, сферы, цилиндры на плоскости и в пространстве.

· Поддержка различных моделей источников света, таких как точечный, направленный и другие.

· Преобразования переноса, масштабирования и вращения трехмерных объектов.

· Изображение трехмерных объектов в параллельной и перспективной проекциях.

· Поддержка свойств материалов поверхностей.

· Сплайновые кривые и поверхности Безье и NURBS.

· Использование трафаретов.

· Эффект тумана.

· Смешение цветов и прозрачность.

· Создание тени и отражения.

· Текстурное наложение.

· Сечения трехмерных объектов.

· Вывод трехмерного текста на основе двумерных шрифтов Windows.

 

Также в OpenGL имеется возможность моделировать тени и отражение, однако для этого не предусмотрено встроенных средств, т.е. можно создать лишь имитацию тени и отражения.

Замечание. Библиотека OpenGL предназначена в первую очередь для создания интерактивных приложений компьютерной графики. Поэтому в ней не используется такой сложный метод как обратная трассировка лучей, который, несмотря на превосходные результаты при визуализации трехмерных сцен, все еще не может считаться достаточно быстродействующим.

Некоторые функции в OpenGL поддерживают специальную нотацию или форму записи, которая позволяет легче запоминать функции, которые выполняют схожие действия с входными параметрами разных типов.

Например, функция установки значения текущего цвета вершины glColor имеет 16 модификаций в зависимости от типа и количества входных параметров. В соответствии с [15] можно представить такие функции в общем виде:

rtype CommandName[1 2 3 4][b s i f d ub us ui][v] (atype arg)

Команда состоит из имени и трех символов, которые могут встречаться в различных комбинациях, хотя не все из них обязательно будут встречаться.

CommandName Имя команды, например, glColor
[1 2 3 4] Цифра, показывающая количество аргументов команды
[b s i f d ub us ui] Символы, определяющие тип аргумента
[v] Буква, показывающая что в качестве аргумента используется указатель на массив значений.

 

Рассмотрим типы данных которые используются в OpenGL.

Символ Тип OpenGL Соответствие в С
b GLbyte char
s GLshort short
i GLint int
f GLfloat float
d GLdouble double
ub GLubyte unsigned byte
us GLushort unsigned short
ui GLuint unsigned int

 

В качестве примера рассмотрим два вида вызова функции glColor:

glColor4f (0.8,0.5,0.4,1.0);

glColor3i (200,109,38);

 

Контекст воспроизведения

Рассмотрим процедуру FormCreate, которая выполняет подготовительные действия для работы с OpenGL. Главное, что здесь требуется – создание контекста воспроизведения OpenGL и установка его текущим. OpenGL недостаточно просто ссылки на окно Windows или контекст устройства. Контекст воспроизведения связывает окно с так называемым конвейером OpenGL с учетом установленного формата пикселов.

procedure TfrmGL.FormCreate(Sender: TObject);

var

pfd : TPixelFormatDescriptor;

nPixelFormat : integer;

begin

hdc := GetDC (Panel1.Handle);

FillChar(pfd,SizeOf(pfd),0);

pfd.nSize:= SizeOf(pfd);

pfd.dwFlags:=PFD_DOUBLEBUFFER;

nPixelFormat:= ChoosePixelFormat(hdc,@pfd);

if nPixelFormat = 0 then

begin

ShowMessage(‘Ошибка OpenGL’);

Halt;

end;

 

SetPixelFormat(hdc,nPixelFormat,@pfd);

RContext := wglCreateContext(hdc);

WglMakeCurrent(hrc,RContext);

 

glClearColor (0.0, 0.0, 0.0,1.0); // цвет фона

glEnable(GL_DEPTH_TEST);

SphereObj:=gluNewQuadric;

CylObj :=gluNewQuadric;

LightObj :=gluNewQuadric;

end;

Первый оператор в этой процедуре – получение оконного контекста устройства с помощью вызова функции GetDC. На основе полученного значения выбираем и устанавливаем формат пиксела. Формат пиксела – это структура на С или, как в данном случае, запись на ObjectPascal типа TPixelFormatDescriptor. В документации эта структура содержит следующие поля:

typedef struct tagPIXELFORMATDESCRIPTOR { // pfd

WORD nSize;//размер структуры данных

WORD nVersion;//версия структуры данных – всегда 1.

DWORD dwFlags;//битовые флаги, например: PFD_SUPPORT_GDI

BYTE iPixelType;//тип пиксела: PFD_TYPE_RGBA или //PFD_TYPE_COLORINDEX

BYTE cColorBits;//битов для описания цвета пиксела, кроме Alpha

BYTE cRedBits;//битов красной компоненты пиксела

BYTE cRedShift;

BYTE cGreenBits; //битов зеленой компоненты пиксела

BYTE cGreenShift;

BYTE cBlueBits; //битов синей компоненты пиксела

BYTE cBlueShift;

BYTE cAlphaBits;

BYTE cAlphaShift;

BYTE cAccumBits;

BYTE cAccumRedBits;

BYTE cAccumGreenBits;

BYTE cAccumBlueBits;

BYTE cAccumAlphaBits;

BYTE cDepthBits;//битов элемент глубины в z-буфере

BYTE cStencilBits;

BYTE cAuxBuffers;

BYTE iLayerType;//для этой версии всегда PFD_MAIN_PLANE

BYTE bReserved;

DWORD dwLayerMask;

DWORD dwVisibleMask;

DWORD dwDamageMask;

} PIXELFORMATDESCRIPTOR;

 

Для выбора требуемого формата пиксела необходимо заполнить наиболее важные поля записи и сделать запрос к OpenGL. Если система в состоянии работать с заданным форматом, то результат вызова функции ChoosePixelFormat(hdc,@pfd) будет ненулевым.

Для установки формата пиксела, который наиболее близок к тому, который установлен в операционной системе, достаточно обнулить все поля записи, и вызвать ChoosePixelFormat, предварительно указав в поле pfd.nSize размер самой записи в байтах. В качестве единственного требования указываем на необходимость двойной буферизации графического вывода: pfd.dwFlags:=PFD_DOUBLEBUFFER. При этом изображение будет строиться на невидимой области, а затем полностью выводиться на экран. Это гарантирует нам отсутствие мерцания изображения при смене кадров.

При успешном получении номера формата пикселов устанавливаем его:

SetPixelFormat(hdc,nPixelFormat,@pfd).

Далее, на основе контекста устройства и формата пикселов создаем контекст воспроизведения OpenGL:

RContext := wglCreateContext(hdc);

и устанавливаем его текущим:

wglMakeCurrent(hdc,RContext).

Для завершения работы с OpenGL требуется освободить текущий контекст устройства:

wglMakeCurrent(0, 0) ,

и затем удалить его:

wglDeleteContext(RContext).

Как видим, с помощью функции wglMakeCurrent можно не только устанавливать текущий контекст воспроизведения, но и освобождать его. Рассмотрим определение этой функции:

BOOL wglMakeCurrent(

HDC hdc, //Контекст устройства на который осуществляется вывод графики

HGLRC hglrc //Контекст воспроизведения OpenGL для текущего потока

);

Для освобождении контекста воспроизведения потока первый параметр игнорируется, а второй должен иметь нулевое значение. При успешном выполнении функция возвращает значение True.

Завершающие действия описаны в процедуре FormDestroy:

procedure TfrmGL.FormDestroy(Sender: TObject);

begin

wglMakeCurrent(0, 0);

wglDeleteContext(RContext);

ReleaseDC (Handle, RContext);

DeleteDC (hdc);

end;

Параметры визуализации

Для изучения моделей отражения и освещения в OpenGL рассмотрим фрагменты программы, которая предназначена для интерактивной и наглядной демонстрации работы этих моделей.

Процедура вывода на экран очередного кадра изображения является ключевой. Для того чтобы автоматическая перерисовка окна не стирала изображение кадра, все действия встроены в обработчик перерисовки формы FormPaint.

 

procedure TfrmGL.FormPaint(Sender: TObject);

var

ps : TPaintStruct;

begin

try

BeginPaint (Panel1.Handle, ps); //для устойчивой работы

//очистка буферов цвета и глубины

glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT );

SetUpCommons;

DrawScene;

glFlush;

SwapBuffers(hrc);

EndPaint (Panel1.Handle, ps);

except

end;

end;

 

Для большей надежности работы операторы заключены в конструкцию try..except, а также в программные скобки BeginPaint – EndPaint. Функция BeginPaint подготавливает окно для вывода графики.

Собственно обновление окна производится вызовом функций OpenGL:

· glFlush;

· SwapBuffers(hrc);

 

Функция glFlush заставляет сервер OpenGL закончить все команды по построении сцены OpenGL за конечное, как сказано в документации, время. Таким образом, мы форсируем построение очередного кадра полностью прежде чем он будет показан на экране.

Функция SwapBuffers выводит изображение очередного кадра с невидимой области построения на экран. В качестве входного параметра этой функции указывается идентификатор контекста устройства, типа HDC, который был предварительно получен для окна Windows, в которое осуществляется вывод графики. Функция возвращает значение true при успешном выполнении.

За построение очередного кадра ответственны три функции:

· glClear (GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT );

· SetUpCommons;

· DrawScene;

Функция glClear предназначена для очистки буферов OpenGL. Буферы OpenGL это двумерные массивы размером с окно вывода, которые содержат различные специфические параметры трехмерной сцены. Например, в нашем случае используется два буфера: буфер цвета и буфер глубины. Как можно догадаться, буфер цвета содержит информацию о цвете пикселов, а в буфере глубины содержится текущее значение “глубины” пикселов, т.е. значение координат точек по оси z, которые видимы наблюдателю. Битовая константа на входе функции glClear, которая ответственна за очистку буфера цвета, называется GL_COLOR_BUFFER_BIT, а буфера глубины - GL_DEPTH_BUFFER_BIT. В OpenGL используются и некоторые другие буферы, рассматривать их мы пока не будем.

Следующие две пользовательские функции интенсивно используют функции OpenGL. Функция SetUpCommons предназначена для установки необходимых параметров визуализации. Внутри функции DrawScene строятся трехмерные объекты и осуществляются их перемещения и другие необходимые трансформации с учетом предварительно установленных параметров визуализации.

Рассмотрим каким образом настраиваются параметры вида и проекции в функции SetUpCommons:

glMatrixMode(GL_PROJECTION);

glLoadIdentity;

 

Case RGProj.ItemIndex of

0: glOrtho (-LC,LC,-Panel1.height/Panel1.width*LC,

Panel1.height/Panel1.width*LC, 3, 1000);

 

1: glFrustum (-1,1,-Panel1.height/Panel1.width,

Panel1.height/Panel1.width, 3, 1000);

end;//case

 

procedure TfrmGL.InitViewPort;

begin

glViewport(0, 0, Panel1.Width, Panel1.Height);

glMatrixMode(GL_PROJECTION);

glLoadIdentity;

glFrustum (-1,1,-Panel1.height/Panel1.width,

Panel1.height/Panel1.width, 3, 1000);

end;

 

Функция glViewport устанавливает так называемый порт вывода. Порт вывода это прямоугольная область в пределах окна Windows, в которое осуществляется отображение графики. Иногда порт вывода так и называют в английской транскрипции - “вьюпорт”:

void glViewport(

GLint x,

GLint y,

GLsizei width,

GLsizei height

);

Параметры x и y определяют левый нижний угол порта вывода в пикселах. Если порт вывода явно не указан, то (x,y)=(0,0). Параметры width и height задают ширину и высоту порта вывода, соответственно. При первом подключении контекста воспроизведения размеры порта вывода совпадают с размерами окна Windows к которому подключен контекст воспроизведения.

 

Команда glMatrixMode(GL_PROJECTION) устанавливает матрицу проекций текущей. В OpenGL используется общеизвестная модель матричных преобразований трехмерной графики, которая включает в себя матрицу видового преобразования, и матрицу проекции. Матрица видового преобразования задает расположение, перемещения, и масштабирование объектов в пространстве, а также расположение объектов по отношению к наблюдателю. Матрица проекции определяет параметры проецирования на экран. В OpenGL можно установить любую матрицу перспективного преобразования непосредственно. Но для параллельной и центральной перспективной проекций можно воспользоваться специализированными функциями.

После установки текущей матрицы она содержит в себе значения, которые в общем случае могут быть не известны. Если применять преобразования, связанные с этой матрицей, то текущие значения будут использоваться, что может привести к непредсказуемым результатам на экране. Для того чтобы этого не произошло, текущую матрицу следует инициализировать как единичную с помощью функции glLoadIdentity, либо запомнить ее в стеке для последующего восстановления.

Из двух типов проекций рассмотрим в начале центральную перспективную проекцию. Она устанавливается командой glFrustum:

 

void glFrustum(

GLdouble left,
GLdouble right,
Gldouble bottom,
GLdouble top,
GLdouble near,
GLdouble far
);

 

Параметры этой функции определяют координаты расположения граней усеченной пирамиды видимого объема: left и right – для левой и правой граней относительно ближней near грани. Нижняя и верхняя грани соответствуют параметрам bottom и top, а дальняя отсекающая плоскость определяется параметром far. Обратите внимание, что в приведенном примере параметры левой и правой стороны усеченной пирамиды кажутся выбранными слишком малыми по сравнению с параметрами верхней и нижней сторон. Однако это вполне допустимо, поскольку OpenGL самостоятельно подстраивает пропорции видимого объема под параметры окна отображения Windows.

В библиотеке glu32.dll также находится функция установки перспективной проекции gluPerspective, однако ее мы рассматривать не будем.