Отчет по OpenGL

Оглавление





Структура и применение OpenGl 

Основные библиотеки, используемые при создании приложений 

Массивы вершин и списки изображений (назначение и команды для их описания)

Преобразование проецирования, задание ортографической и перспективной проекции

Задание и исходный код

Рисунок

Использованные источники


Структура и применение OpenGL



OpenGL (Open Graphics Library — открытая графическая библиотека) — спецификация, определяющая независимый от языка программирования кросс-платформенный программный интерфейс для написания приложений, использующих двумерную и трехмерную компьютерную графику (это стандарт API).

Включает более 250-ти функций для рисования сложных трехмерных сцен из простых примитивов. Используется при создании видео-игр, САПР, виртуальной реальности, визуализации в научных исследованиях. На платформе Windows конкурирует с DirectX. 

OpenGL ориентируется на следующие две задачи:

		скрыть сложности адаптации различных 3D-ускорителей предоставляя разработчику единый API

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

Основным принципом работы OpenGL является получение наборов векторных графических примитивов в виде точек, линий и многоугольников с последующей математической обработкой полученных данных и построением растровой картинки на экране и/или в памяти. Векторные трансформации и растеризация выполняются графическим конвейером (graphics pipeline), который, по сути, представляет собой дискретный автомат. Абсолютное большинство команд OpenGL попадают в одну из двух групп: либо они добавляют графические примитивы на вход в конвейер, либо конфигурируют конвейер на различное исполнение транформаций.

OpenGL является низкоуровневым процедурным API, что вынуждает программиста диктовать точную последовательность шагов, чтобы построить результирующую растровую графику (императивный подход). Это является основным отличием от дескрипторных подходов, когда вся сцена передается в виде структуры данных (чаще всего дерева), которое обрабатывается и строится на экране. С одной стороны императивный подход требует от программиста глубокого знания законов трёхмерной графики и математических моделей, с другой стороны — даёт свободу внедрения различных инноваций. Как уже было сказано, существует реализация  OpenGL для разных платформ, для чего было удобно разделить базовые функции графической системы и функции для отображения графической информации и взаимодействия с пользователем. 

  Этапы обработки данных в системе OpenGL:

Аппроксимация кривых и поверхностей

Обработка вершин и сборка примитивов

Растеризация и обработка фрагментов

Операции над пикселами

Подготовка текстуры

Передача данных в буфер кадра



Основные библиотеки используемые при создании приложений



OpenGL  является на данный момент одним из самых популярных программных интерфейсов (API) для разработки приложений в области двумерной и трехмерной графики. Стандарт OpenGL был разработан и утвержден в 1992 году ведущими фирмами в области разработки программного обеспечения, а его основой стала библиотека IRIS GL, разработанная Silicon Graphics.

На данный момент реализация  OpenGL  включает в себя несколько библиотек. Были созданы библиотеки для отображения информации с помощью оконной подсистемы для операционных систем Windows и Unix (WGL и GLX соответственно), а также библиотеки GLAUX и GLUT, которые используются для создания так называемых консольных приложений.

Библиотека GLAUX уступает по популярности написанной несколько позже библиотеке GLUT, хотя они предоставляют примерно одинаковые возможности.



Массивы вершин и списки изображений

Назначение и команды для их описания



Под вершиной понимается точка в трехмерном пространстве, координаты которой можно задавать следующим образом:



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 последовательно задаются вершины выпуклого многоугольника.



Массивы вершин



Если вершин много, то чтобы не вызывать для каждой команду 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.



Преобразование проецирования, задание ортографической и перспективной проекции



В OpenGL используются как основные три системы координат: левосторонняя, правосторонняя и оконная. Первые две системы являются трехмерными и отличаются друг от друга направлением оси z: в правосторонней она направлена на наблюдателя, а в левосторонней – в глубь экрана. Расположение осей x и y аналогично описанному выше. Левосторонняя система используется для задания значений параметрам команды gluPerspective(), glOrtho(), а правосторонняя или мировая система координат во всех остальных случаях. Отображение трехмерной информации происходит в двумерную оконную систему координат.

Для задания различных преобразований объектов сцены в OpenGL используются операции над матрицами, при этом различают три типа матриц: видовая, проекций и текстуры. Все они имеют размер 4x4. Видовая матрица определяет преобразования объекта в мировых координатах, такие как параллельный перенос, изменение масштаба и поворот. Матрица проекций задает как будут проецироваться трехмерные объекты на плоскость экрана (в оконные координаты), а матрица текстуры определяет наложение текстуры на объект.

Для того чтобы выбрать, какую матрицу надо изменить, используется команда



void glMatrixMode(GLenum mode)



вызов которой со значением параметра mode равным GL_MODELVIEW, GL_PROJECTION, GL_TEXTURE включает режим работы с видовой, проекций и матрицей текстуры соответственно. Для вызова команд, задающих матрицы того или иного типа необходимо сначала установить соответствующий режим.

