Для создания
окна будем использовать библиотеку
xlib. Запустите
Lazarus и создайте новый проект, для этого
выберите пункт меню File>New>Program и
нажмите кнопку OK (Смотрите рисунок 1).
Назовите его ogl_p3.lpr и сохраните проект
в домашней директории пользователя
/home/[имя пользователя]/Projects/lazarus/ogl_p3/.
Рисунок
1. Создание нового проекта
Чтобы
сохранить проект под выбранным именем,
нажмите пункт меню File>Save. В качестве
пути сохранения проекта укажите каталог
который вы создали ранее.
Если посмотреть
в редактор кода среды Lazarus, то можно
увидеть уже введенный стартовый код
проекта. Удалим лишнее и оставим только
то, что нам пригодится в дальнейшем:
program ogl_p3;
uses
begin
end.
Теперь в секции
uses подключим необходимые модули:
1) для работы с
функциями связывающие opengl с x window system
(glx)
2) для работы с
функциями создания окна x window system (x,
xutil, xlib)
3) для работы с
функциями OpenGL (gl, glu)
Таким образом
секция uses у нас примит вид:
uses glx, x, xutil, xlib,
gl, glu;
Выше секции uses
укажите режим синтаксиса совместимый
с delphi
{$MODE delphi}
После секции
uses создадим секцию глобальных переменных
var, в данной секции определим следующие
переменные:
var
dpy: PDisplay;
visinfo: PXVisualInfo;
Attr: Array[0..10] of
integer =
(GLX_DEPTH_SIZE, 16,
GLX_RGBA,
GLX_RED_SIZE, 1,
GLX_GREEN_SIZE, 1,
GLX_BLUE_SIZE, 1,
GLX_DOUBLEBUFFER,
none);
cm: TColormap;
winAttr:
TXSetWindowAttributes;
win :TWindow;
glXCont: GLXContext;
1) Указатель
на структуру Display
с именем dpy, которая определена в xlib и
содержит информацию
о соединении с X-сервером.
2) Указатель
на структуру XvisualInfo
c именем visinfo, которая определена в xlib и
содержит информацию о визуальных
характеристиках поддерживаемых
экраном X-сервера.
3)
Attr, массив
атрибутов визуальных характеристик,
которые мы запрашиваем у X-сервера.
5)
Структура TXSetWindowAttributes
с именем winAttr,
хранит все необходимые
атрибуты для создания нового окна.
6) Идентификатор,
с именем win, созданного окна типа Twindow.
7) Переменная
хранящая информацию о созданном контексте
рендеринга OpenGL.
Для начала нам
нужно определится, что мы будем рисовать
с помощью функций OpenGL. В книге Эдварда
Эйнджела «Интерактивная компьютерная
графика. Вводный курс на базе OpenGL» 2-е
издание 2001г. написан си код алгоритма
построения треугольника Серпинского.
Так что попробуем его нарисовать.
Далее определим
новый тип:
type
point2 =
array[1..2] of GLfloat;
Этот новый тип
point2, мы будем использовать для описания
координат одной точки.
Далее определи
секцию var, где определим одну точку
(начальная точка, откуда
идут все расчеты) и массив
из трех точек (вершины
большого треугольника):
var
p: point2 = (0.0, 0.0);
vertices: array[1..3]
of point2 =
((0.0, 0.0), (250.0, 500.0), (500.0, 0.0));
Ниже
напишем код процедуры перерисовки окна:
procedure redraw();
var
j,k: integer;
begin
Randomize;
glClear(GL_COLOR_BUFFER_BIT);
for k:=1 to 250000 do
begin
j := Random(3) + 1;
p[1] := (p[1] +
vertices[j][1]) / 2;
p[2] := (p[2] +
vertices[j][2]) / 2;
glBegin(GL_POINTS);
glVertex2fv(@p);
glEnd;
end;
glXSwapBuffers(dpy,
win);
end;
В данной
процедуре размещен кусок кода который
рисует 250000 точек в определенных
координатах согласно алгоритму, эти
точки не выходя за пределы большого
треугольника.
Здесь процедура
glXSwapBuffers переключает
сформировавшуюся картинку из буфера
кадра на экран.
Ниже напишем
процедуру инициализации обзора сцены:
procedure initgl();
begin
glClearColor(1.0, 1.0,
1.0, 0.0);
glColor3f(1.0, 0.0,
0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, 500.0,
0.0, 500.0);
glMatrixMode(GL_MODELVIEW);
end;
Ниже напишем
процедуру, которая вызывается каждый
раз при изменение размеров окна:
procedure resize(width,
height: integer);
begin
glViewport(0, 0, width,
height);
end;
Ниже напишем
процедуру, в которой находится бесконечный
цикл обработки событий
окна:
procedure loop();
var
event: TXEvent;
begin
while true do
begin
XNextEvent(dpy,
@event);
case event._type of
Expose: redraw();
ConfigureNotify:
resize(event.xconfigure.width,
event.xconfigure.height);
KeyPress:
halt(1);
end;
end;
end;
в данном
случае обрабатываются событие перерисовки
окна (Expose),
событие изменения размеров окна
(ConfigureNotify),
события нажатия клавиши (KeyPress).
Ниже определим
секцию var, в которой определены переменные
необходимые для последующего кода
проекта в главных операторных скобках
(begin ... end.)
var
errorBase, eventBase:
integer;
title: String;
window_title_property:
TXTextProperty;
1) Переменные с
именами errorBase и eventBase нужны для хранения
кода ошибок возвращаемые процедурой
glXQueryExtension.
2) Переменная
строка с именем title для хранения названия
заголовка окна
3) Переменная
window_title_property необходима для установки
имени приложения
Ниже напишем
код главных операторных скобок:
begin
initGlx();
dpy := XOpenDisplay(
nil );
if (dpy = nil) then
writeLn('Error: Could
not connect to X server');
if not
(glXQueryExtension(dpy, errorBase, eventBase)) then
writeLn('Error: GLX
extension not supported');
visinfo :=
glXChooseVisual(dpy, DefaultScreen(dpy), Attr);
if (visinfo = nil)
then
writeLn('Error: Could
not find visual');
cm :=
XCreateColormap(dpy, RootWindow(dpy, visinfo.screen),
visinfo.visual,
AllocNone);
winAttr.colormap :=
cm;
winAttr.border_pixel
:= 0;
winAttr.background_pixel := 0;
winAttr.event_mask :=
ExposureMask or
ButtonPressMask or
StructureNotifyMask or
KeyPressMask;
win :=
XCreateWindow(dpy, RootWindow(dpy, visinfo.screen),
0, 0, 500, 500, 0,
visinfo.depth, InputOutput, visinfo.visual,
CWBorderPixel or
CWColormap or CWEventMask, @winAttr);
title :=
'ogl_p3';
XStringListToTextProperty(@title, 1, @window_title_property);
XSetWMName(dpy, win,
@window_title_property);
glXCont :=
glXCreateContext(dpy, visinfo, none, true);
if (glXCont = nil)
then
writeLn('Error: Could
not create an OpenGL rendering context');
glXMakeCurrent(dpy,
win, glXCont);
XMapWindow(dpy, win);
initgl();
loop();
end.
1) Функция
initGlx производит
инициализацию расширения GLX.
2) Функция
XopenDisplay устанавливает
соединение клиента с X-сервером.
3) Функция
glXQueryExtension запрашивает
у X-сервера поддерживает ли он GLX
расширения.
4) Функция
glXChooseVisual запрашивает у X-сервера возможные
визуальные характеристики экрана.
5) Функция
XcreateColormap создает новую цветовую палитру
для нового окна
6) Далее
мы задаем необходимые атрибуты
создаваемого окна:
winAttr.colormap :=
cm;
winAttr.border_pixel
:= 0;
winAttr.background_pixel := 0;
winAttr.event_mask :=
ExposureMask or
ButtonPressMask or
StructureNotifyMask or
KeyPressMask;
7) Функция
XcreateWindow создает
новое окно согласно заданным атрибутам
и полученным характеристикам.
8)
Следующими строчками
кода мы устанавливаем заголовок окна:
title :=
'ogl_p3';
XStringListToTextProperty(@title, 1, @window_title_property);
XSetWMName(dpy, win,
@window_title_property);
9) Функция
glXCreateContext создает
контекст OpenGL
10) Функция
glXMakeCurrent привязывает созданный контекст
OpenGL к созданному окну.
11) Функция
XmapWindow делает окно видимым
12) Перед тем как
передать управление обработчику событий
окна, необходимо выполнить процедуру
initgl инициализации обзора сцены.
13) Запускаем
бесконечный цикл обработки событий
окна.
Теперь давайте
соберем проект. Результат работы
программы представлен рисунком 2.
Рисунок
2. Треугольник Серпинского
Код
полностью:
program ogl_p3; {$MODE delphi} uses glx, x, xutil, xlib, gl, glu; var dpy: PDisplay; visinfo: PXVisualInfo; Attr: Array[0..10] of integer = (GLX_DEPTH_SIZE, 16, GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER, none); cm: TColormap; winAttr: TXSetWindowAttributes; win :TWindow; glXCont: GLXContext; type point2 = array[1..2] of GLfloat; var p: point2 = (0.0, 0.0); vertices: array[1..3] of point2 =
((0.0, 0.0), (250.0, 500.0), (500.0, 0.0)); procedure redraw(); var j,k: integer; begin Randomize; glClear(GL_COLOR_BUFFER_BIT); for k:=1 to 250000 do begin j := Random(3) + 1; p[1] := (p[1] + vertices[j][1]) / 2; p[2] := (p[2] + vertices[j][2]) / 2; glBegin(GL_POINTS); glVertex2fv(@p); glEnd; end; glXSwapBuffers(dpy, win); end; procedure initgl(); begin glClearColor(1.0, 1.0, 1.0, 0.0); glColor3f(1.0, 0.0, 0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, 500.0, 0.0, 500.0); glMatrixMode(GL_MODELVIEW); end; procedure resize(width, height: integer); begin glViewport(0, 0, width, height); end; procedure loop(); var event: TXEvent; begin while true do begin XNextEvent(dpy, @event); case event._type of Expose: redraw(); ConfigureNotify: resize(event.xconfigure.width, event.xconfigure.height); KeyPress: halt(1); end; end; end; var errorBase, eventBase: integer; title: String; window_title_property: TXTextProperty; begin initGlx(); dpy := XOpenDisplay( nil ); if (dpy = nil) then writeLn('Error: Could not connect to X server'); if not (glXQueryExtension(dpy, errorBase, eventBase)) then writeLn('Error: GLX extension not supported'); visinfo := glXChooseVisual(dpy, DefaultScreen(dpy), Attr); if (visinfo = nil) then writeLn('Error: Could not find visual'); cm := XCreateColormap(dpy, RootWindow(dpy, visinfo.screen), visinfo.visual, AllocNone); winAttr.colormap := cm; winAttr.border_pixel := 0; winAttr.background_pixel := 0; winAttr.event_mask := ExposureMask or ButtonPressMask or StructureNotifyMask or KeyPressMask; win := XCreateWindow(dpy, RootWindow(dpy, visinfo.screen), 0, 0, 500, 500, 0, visinfo.depth, InputOutput, visinfo.visual, CWBorderPixel or CWColormap or CWEventMask, @winAttr); title := 'ogl_p3'; XStringListToTextProperty(@title, 1, @window_title_property); XSetWMName(dpy, win, @window_title_property); glXCont := glXCreateContext(dpy, visinfo, none, true); if (glXCont = nil) then writeLn('Error: Could not create an OpenGL rendering context'); glXMakeCurrent(dpy, win, glXCont); XMapWindow(dpy, win); initgl(); loop(); end.
syntax highlighted by Code2HTML, v. 0.9.1