OpenGL в Delphi

       

Точка


Для начала скажем, что в OpenGL левый нижний угол области вывода имеет координаты [-1; -1], правый верхний - [1, 1]
Начнем наше знакомство с примитивами OpenGL c самого простого из них - точки. Нарисуем на экране пять точек, четыре по углам окна и одну в центре (проект располагается в подкаталоге Ex01).
Обработчик события onPaint формы дополнился следующими строками

glViewPort (0, 0, ClientWidth, ClientHeight); // область вывода
glPointSize (20); // размер точек
glColor3f (1. 0, 1. 0, 1. 0); // цвет примитивов
glBegin (GL_POINTS); // открываем командную скобку
glVertex2f (-1, -1); // левый нижний угол
glVertex2f (-1, 1); // левый верхний угол
glVertex2f (0, 0);
glVertex2f (1, -1);
glVertex2f (1, 1); glEnd; SwapBuffers(Canvas. Handle)
// центр окна
// правый верхний угол
// правый нижний угол
// закрываем командную скобку
// содержимое буфера - на экран

Разберем каждую из строк программы подробно.
Первая строка задает область вывода указанием координат левого нижнего и правого верхнего углов (в пикселах, в оконных координатах). Я взял в качестве области вывода всю клиентскую часть окна. Если третий параметр процедуры glViewPort записать как round (clientwidth / 2), то картинка вдвое сузится, но окрашено будет все окно (проект из подкаталога Ex02).
Следующие две строки программы определяют параметры выводимых точек, размер и цвет.
На примере команды glcolor3f разберем синтаксис команд OpenGL.
Из справки по этой команде вы узнаете, что она принадлежит к целому набору команд glcolor с различными окончаниями: зb, 4i и прочие. Цифра в окончании соответствует количеству требуемых аргументов, а следующая цифрой буква показывает требуемый тип аргументов. To есть glColor3f требует в качестве аргументов тройку вещественных (float) чисел, а glColor3i - тройку целых (int) чисел.
Аналогичный синтаксис мы встретим у многих других команд OpenGL
Здесь же, в справке, выясняем, что при записи функции в вещественной форме аргументы лежат в интервале [0; 1], а в целочисленной форме - линейно отображаются на этот интервал, т. e. для задания белого цвета целочисленная форма команды будет выглядеть так:

glColor3i (2147483647, 2147483647, 2147483647); //цвет примитивов

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

Замечание
Если имя команды заканчивается на v (векторная форма), то аргументом ее служит указатель на структуру, содержащую данные, например, массив To есть, например, если последние три символа в имени команды 3fv, то ее аргумент - адрес массива трех вещественных чисел Использование такой формы команды является самым оптимальным по скоростным характеристикам.

Далее в программе следуют функции (командные скобки) glBegin и glEnd, между которыми заключены собственно процедуры рисования. Разберемся подробнее с функциями giBegin и glEnd ввиду их особой важности: большинство графических построений связано с использованием именно этой пары функций.
Во-первых, необходимо уяснить, что это командные скобки библиотеки OpenGL, не заменяющие операторные скобки языка Pascal и не имеющие к ним никакого отношения Ошибка при использовании командных скобок не распознается компилятором. Если в программе написана неправильная вложенность командных скобок OpenGL, то ошибка проявится только в процессе диалога приложения с сервером.
Во-вторых, внутри этих скобок могут находиться любые операторы языка Pascal и почти любые функции OpenGL (вернее, очень многие) Включенные в скобки команды OpenGL отрабатываются так же, как и за пределами этих скобок Главное назначение командных скобок - это задание режима (примитива) для команд glVertex (вершина), определяющих координаты вершин для рисования примитивов OpenGL.
В рассмотренном примере аргументом функции glBegin я взял символическую константу GL^POINTS Из файла справки выясняем, что в этом случае все встретившиеся до закрывающей скобки glEnd вершины (аргументы glvertex) задают координаты очередной отдельной точки. Команду glvertex я взял в форме с двумя вещественными аргументами. Получив справку по glvertex, вы можете убедиться, что и эта команда имеет целый ворох разновидностей.
Мы собирались нарисовать четыре точки по углам окна и одну в центре, поэтому между командными скобками располагаются пять строк с вызовом glvertex, аргументы которых соответствуют положениям точек в системе координат области вывода библиотеки OpenGL.
Сейчас обратите внимание на то, что вместе с изменением размеров окна рисуемое изображение также изменяется: точки всегда рисуются на своих местах относительно границ окна. Следующее замечание тоже очень важно обработчик события onResize формы тот же, что и у события OnPaint.
Приведу еще несколько простых примеров на рисование точек
Если надо нарисовать десять точек по диагонали, то можно написать так (пример располагается в подкаталоге Ех0З):

glBegin (GL_POINTS);
For i := 0 to 9 do
glVertex2f (i / 5 - 1, i / 5 - 1);
glEnd;

Ну а следующий код нарисует сотню точек со случайными координатами и цветами (пример из подкаталога Ех04):