Для определения элементов матрицы текущего типа вызывается команда



void glLoadMatrix[f d](GLtype *m)



где m указывает на массив из 16 элементов типа float или double в соответствии с названием команды, при этом сначала в нем должен быть записан первый столбец матрицы, затем второй, третий и четвертый.



Команда



void glLoadIdentity(void)



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



void glPushMatrix(void)

void glPopMatrix(void)



Они записывают и восстанавливают текущую матрицу из стека, причем для каждого типа матриц стек свой. Для видовых матриц его глубина равна как минимум 32, а для двух оставшихся типов как минимум 2.

Для умножения текущей матрицы слева на другую матрицу используется команда



void glMultMatrix[f d](GLtype *m)



где m должен задавать матрицу размером 4x4 в виде массива с описанным расположением данных. Однако обычно для изменения матрицы того или иного типа удобно использовать специальные команды, которые по значениям своих параметров создают нужную матрицу и перемножают ее с текущей. Чтобы сделать текущей созданную матрицу, надо перед вызовом этой команды вызвать glLoadIdentity().

В целом, для отображения трехмерных объектов сцены в окно приложения используется следующая последовательность действий:

Координаты объекта	=>	Видовые координаты	=>	Усеченные координаты	=>	Нормализованные координаты	=>	Оконные координаты



В OpenGL существуют ортографическая (параллельная) и перспективная проекция. Первый тип проекции может быть задан командами



void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far)

void gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top)



Первая команда создает матрицу проекции в усеченный объем видимости (параллелограмм видимости) в левосторонней системе координат. Параметры команды задают точки (left, bottom, -near) и (right, top, -near), которые отвечают левому нижнему и правому верхнему углам окна вывода. Параметры near и far задают расстояние до ближней и дальней плоскостей отсечения по дальности от точки (0,0,0) и могут быть отрицательными.

Во второй команде, в отличие от первой, значения near и far устанавливаются равными –1 и 1 соответственно.

Перспективная проекция определяется командой



void gluPerspective(GLdouble angley, GLdouble aspect, GLdouble znear, GLdouble zfar)



которая задает усеченный конус видимости в левосторонней системе координат. Параметр angley определяет угол видимости в градусах по оси у и должен находиться в диапазоне от 0 до 180. Угол видимости вдоль оси x задается параметром aspect, который обычно задается как отношение сторон области вывода. Параметры zfar и znear задают расстояние от наблюдателя до плоскостей отсечения по глубине и должны быть положительными. Чем больше отношение zfar/znear, тем хуже в буфере глубины будут различаться расположенные рядом поверхности, так как по умолчанию в него будет записываться ‘сжатая’ глубина в диапазоне от 0 до 1.



Задание и исходный код



Задание: рисунок «КУБ».



unit mgl;

interface

uses

  Windows, Messages, Classes, Graphics, Forms, ExtCtrls, OpenGL, StdCtrls,

  Controls, SysUtils, Spin, Menus, Dialogs;

type

  TForm1 = class(TForm)

    procedure FormCreate(Sender: TObject);

    procedure FormResize(Sender: TObject);

    procedure FormDestroy(Sender: TObject);

    procedure FormKeyPress(Sender: TObject; var Key: Char);

  private

    DC : HDC;

    hrc : HGLRC;

    Angle, AngleX, AngleY, AngleZ: GLfloat;

    procedure DrawScene;

    procedure InitializeRC;

    procedure SetDCPixelFormat;

  protected

    // Обработка сообщения WM_PAINT - аналог события OnPaint

    procedure WMPaint(var Msg: TWMPaint); message WM_PAINT;

  end;

var

  Form1: TForm;

  ch, c, i: integer;

  s: string;

  ShowHelp: boolean=true;

implementation

{$R *.DFM}

const

 // массив свойств материала

 MaterialColor: Array [0..3] of GLfloat = (0.5, 0.0, 1.0, 1.0);

 // Процедура инициализации источника цвета

procedure TForm1.InitializeRC;

begin

 glEnable(GL_DEPTH_TEST); // разрешаем тест глубины

 glEnable(GL_LIGHTING); // разрешаем работу с освещенностью

 glEnable(GL_LIGHT0); // включаем источник света 0

end;

 // Отрисовка картинки

procedure TForm1.DrawScene;

begin

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

 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

 // трехмерность

 glMatrixMode(GL_MODELVIEW);

 glLoadIdentity;

 glTranslatef(0.0, 0.0, -8.0); // влево/вправо, вверх/вниз, назад/вперед

 glRotatef(AngleX, 1.0, 0.0, 0.0); // поворот на угол X

 glRotatef(AngleY, 0.0, 1.0, 0.0); // поворот на угол Y

 glRotatef(AngleZ, 0.0, 0.0, 1.0); // поворот на угол Z

 // Шесть сторон куба

 glBegin(GL_POLYGON);

  glNormal3f(0.0, 0.0, 1.0);

  glVertex3f(1.0, 1.0, 1.0);

  glVertex3f(-1.0, 1.0, 1.0);

  glVertex3f(-1.0, -1.0, 1.0);

  glVertex3f(1.0, -1.0, 1.0);

 glEnd;



 glBegin(GL_POLYGON);

  glNormal3f(0.0, 0.0, -1.0);

  glVertex3f(1.0, 1.0, -1.0);

  glVertex3f(1.0, -1.0, -1.0);

  glVertex3f(-1.0, -1.0, -1.0);

  glVertex3f(-1.0, 1.0, -1.0);

 glEnd;



 glBegin(GL_POLYGON);

  glNormal3f(-1.0, 0.0, 0.0);

  glVertex3f(-1.0, 1.0, 1.0);

  glVertex3f(-1.0, 1.0, -1.0);

  glVertex3f(-1.0, -1.0, -1.0);

  glVertex3f(-1.0, -1.0, 1.0);

 glEnd;



 glBegin(GL_POLYGON);

  glNormal3f(1.0, 0.0, 0.0);

  glVertex3f(1.0, 1.0, 1.0);

  glVertex3f(1.0, -1.0, 1.0);

  glVertex3f(1.0, -1.0, -1.0);

  glVertex3f(1.0, 1.0, -1.0);

 glEnd;



 glBegin(GL_POLYGON);

  glNormal3f(0.0, 1.0, 0.0);

  glVertex3f(-1.0, 1.0, -1.0);

  glVertex3f(-1.0, 1.0, 1.0);

  glVertex3f(1.0, 1.0, 1.0);

  glVertex3f(1.0, 1.0, -1.0);

 glEnd;



 glBegin(GL_POLYGON);

  glNormal3f(0.0, -1.0, 0.0);

  glVertex3f(-1.0, -1.0, -1.0);

  glVertex3f(1.0, -1.0, -1.0);

  glVertex3f(1.0, -1.0, 1.0);

  glVertex3f(-1.0, -1.0, 1.0);

 glEnd;



 SwapBuffers(DC);   // конец работы

end;



 // обычные действия OpenGL, Создание окна

procedure TForm1.FormCreate(Sender: TObject);

begin

 Angle:=0;

 AngleX:=30;

 AngleY:=-30;

 AngleZ:=0;

 c:=1;

 DC:=GetDC(Handle);

 SetDCPixelFormat;

 hrc:=wglCreateContext(DC);

 wglMakeCurrent(DC, hrc);

 InitializeRC;

 // свойства материала - лицевые стороны - рассеянный

 // цвет материала и диффузное отражение материала - значения из массива

 glMaterialfv(GL_FRONT,GL_AMBIENT_AND_DIFFUSE,@MaterialColor);

end;



 // Установка формата пикселей

procedure TForm1.SetDCPixelFormat;

var

 nPixelFormat: integer;

 pfd: TPixelFormatDescriptor;

begin

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

 with pfd do

  begin

   nSize   :=sizeof(pfd);

   nVersion:=1;

   dwFlags :=PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or

                                             PFD_DOUBLEBUFFER;

   iPixelType:=PFD_TYPE_RGBA;

   cColorBits:=24; // 24

   cDepthBits:=32; // 32

   iLayerType:= PFD_MAIN_PLANE;

 end;

 nPixelFormat := ChoosePixelFormat(DC, @pfd);

 SetPixelFormat(DC, nPixelFormat, @pfd);

end;

 // Изменение размеров окна

procedure TForm1.FormResize(Sender: TObject);

begin

 glMatrixMode(GL_PROJECTION);

 glLoadIdentity;

 gluPerspective(30.0, Width/Height, 1.0, 10.0);

 glViewport(0, 0, Width, Height);

 glMatrixMode(GL_MODELVIEW);

 InvalidateRect(Handle, nil, False);

end;

 // Обработка сообщения WM_PAINT, рисование окна

procedure TForm1.WMPaint(var Msg: TWMPaint);

var

 ps: TPaintStruct;

begin

 BeginPaint(Handle, ps);

 DrawScene;

 EndPaint(Handle, ps);

end;

 // Конец работы программы

procedure TForm1.FormDestroy(Sender: TObject);

begin

 wglMakeCurrent(0, 0);

 wglDeleteContext(hrc);

 ReleaseDC(Handle, DC);

end;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

  if ord(key)=27 then Application.Terminate; // Esc

  FormResize(nil);

end;

end.


Рисунок «Куб»






Использованные источники



Тихомиров Ю. Программирование трехмерной графики. СПб., BHV 1998.

Visual Introduction in OpenGL, Siggraph’98.

The OpenGL graphics system: a specification (version 1.1).

The OpenGL Utility Toolkit (GLUT) Programming Interface, API version 3, specification.