glBegin (GL_POINTS);
For i := 1 to 100 do begin
glColor3f (random, random, random); glVertex2f (random * 2 - 1, random * 2-1);
end;
glEnd;

Из этого примера мы видим, что команда, задающая цвет примитивов, может присутствовать внутри командных скобок OpenGL. Вызов же команды glpomtsize между командными скобками безрезультатен и будет генерировать внутреннюю ошибку OpenGL. Так что если мы хотим получать точки случайных размеров, цикл надо переписать так (проект из подкаталога Ех05):

For i := I to 100 do begin
glColorSf (random, random, random);
glPointSize (random (20)); // обязательно за пределами скобок
glBegin (GL_POINTS);
glVertex2f (random * 2 - 1, random * 2 - 1);
glEnd;
end;

Скорее всего, вас удивило то, что точки рисуются в виде квадратиков Чтобы получить точки в виде кружочков, перед glBegin вставьте строку

glEnable (GL_POINT_SMOOTH); // включаем режим сглаживания точек

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

Замечание
Вспомним в режиме двойной буферизации все рисование осуществляется в задний буфер кадра, он является текущим на всем процессе воспроизведения По команде SwapBuffers текущее содержимое переднего буфера подменяется содержимым заднего буфера кадра, но текущим буфером после этого все равно остается задний.
Команда glciear с аргументом GL_COLOR_BUFFER_BIT очищает текущий буфер вывода.

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



Замечание
В большинстве примеров этой книги я не использую вызов glFinish или glFlush, ускорение от них получается микроскопическим. Но в программах, не применяющих двойную буферизацию, эти команды должны вызываться обязательно, иначе на экране может оказаться "недорисованная" картинка.

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

xpos: GLfloat; // координаты курсора в системе координат
OpenGL ypos: GLfloat;
Создайте следующий обработчик события MouseMove формы
xpos: = 2 * X / ClientWidth - 1;
ypos: = 2 * (ClientHeight - Y) / ClientHeight - 1;
Refresh; // перерисовываем окно

В обработчике события Paint опишите локальную целочисленную переменную i и содержательную часть кода приведите к виду

For i: = 1 to 40 do begin // сорок точек
glColor3f (random, random, random); // случайного цвета
glPointSize (random (10)); // случайного размера
glBegin (GL_POINTS}; // со случайными координатами вокруг курсора
glVertex2f (xpos + 0. 2 * random * sin (random (360)),
ypos + 0. 2 * random * cos (random (3 60)));
glEnd;
end;

Если все сделано правильно, при движении курсора по поверхности формы в районе курсора появляется облачко разноцветных точек (готовый проект я поместил в подкаталог Ex06). Разберитесь в этом примере с масштабированием значения переменных xpos и ypos и обратите внимание, что обработчик движения мыши заканчивается вызовом Refresh - принудительной перерисовкой окна при каждом движении курсора.
Предлагаю также разобрать простой пример из подкаталога Ex07 - построение синусоиды средствами OpenGL:

procedure TfrmGL. FormPaint(Sender: TObject);
const
а = -Pi; // начало интервала
b = Pi; // конец интервала
num = 200; // количество точек на интервале
var
x: GLfloat;
l: GLint; begin wglMakeCurrent(Canvas. Handle, hrc);
glViewPort (0, 0, ClientWidth, ClientHeight);
glClearColor (0. 5, 0. 5, 0. 75, 1. 0);
glClear (GL_COLOR_BUFFER_BIT);
glEnable (GL_POINT_SMOOTH);
glColor3f (1. 0, 0. 0, 0. 5);
glBegin (GL_POINTS); For i: = 0 to num do begin x: = а + i * (b - а) / num;
glVertex2f (2 * (x - а) / (b - а) - 1. 0, sin(x) * 0. 75);
end;
glEnd;
SwapBuffers(Canvas. Handle);
wglMakeCurrent(0, 0);
end;

Пример из подкаталога Ex08 демонстрирует вывод средствами OpenGL прямо на поверхность рабочего стола. Напоминаю, окно с нулевым значением дескриптора соответствует поверхности рабочего стола, чем мы и пользуемся в этом примере для получения ссылки на контекст устройства:

dc: = GetDC (0);

Для завершения работы приложения нажмите клавиши <Alt>+<F4> или <Esc>. Обратите внимание, что по завершении работы приложения перерисовываем рабочий стол:

InvalidateRect (0, nil, False);

Перерисовка здесь необходима для восстановления нормального вида рабочего стола.

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

В заключение разговора о точках приведу одно маленькое, но важное замечание: помимо константы GL_points в OpenGL имеется символическая константа GL_poiNT, использующаяся в других, нежели glBegin, командах Но компилятор Delphi, конечно, не сможет распознать ошибку, если вместо одной константы OpenGL указать другую, когда типы констант совпадают. В этом случае ошибка приведет только к тому, что не будет построено ожидаемое изображение, аварийное завершение работы приложения не произойдет.
To же самое справедливо для всех рассматриваемых далее констант - необходимо внимательно следить за синтаксисом используемых команд.



Содержание раздела