Sign in Join
Wiki copytemporary

OpenGL Tutorial/ru: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11# Введение
22
33OpenGL является средой для разработки интерактивных 2D и 3D графических приложений. OpenGL позволяет быстро разрабатывать приложения, включающие в себя рендеринг, текстурирование, спецэффекты и другие мощные функции визуализации. Разработчики могут использовать OpenGL на всех популярных платформах и операционных системах.
44
55Более подробную информацию о OpenGL можно узнать [здесь](http://www.opengl.org/about/overview/).
66
77## GLUT
88
99GLUT - библиотека для использования OpenGL, реализующий простой оконный интерфейс API. GLUT делает изучение OpenGL более легким. Это кроссплатформенный API, так что вы можете написать программу OpenGL, которая будет работать на большинстве операционных систем.
1010
1111Подробнее о GLUT вы можете почитать [здесь](http://www.opengl.org/resources/libraries/glut/).
1212
1313Многие ОС поставляются с GLUT, но если у вас её не оказалось, можно поискать её в Интернете с помощью [Google](http://www.google.com/).
1414
1515Версию для Windows можно скачать на [www.xmission.com](http://www.xmission.com/~nate/glut.html).
1616
1717Про GLUT unit можно почитать в статье [[OpenGL]].
1818
1919## GLFW
2020
2121См. <http://www.glfw.org/>
2222
2323## LCL
2424
2525The Lazarus Component Library также может быть использована при работе с OpenGL. Lazarus включает в себя TOpenGLControl. Пакет LazOpenGLContext можно найти в lazarus/components/opengl/lazopenglcontext.lpk, примеры использования - в lazarus/examples/openglcontrol/openglcontrol_demo.lpi.
2626
2727## LCL / GLFW / GLUT
2828
2929Когда нужно использовать GLUT, а когда LCL?
3030
3131- GLUT лучше применим, если вы хотите сделать все сами.
3232- LCL лучше использовать для обычных приложений. Например, 3D-редактор должен иметь несколько окон OpenGL и прочие обыкновенные элементы: кнопки, выпадающие списки, окна, модальные окна и т. д.
3333
3434Часть OpenGL почти такая же. Для GLUT нужны dll под windows, а LCL обычно работает "из коробки", но и исполняемый файл имеет больший размер.
3535
3636# Примеры
3737
3838## Ваша первая LCL-программа
3939
4040Использование LCL, как правило, является самым простым способом доступа к OpenGL с помощью Lazarus. Поскольку GLUT устарела, использование LCL - это хорошая идея для нового проекта OpenGL Lazarus. Большинство примеров кода, описанных ниже для GLUT, легко перевести в код LCL, хотя вам нужно будет найти эквиваленты для функций с префиксом 'glut', например, вместо "glutSwapBuffers" мы будем использовать LCL-свойство "SwapBuffers" для отображения нашего рендеринга. Единственная замечательная функция, которую обеспечивает GLUT и которую трудно сделать с LCL - это отображение текста на экране (см. Раздел «Растровые шрифты» GLUT ниже). Однако, поскольку это ваша первая LCL-программа, мы немного упростим ее, не отображая текст.
4141
4242Lazarus поставляется с примером программы OpenGL, вы можете найти ее в папке *Lazarus/Examples/openglcontrol*. Этот пример демонстрирует множество мощных функций для создания анимированного изображения OpenGL. Тем не менее, это также относительно сложная программа. Ниже приведен минимальный проект Lazarus, который имитирует некоторые функции, описанные в примерах GLUT, описанных ниже. Чтобы создать его, запустите Lazarus и выберите Project/NewProject, чтобы создать новое приложение. Выберите пункт меню Project/ProjectInspector, нажмите кнопку 'Add..' (Добавить ...), перейдите к 'New Requirement' (Новое требование) и пакету «LazOpenGLContext». Затем вставьте приведенный ниже код в ваш 'unit1.pas'. Затем щелкните форму и на вкладке событий инспектора объектов свяжите событие «OnCreate» с функцией «FormCreate». Теперь вы сможете запустить новое приложение, выбрав пункт меню Run/Run (Выполнить/запустить).
4343
4444Код создает новую панель OpenGL, которая заполняет форму. OpenGL рисует простой треугольник на форме. Обратите внимание, что при запуске приложения вы можете изменить размер формы, и треугольник, чтобы заполнить форму, масштабируется пропорционально.
4545
4646``` pascal
4747unit Unit1;
4848
4949{$mode objfpc}{$H+}
5050
5151interface
5252
5353uses
5454 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, OpenGLContext, gl;
5555
5656type
5757 TForm1 = class(TForm)
5858 procedure FormCreate(Sender: TObject);
5959 procedure GLboxPaint(Sender: TObject);
6060 private
6161 GLBox: TOpenGLControl;
6262 public
6363 end;
6464
6565var
6666 Form1: TForm1;
6767
6868implementation
6969
7070{$R *.lfm}
7171
7272procedure TForm1.GLboxPaint(Sender: TObject);
7373begin
7474 glClearColor(0.27, 0.53, 0.71, 1.0); // Задаем синий фон
7575 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
7676 glLoadIdentity;
7777 glBegin(GL_TRIANGLES);
7878 glColor3f(1, 0, 0);
7979 glVertex3f( 0.0, 1.0, 0.0);
8080 glColor3f(0, 1, 0);
8181 glVertex3f(-1.0,-1.0, 0.0);
8282 glColor3f(0, 0, 1);
8383 glVertex3f( 1.0,-1.0, 0.0);
8484 glEnd;
8585 GLbox.SwapBuffers;
8686end;
8787
8888procedure TForm1.FormCreate(Sender: TObject);
8989begin
9090 GLbox := TOpenGLControl.Create(Self);
9191 GLbox.AutoResizeViewport := true;
9292 GLBox.Parent := Self;
9393 GLBox.MultiSampling := 4;
9494 GLBox.Align := alClient;
9595 GLBox.OnPaint := @GLboxPaint; // для "mode delphi" должно быть "GLBox.OnPaint := GLboxPaint"
9696 GLBox.invalidate;
9797end;
9898
9999end.
100100```
101101
102102## Ваша первая GLUT-программа
103103
104104Для того чтобы использовать GLUT, сначала необходимо инициализировать его. Это делается с помощью функции glutInit. Эта функция может разобрать командную строку и установить параметры для главного окна, но она ожидает, что ввод будет в стиле C/C++. Вы можете написать собственную функцию, которая будет преобразовывать ParamCount и ParamStr в стиль C/C++.
105105
106106``` pascal
107107 procedure glutInitPascal(ParseCmdLine: Boolean);
108108 var
109109 Cmd: array of PChar;
110110 CmdCount, I: Integer;
111111 begin
112112 if ParseCmdLine then
113113 CmdCount := ParamCount + 1
114114 else
115115 CmdCount := 1;
116116 SetLength(Cmd, CmdCount);
117117 for I := 0 to CmdCount - 1 do
118118 Cmd[I] := PChar(ParamStr(I));
119119 glutInit(@CmdCount, @Cmd);
120120 end;
121121```
122122
123123По сути, вы создаете массив и заполняете его строками из ParamStr. Эта функция также имеет параметр, который указывает то, что передается glutInit -- вся команда или просто имя исполняемого файла.
124124
125125ToDo: вероятно достаточно **glutInit(@argc, @argv);**.
126126
127127Подробнее о glutInit: <http://www.opengl.org/resources/libraries/glut/spec3/node10.html>
128128
129129Дальше нужно создать главное окно. Установите режим отображения для главного окна использованием `glutInitDisplayMode`. Она принимает только один параметр, который является комбинацией флагов. Обычно `GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH` - это всё, что необходимо.
130130
131131Подробнее о glutInitDisplayMode: <http://www.opengl.org/resources/libraries/glut/spec3/node12.html>
132132
133133Положение и размер окна изменяются с помощью `glutInitWindowPosition` и `glutInitWindowSize`. Они принимают 2 параметра: X и Y координаты в первой функции, ширина и высота в последней. Вы можете использовать `glutGet`, чтобы найти размер экрана и поместить центр окна.
134134
135135ToDo: неверный перевод
136136
137137Подробнее о `glutInitWindowPosition`, `glutInitWindowSize` и `glutGet`: <http://www.opengl.org/resources/libraries/glut/spec3/node11.html> <http://www.opengl.org/documentation/specs/glut/spec3/node70.html>
138138
139139Наконец, окна должны быть созданы с использованием функции glutCreateWindow. Это создаст его с названием, переданным в параметре. Функция возвращает handle окна. Это можно использовать в других функциях, которые требуют handle.
140140
141141Подробнее о glutCreateWindow: <http://www.opengl.org/resources/libraries/glut/spec3/node16.html>
142142
143143Прежде чем программа войдёт в главный цикл, нужно установить некоторые функции для обратной связи. Это нужно для рисования окна, для изменения размеров и для получения нажатий клавиш на клавиатуре. Она устаналивается с помощью `glutDisplayFunc`, `glutReshapeFunc` и `glutKeyboardFunc`.
144144
145145Подробнее: <http://www.opengl.org/resources/libraries/glut/spec3/node45.html#SECTION00080000000000000000>
146146
147147Функция рисования может выглядеть следующим образом:
148148
149149``` pascal
150150 procedure DrawGLScene; cdecl;
151151 begin
152152 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
153153 glutSwapBuffers;
154154 end;
155155```
156156
157157Она только очистит окно, зальёт его цветом фона и сбросит ZBuffer (не волнуйтесь о zbuffer... об этом позже).
158158
159159Функция изменения размера может выглядеть так:
160160
161161``` pascal
162162 procedure ReSizeGLScene(Width, Height: Integer); cdecl;
163163 begin
164164 if Height = 0 then
165165 Height := 1;
166166
167167 glViewport(0, 0, Width, Height);
168168 glMatrixMode(GL_PROJECTION);
169169 glLoadIdentity;
170170 gluPerspective(45, Width / Height, 0.1, 1000);
171171
172172 glMatrixMode(GL_MODELVIEW);
173173 glLoadIdentity;
174174 end;
175175```
176176
177177С помощью этого кода вы говорите OpenGL, где в окне будет происходить отрисовка и задаете матрицу (матричные функции будут описаны позже).
178178
179179Ввод с клавиатуры:
180180
181181``` pascal
182182 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
183183 begin
184184 if Key = 27 then
185185 Halt(0);
186186 end;
187187```
188188
189189Эта функция завершит работу программы при нажатии клавиши ESC. Это единственный способ остановить программу. Если вы закроете окно по-другому, оно исчезнет, но сама программа продолжит работать бесконечно долго.
190190
191191Для запуска основного цикла вызовите `glutMainLoop`. Это запустит бесконечный цикл, в котором содержатся все ваши функции для обратной связи.
192192
193193Основная часть программы может выглядеть так:
194194
195195``` pascal
196196 const
197197 AppWidth = 640;
198198 AppHeight = 480;
199199
200200 procedure InitializeGL;
201201 begin
202202 glClearColor(0.18, 0.20, 0.66, 0);
203203 end;
204204
205205 var
206206 ScreenWidth, ScreenHeight: Integer;
207207 begin
208208 glutInitPascal(True);
209209 glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
210210 glutInitWindowSize(AppWidth, AppHeight);
211211 ScreenWidth := glutGet(GLUT_SCREEN_WIDTH);
212212 ScreenHeight := glutGet(GLUT_SCREEN_HEIGHT);
213213 glutInitWindowPosition((ScreenWidth - AppWidth) div 2,
214214 (ScreenHeight - AppHeight) div 2);
215215 glutCreateWindow('OpenGL Tutorial 1');
216216
217217 InitializeGL;
218218
219219 glutDisplayFunc(@DrawGLScene);
220220 glutReshapeFunc(@ReSizeGLScene);
221221 glutKeyboardFunc(@GLKeyboard);
222222
223223 glutMainLoop;
224224 end.
225225```
226226
227227Следующий урок добавит некоторый код, который будет рисовать простую фигуру.
228228
229229Скачать исходный код или исполняемые файлы linux/windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
230230
231231## Рисование простой фигуры
232232
233233> **Note:** Следующие детали описывают в основном только код OpenGL, поэтому они будут работать как с GLUT, так и с LCL. Вы можете узнать функции GLUT по префиксу 'glu'.
234234
235235Мы добавим только несколько строк кода и сосредоточим внимание на объяснение некоторых функций OpenGL.
236236
237237Поясним следующий код.
238238
239239``` pascal
240240 begin
241241 glMatrixMode(GL_PROJECTION);
242242 glLoadIdentity;
243243 gluPerspective(45, Width / Height, 0.1, 1000);
244244
245245 glMatrixMode(GL_MODELVIEW);
246246 glLoadIdentity;
247247 end;
248248```
249249
250250Использование функции `glMatrixMode` указывает, какую матрицу вы хотите установить. OpenGL работает с 3 матрицами: `GL_MODELVIEW`: это используется при перемещении вершин в пространстве модели. `GL_PROJECTION`: это используется для преобразования 3D-координат в 2D-координаты. `GL_TEXTURE`: это используется для изменения координат текстуры.
251251
252252После того, как вы выбрали матрицу, которую хотите изменить, вы можете вызвать функции, которые изменяют значения матриц. `glLoadIdentity` сбросит матрицу. Поскольку почти все матричные функции умножают текущую матрицу на сгенерированную, иногда необходимо очистить матрицу с помощью этой функции.
253253
254254Чтобы установить перспективу матрицы, используйте функцию `gluPerspective`. Четыре параметра задают поле зрения, соотношение сторон, передний и задний план. Это просто.
255255
256256Теперь вы измените матрицу модели ... на этот раз вы просто установите ее на идентичность.
257257
258258Код рисования для первой фигуры:
259259
260260``` pascal
261261 procedure DrawGLScene; cdecl;
262262 begin
263263 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
264264
265265 glLoadIdentity;
266266 glTranslatef(0, 0, -5);
267267
268268 glBegin(GL_TRIANGLES);
269269 glColor3f(1, 0, 0);
270270 glVertex3f(-1, -1, 0);
271271
272272 glColor3f(0, 1, 0);
273273 glVertex3f(1, -1, 0);
274274
275275 glColor3f(0, 0, 1);
276276 glVertex3f(0, 1, 0);
277277 glEnd;
278278
279279 glutSwapBuffers;
280280 end;
281281```
282282
283283Мы использовали функцию glClear. Она просто сбрасывает буферы. Следующие две функции пока пропустим.
284284
285285`glBegin` обозначает начало рисования в блок. После нее можно начать ввод вершин. Параметр описывает, что мы будем рисовать:
286286
287287`GL_POINTS`: просто рисуем n точек. Нужно указать n вершин.
288288
289289`GL_LINES`: каждая пара вершин задаст линию. Вершины 2n-1 и 2n задают одну линию. При использовании n вершин получится n/2 линий.
290290
291291`GL_LINE_STRIP`: рисует отрезки из первой вершины через все в последнюю. Получится n-1 линий.
292292
293293`GL_LINE_LOOP`: то же самое, но добавляется отрезок из последней вершины в первую. Получится n линий.
294294
295295`GL_TRIANGLES`: каждые три точки зададут треугольник. Получится n/3 треугольников.
296296
297297`GL_TRIANGLE_STRIP`: рисует связанные треугольники. Каждая вершина после первых двух будет задавать треугольник. При нечетном n вершины n, n+1 и n+2 зададут треугольник. При четном n - n+1, n и n+2. Получится n-2 треугольника.
298298
299299`GL_TRIANGLE_FAN`: тоже рисует связанные треугольники. Каждая вершина после первых двух будет задавать треугольник. Вершины 1, n+1 и n+2 зададут треугольник. Получится n-2 треугольников.
300300
301301`GL_QUADS`: каждые четыре вершины рисуют четырехугольник. Получится n/4 четырехугольников.
302302
303303`GL_QUAD_STRIP`: рисует связанные четырехугольники. Каждая пара вершин после первой пары будет задавать четырехугольник. Вершины 2n-1, 2n, 2n+2 и 2n+1 задают четырехугольник n. n/2-1 четырехугольников получится. Обратите внимание, что порядок, в котором вершины используются для построения четырехугольника из ленты данных отличается от используемого с независимыми данными.
304304
305305`GL_POLYGON`: рисует один выпуклый многоугольник. Вершины определяют этот полигон.
306306
307307![](/wiki-images/f/f3/SimpleShapePic1.jpg) Нарисуем один треугольник с помощью `GL_TRIANGLES`. Функция `glVertex3f` задаёт положение вершины. Есть еще glVertex* функции. Единственное отличие состоит в количестве и типе параметров, которые они принимают. Например, `glVertex2i` принимает два параметра (x и y) типа integer. `glVertex3f` это обычно то, что вам нужно.
308308
309309Перед `glVertex` можно установить цвет, материал, текстуру... Для простоты просто укажем цвет для каждой вершины. Цвет задаётся с помощью функции `glColor3f`. `glColor` также может принимать разные параметры, аналогично `glVertex`.
310310
311311В коде мы можем увидеть, что Z имеет значение 0 для всех вершин. Поскольку передний план установлен как 0.1, треугольник не будет видно. Тут как раз нужны те две функции, которые мы пропустили. Вы уже знаете, что функция `glLoadIdentity` сбрасывает матрицу. `glTranslatef` перемещает треугольник на X, Y и Z, которые вы зададите. Если установить Z как -5 (отрицательный Z будет находиться дальше от камеры), то все вершины будут на 5 единиц дальше от камеры, и треугольник будет видно.
312312
313313После завершения рисования вызовите функцию `glEnd`. Если хотите, то можете начать новое рисование с помощью `glBegin`.
314314
315315Скачать исходный код, исполняемые файлы linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
316316
317317## Использование списков отображения
318318
319319Иногда вам будет необходимо отобразить один и тот же объект на сцене несколько раз. OpenGL имеет возможность создавать **списки отображения**, которые также создают рисунок немного быстрее. Создать этот список очень легко. Просто задать вершины и заключить их между `glNewList` и `glEndList`.
320320
321321``` pascal
322322 const
323323 LIST_OBJECT = 1;
324324
325325 procedure CreateList;
326326 begin
327327 glNewList(LIST_OBJECT, GL_COMPILE);
328328 glBegin(GL_TRIANGLE_FAN);
329329 glColor3f(1, 0, 0);
330330 glVertex3f(0, 0.5, 0);
331331
332332 glColor3f(1, 1, 0);
333333 glVertex3f(-0.5, -0.5, 0.5);
334334
335335 glColor3f(1, 1, 1);
336336 glVertex3f(0.5, -0.5, 0.5);
337337
338338 glColor3f(0, 1, 1);
339339 glVertex3f(0.5, -0.5, -0.5);
340340
341341 glColor3f(0, 0, 1);
342342 glVertex3f(-0.5, -0.5, -0.5);
343343
344344 glColor3f(0, 1, 0);
345345 glVertex3f(-0.5, -0.5, 0.5);
346346 glEnd;
347347
348348 glBegin(GL_QUADS);
349349 glColor3f(1, 1, 0);
350350 glVertex3f(-0.5, -0.5, 0.5);
351351
352352 glColor3f(1, 1, 1);
353353 glVertex3f(0.5, -0.5, 0.5);
354354
355355 glColor3f(0, 1, 1);
356356 glVertex3f(0.5, -0.5, -0.5);
357357
358358 glColor3f(0, 0, 1);
359359 glVertex3f(-0.5, -0.5, -0.5);
360360
361361 glColor3f(0, 1, 0);
362362 glVertex3f(-0.5, -0.5, 0.5);
363363 glEnd;
364364 glEndList;
365365 end;
366366```
367367
368368`glNewList` создаёт новый список, и все функции рисования будут записаны, пока не вызовется `glEndList`.
369369
370370Первый параметр функции `glNewList` задаёт ID списка. Каждый список определяется по его идентификатору. Если список с заданным ID уже существует, то он очистится перед записью. Если второй параметр `GL_COMPILE`, то все функции рисования просто записываются, если это `GL_COMPILE_AND_EXECUTE`, то они записываются и выполняются автоматически.
371371
372372Функция `glIsList` поможет вам со списками. Она сообщает, существует ли список с таким ID.
373373
374374Еще одна полезная функция - это `glGenLists`. Он создает несколько пустых списков отображения. Вы задаёте количество нужных списков и получаете номер первого из них. Если вам нужно создать n списков и получить r ID, получатся списки: r, r+1, r+2,..., r+n-1
375375
376376Все созданные списки должны быть удалены. Для этого при выходе из программы вы должны сделать следующее:
377377
378378``` pascal
379379 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
380380 begin
381381 if Key = 27 then
382382 begin
383383 glDeleteLists(LIST_OBJECT, 1);
384384 Halt(0);
385385 end;
386386 end;
387387```
388388
389389`glDeleteLists` принимает 2 параметра, ID списка отображения и количество списков для удаления. Если ID - r и количество списков для удаления n, удаляются следующие листы: r, r+1, r+2,..., r+n-1
390390
391391Теперь вы знаете, как создавать и удалять списки отображения. Попробуем их применить:
392392
393393``` pascal
394394 procedure DrawGLScene; cdecl;
395395 begin
396396 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
397397
398398 glLoadIdentity;
399399 glTranslatef(-2, 0, -5);
400400 glRotatef(40, 1, 0, 1);
401401 glCallList(LIST_OBJECT);
402402
403403 glLoadIdentity;
404404 glTranslatef(1, -2, -10);
405405 glRotatef(62, 0, 1, 0);
406406 glCallList(LIST_OBJECT);
407407
408408 glLoadIdentity;
409409 glTranslatef(-4, 0.5, -15);
410410 glRotatef(200, 1, 0, 0);
411411 glCallList(LIST_OBJECT);
412412
413413 glutSwapBuffers;
414414 end;
415415```
416416
417417![](/wiki-images/a/a5/DisplayListsPic1.jpg) Используйте `glCallList` для рисования одного списка. Прежде, чем делать список отображения, вы меняли модели матрицы и рисовали объект в разных местах. Иногда нужно рисовать несколько списков одновременно. Это возможно при использовании функции `glCallLists`. Она принимает количество списков, которые вы хотите нарисовать, тип массива, в котором содержатся ID и массив с идентификаторами списков. Тип может принимать одно из следующих значений:
418418
419419`GL_BYTE`: список обрабатывается как массив байтов со знаком, каждый в диапазоне от -128 до 127.
420420
421421`GL_UNSIGNED_BYTE`: список обрабатывается как массив байтов без знака, каждый в диапазоне от 0 до 255.
422422
423423`GL_SHORT`: Список обрабатывается как массив двухбайтовых целых чисел со знаком, каждое из которых находится в диапазоне от -32768 до 32767.
424424
425425`GL_UNSIGNED_SHORT`: Список обрабатывается как массив двухбайтовых целых чисел без знака, каждое из которых находится в диапазоне от 0 до 65535.
426426
427427`GL_INT`: списки обрабатываются как массив четырехбайтовых целых чисел со знаком.
428428
429429`GL_UNSIGNED_INT`: Список обрабатывается как массив беззнаковых четырехбайтовых целых чисел.
430430
431431`GL_FLOAT`: Список обрабатывается как массив четырехбайтовых значений с плавающей точкой.
432432
433433`GL_2_BYTES`: Список обрабатывается как массив байтов без знака. Каждая пара байтов определяет один идентификатор списка отображения. Значение пары вычисляется как 256-кратное значение без знака первого байта плюс значение без знака второго байта.
434434
435435`GL_3_BYTES`: Список обрабатывается как массив байтов без знака. Каждый триплет байтов определяет один идентификатор списка отображения. Значение триплета вычисляется как 65536, умноженное на значение без знака первого байта, плюс 256 раз без знака без знака второго байта, плюс значение без знака третьего байта.
436436
437437`GL_4_BYTES`: Список обрабатывается как массив байтов без знака. Каждая четверка байтов задает один идентификатор списка отображения. Значение четверки вычисляется как 16777216 раз значение без знака первого байта, плюс 65536 раз значение без знака второго байта, плюс 256 раз значение без знака третьего байта, плюс значение без знака четвертого байта.
438438
439439Это пока. Следующий урок покажет, как создать маленькую планетную систему. Мы поговорим о матрицах и о том, как сделать анимированную сцену, которая не зависит от количества кадров в секунду.
440440
441441Скачать исходный код, исполняемые файлы linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
442442
443443## Анимация в полноэкраном режиме
444444
445445Переход в полноэкранный режим в GLUT прост. Измените основную часть программы:
446446
447447``` pascal
448448 const
449449 FSMode = '800x600:32@75';
450450
451451 begin
452452 glutInitPascal(False);
453453 glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
454454 glutGameModeString(FSMode);
455455 glutEnterGameMode;
456456 glutSetCursor(GLUT_CURSOR_NONE);
457457
458458 InitializeGL;
459459
460460 glutDisplayFunc(@DrawGLScene);
461461 glutReshapeFunc(@ReSizeGLScene);
462462 glutKeyboardFunc(@GLKeyboard);
463463 glutIdleFunc(@DrawGLScene);
464464
465465 glutMainLoop;
466466 end.
467467```
468468
469469Так как нам не нужно, чтобы GLUT разбирал командную строку, то на этот раз на glutInitPascal стави параметр false. Также не нужен код для создания окна. Функцией `glutEnterGameMode` GLUT автоматически создаст окно на весь экран. Чтобы указать желаемый режим, через функцию `glutGameModeString`передаём нужные параметры экрана. Format of that string is:
470470
471471Так как в этот раз мы не хотим, чтобы GLUT анализировал командную строку, мы вызываем `glutInitPascal` с параметром False. Как видите, нет кода для создания окна. GLUT имеет `glutEnterGameMode`, который создает полноразмерное окно. Чтобы указать, какой полноэкранный режим вы хотите, вы вызываете функцию `glutGameModeString`, которая принимает строку, которая определяет режим, который вам нравится.
472472
473473Формат этой строки:
474474
475475``` pascal
476476 [ширина "x" высота][":" цвет. палитра]["@" Гц]
477477```
478478
479479В строке FSMode мы задали разрешение экрана 800x600, 32-битнкю цветовую палитру и частоту обновления экрана 75Гц. Можно пропустить один из элементов. Если вы пропустите разрешение, GLUT будет пытаться использовать текущее или самый низкое из тех, которые можно установить. То же для других параметров.
480480
481481В строке `FSMode` мы объявили, что полноэкранный режим должен быть 800x600, с 32-битной палитрой и обновлением 75 Гц. Можно пропустить одну из групп. Если вы опустите размер, GLUT попытается использовать текущий или первый наименьший, который может работать. Эта политика используется и для других параметров.
482482
483483Обычно в полноэкранном режиме курсор не виден. Чтобы скрыть курсор, вы должны использовать функцию `glutSetCursor`. Она принимает только один параметр, который описывает курсор, который вы хотели бы видеть:
484484
485485``` pascal
486486 GLUT_CURSOR_RIGHT_ARROW
487487 GLUT_CURSOR_LEFT_ARROW
488488 GLUT_CURSOR_INFO
489489 GLUT_CURSOR_DESTROY
490490 GLUT_CURSOR_HELP
491491 GLUT_CURSOR_CYCLE
492492 GLUT_CURSOR_SPRAY
493493 GLUT_CURSOR_WAIT
494494 GLUT_CURSOR_TEXT
495495 GLUT_CURSOR_CROSSHAIR
496496 GLUT_CURSOR_UP_DOWN
497497 GLUT_CURSOR_LEFT_RIGHT
498498 GLUT_CURSOR_TOP_SIDE
499499 GLUT_CURSOR_BOTTOM_SIDE
500500 GLUT_CURSOR_LEFT_SIDE
501501 GLUT_CURSOR_RIGHT_SIDE
502502 GLUT_CURSOR_TOP_LEFT_CORNER
503503 GLUT_CURSOR_TOP_RIGHT_CORNER
504504 GLUT_CURSOR_BOTTOM_RIGHT_CORNER
505505 GLUT_CURSOR_BOTTOM_LEFT_CORNER
506506 GLUT_CURSOR_FULL_CROSSHAIR
507507 GLUT_CURSOR_NONE
508508 GLUT_CURSOR_INHERIT
509509```
510510
511511`glutIdleFunc` определяет функцию обратного вызова, которую вы захотите вызывать каждый раз, когда у вашей программы нет сообщений для обработки. Так как мы просто хотим рендерить новый кадр, если ничего не нужно делать, просто установите функцию ожидания в `DrawGLScene`. Некоторые другие учебные пособия показывают, что функция бездействия должна отправлять сообщение об обновлении вместо рисования, но в этом случае у меня на 50-100 кадров меньше, чем при использовании описанного мной метода.
512512
513513Теперь давайте посмотрим на завершение программы, где вам нужно выйти из полноэкранного режима:
514514
515515``` pascal
516516 procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
517517 begin
518518 if Key = 27 then
519519 begin
520520 glutLeaveGameMode;
521521 Halt(0);
522522 end;
523523 end;
524524```
525525
526526Как видите, все, что вам нужно сделать, это вызвать `glutLeaveGameMode`.
527527
528528Теперь мы представим некоторые новые матричные функции. Во-первых, давайте изменим функцию `ReSizeGLScene`:
529529
530530``` pascal
531531 procedure ReSizeGLScene(Width, Height: Integer); cdecl;
532532 begin
533533 .
534534 .
535535 .
536536
537537 glMatrixMode(GL_MODELVIEW);
538538 glLoadIdentity;
539539 gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
540540 end;
541541```
542542
543543`gluLookAt` создает матрицу, которая будет определять, откуда вы смотрите на объекты. Первые 3 параметра - это координаты X, Y и Z положения камеры. Следующие 3 параметра - это координаты X, Y и Z точки, на которую смотрит камера, а последние 3 параметра определяют вектор «вверх» (где «вверх» для камеры). Обычно up - это положительная ось y.
544544
545545Хорошо, давайте сейчас порисуем. Поскольку вы устанавливаете матрицу с помощью `gluLookAt`, которая должна использоваться со всеми объектами, вы не можете просто использовать `glLoadIdentity` для сброса матрицы для следующего объекта ... вы сохраните предыдущее состояние матрицы и восстановите его после отрисовки объекта:
546546
547547``` pascal
548548 procedure DrawGLScene; cdecl;
549549 var
550550 T: Single;
551551 begin
552552 T := glutGet(GLUT_ELAPSED_TIME) / 1000;
553553 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
554554
555555 glPushMatrix;
556556 glRotatef(5 * T, 0, 1, 0);
557557 glColor3f(1, 1, 0);
558558 glutWireSphere(2, 20, 20);
559559 glPopMatrix;
560560
561561 glPushMatrix;
562562 glRotatef(90 * T, 0, 1, 0);
563563 glTranslatef(5, 0, 0);
564564 glRotatef(40 * T, 0, 1, 0);
565565 glColor3f(1, 0, 0);
566566 glutWireSphere(0.6, 10, 10);
567567 glPopMatrix;
568568
569569 glPushMatrix;
570570 glRotatef(60 * T, 0, 1, 0);
571571 glTranslatef(-3, 0, 9);
572572 glRotatef(50 * T, 0, 1, 0);
573573 glColor3f(0, 1, 0);
574574 glutWireSphere(1, 16, 16);
575575
576576 glPushMatrix;
577577 glRotatef(360 * T, 0, 1, 0);
578578 glTranslatef(-1.7, 0, 0);
579579 glRotatef(50 * T, 0, 1, 0);
580580 glColor3f(0, 0, 1);
581581 glutWireSphere(0.4, 10, 10);
582582 glPopMatrix;
583583
584584 glPopMatrix;
585585
586586 glutSwapBuffers;
587587 end;
588588```
589589
590590![](/wiki-images/5/51/FullScreenAnimationPic1.jpg)
591591
592592`glPushMatrix` и `glPopMatrix` используются для сохранения и восстановления состояния матрицы. Как видите, мы сохраняем состояние матрицы, затем меняем матрицу, чтобы нарисовать объект в нужном месте, а затем восстанавливаем старое состояние матрицы.
593593
594594Вы можете спросить, для чего нужна переменная `T`? Ну, она используется для определения скорости анимации. Каждое изменение, зависящее от времени, умножается на `T`. Таким образом, скорость анимации постоянна на каждой частоте кадров.
595595
596596Функция `glutGet` с параметром `GLUT_ELAPSED_TIME` возвращает время в миллисекундах из `glutInit`. Разделив ее значение на 1000, мы получим время в секундах.
597597
598598Функция `glRotatef` создает матрицу вращения. Первый параметр - это угол в градусах, а последние 3 параметра определяют ось, вокруг которой будет выполняться вращение. Поскольку вы умножили угол на `T`, объект будет повернут на этот угол ровно за 1 секунду.
599599
600600Загрузите исходный код, исполняемый файл linux или исполняемый файл windows из [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
601601
602602## Освещение
603603
604604Этот урок познакомит вас с освещением сцены. Вы сделаете вращающийся куб и один источник света, который добавит реалистичности сцене, но сначала давайте сделаем несколько вспомогательных модулей.
605605
606606На данный момент урок будет иметь только базовые функции, которые помогут нам получить текущее время и дельту (время, прошедшее от одного рендеринга до другого вызова рендеринга), и рассчитать количество кадров в секунду.
607607
608608``` pascal
609609 unit utils;
610610
611611 {$mode objfpc}{$H+}
612612
613613 interface
614614
615615 uses
616616 glut;
617617
618618 function GetTotalTime: Single;
619619 function GetDeltaTime: Single;
620620 procedure FrameRendered(Count: Integer = 1);
621621 function GetFPS: Single;
622622
623623 implementation
624624
625625 var
626626 OldTime: Integer = 0;
627627 FPSTime: Integer = 0;
628628 FPSCount: Integer = 0;
629629
630630 function GetTotalTime: Single;
631631 begin
632632 Result := glutGet(GLUT_ELAPSED_TIME) / 1000;
633633 end;
634634
635635 function GetDeltaTime: Single;
636636 var
637637 NewTime: Integer;
638638 begin
639639 NewTime := glutGet(GLUT_ELAPSED_TIME);
640640 Result := (NewTime - OldTime) / 1000;
641641 OldTime := NewTime;
642642 end;
643643
644644 procedure FrameRendered(Count: Integer);
645645 begin
646646 Inc(FPSCount, Count);
647647 end;
648648
649649 function GetFPS: Single;
650650 var
651651 NewTime: Integer;
652652 begin
653653 NewTime := glutGet(GLUT_ELAPSED_TIME);
654654
655655 Result := FPSCount / ((NewTime - FPSTime) / 1000);
656656
657657 FPSTime := NewTime;
658658 FPSCount := 0;
659659 end;
660660
661661 end.
662662```
663663
664664Как видите, в этом модуле нет ничего сложного. Время просто сохраняется между вызовами, а разница возвращается. FrameRendered должен вызываться каждый раз, когда вы рисуете сцену, чтобы функция могла рассчитывать FPS.
665665
666666Теперь давайте повеселимся с освещением. В OpenGL есть несколько типов света: ambient (окружающий), diffuse (рассеянный), point(точечный), spot (пятно), specular (зеркальный) и emissive (излучение).
667667
668668Окружающий свет (ambient) - это что-то вроде Солнца. Когда солнечные лучи проходят через окно комнаты, они ударяются о стены и отражаются во всех направлениях, что в среднем осветляет всю комнату. Все вершины освещены окружающим светом.
669669
670670Рассеянный свет(diffuse) может быть представлен как параллельные световые лучи, исходящие издалека. Они будут освещать только вершины, ориентированные на источник света.
671671
672672Точечный свет(point) освещает все вокруг себя. Он похож на огненный шар, он посылает световые лучи вокруг себя и освещает вершины, которые ориентированы на источник света и достаточно близки.
673673
674674Пятно (spot) - как свет от фонарика. Это просто точечный источник света с небольшим радиусом светового конуса. Все вершины, которые попадают внутрь конуса и находятся достаточно близко, подсвечиваются.
675675
676676Зеркальный свет (specular), как и рассеянный свет, является направленным типом света. Это происходит из одного конкретного направления. Разница между ними заключается в том, что зеркальный свет отражается от поверхности острым и равномерным образом. Рендеринг зеркального света зависит от угла между зрителем и источником света. С точки зрения зрителя зеркальный свет создает выделенную область на поверхности наблюдаемого объекта, известную как зеркальное выделение или зеркальное отражение.
677677
678678Излучение (emissive) - свет немного отличается от любых других ранее объясненных компонентов света. Этот свет исходит от объекта, который вы рисуете, но не освещаете другие объекты поблизости.
679679
680680Для простоты мы будем использовать только рассеянный свет в этом уроке. Позже, некоторые другие источники света могут появиться в уроках :)
681681
682682Давайте посмотрим, как включить освещение в сцене:
683683
684684``` pascal
685685 const
686686 DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
687687
688688 glEnable(GL_LIGHTING);
689689 glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
690690 glEnable(GL_LIGHT0);
691691```
692692
693693Как видите, мы включаем подсветку в OpenGL, чтобы свет влиял на сцену, которую вы рендерили. Параметры освещения задаются функцией `glLightfv`. Потребуется 3 параметра ... один для числа освещения, которое вы хотите изменить (OpenGL поддерживает до 8 источников света), другой сообщает OpenGL, какой параметр освещения изменить, а последний - новый параметр для освещения. В этом уроке вы установите только рассеянный свет для освещения. После этого вы можете включить освещение, и на сцене будет свет ... но ... это еще не все.
694694
695695Подробнее о `glLightfv`: <http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html>
696696
697697Если вы хотите использовать источники света, вы не можете просто задать цвет для вершины ... вы также должны задать и материал для вершин. Давайте настроим материал для рисования:
698698
699699``` pascal
700700 glEnable(GL_COLOR_MATERIAL);
701701 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
702702```
703703
704704![](/wiki-images/e/ed/LightPic1.jpg)
705705
706706Вы ожидали чего-то более сложного? :) Ну, этот код позволит нам использовать функцию `glColor`, чтобы задать материал для вершин. Используя функцию `glEnable` и флаг `GL_COLOR_MATERIAL`, вы можете определить, какие свойства материала будут изменять `glColor`. `glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE)` сообщает OpenGL, что `glColor` изменяет окружающий и рассеянный материал. Мы обсудим материалы больше в следующих уроках.
707707
708708Еще одна вещь, которая важна при использовании источников света ... каждая вершина должна иметь нормаль, связанную с ней. Нормаль используется для определения направления вершины, чтобы свет можно было правильно рассчитать. Вы будете использовать функцию GLUT, чтобы нарисовать куб, и он предоставит нам нормали, так что на этот раз мы просто пройдемся по нормали.
709709
710710После всех этих настроек, свет будет сиять в вашем кубе :)
711711
712712Часть текста скопирована из [The OpenGL Light Bible](http://www.falloutsoftware.com/tutorials/gl/gl8.htm)
713713
714714Скачать исходный код, исполняемые файлы linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
715715
716716## Растровые шрифты
717717
718718Игры и программы, как правило, должны писать текст на экране. GLUT предоставляет несколько функций, которые не зависят от платформы, для рисования символов.
719719
720720Сначала мы покажем, как использовать стандартные растровые шрифты. Почти все дополнения кода будут сделаны в модуле utils.pas.
721721
722722Поскольку текст будет отображаться в 2D, нам нужно знать ширину и высоту области просмотра ... поэтому мы напишем две функции для этого:
723723
724724``` pascal
725725 function glGetViewportWidth: Integer;
726726 var
727727 Rect: array[0..3] of Integer;
728728 begin
729729 glGetIntegerv(GL_VIEWPORT, @Rect);
730730 Result := Rect[2] - Rect[0];
731731 end;
732732
733733 function glGetViewportHeight: Integer;
734734 var
735735 Rect: array[0..3] of Integer;
736736 begin
737737 glGetIntegerv(GL_VIEWPORT, @Rect);
738738 Result := Rect[3] - Rect[1];
739739 end;
740740```
741741
742742Мы просто получаем left/right, top/bottom и вычисляем width/height, вычитая их.
743743
744744Должны быть функции для входа и выхода из режима 2D:
745745
746746``` pascal
747747 procedure glEnter2D;
748748 begin
749749 glMatrixMode(GL_PROJECTION);
750750 glPushMatrix;
751751 glLoadIdentity;
752752 gluOrtho2D(0, glGetViewportWidth, 0, glGetViewportHeight);
753753
754754 glMatrixMode(GL_MODELVIEW);
755755 glPushMatrix;
756756 glLoadIdentity;
757757
758758 glDisable(GL_DEPTH_TEST);
759759 end;
760760
761761 procedure glLeave2D;
762762 begin
763763 glMatrixMode(GL_PROJECTION);
764764 glPopMatrix;
765765 glMatrixMode(GL_MODELVIEW);
766766 glPopMatrix;
767767
768768 glEnable(GL_DEPTH_TEST);
769769 end;
770770```
771771
772772При входе в режим 2D мы сохраняем текущие матрицы и устанавливаем 2D-матрицу с помощью функции `gluOrtho2D`. Таким образом, если мы нарисуем какую-либо штуку в координатах (100,100), она будет нарисована точно на 100 пикселей от левого края окна и на 100 пикселей от нижнего края формы (положительные значения по оси Y направлены вверх). Также мы отключаем ZBuffer. Таким образом, текст не изменит ZBuffer.
773773
774774Выход из режима 2D просто возвращает старые матрицы и включает ZBuffer.
775775
776776Теперь мы можем создать функцию для рисования текста:
777777
778778``` pascal
779779 procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
780780 var
781781 I: Integer;
782782 begin
783783 glRasterPos2f(X, Y);
784784 for I := 1 to Length(Text) do
785785 glutBitmapCharacter(Font, Integer(Text[I]));
786786 end;
787787```
788788
789789`glutBitmapCharacter` может рисовать только один символ выбранного шрифта. Первый параметр - желаемый шрифт (GLUT_BITMAP_9_BY_15, GLUT_BITMAP_8_BY_13, GLUT_BITMAP_TIMES_ROMAN_10, GLUT_BITMAP_TIMES_ROMAN_24, GLUT_BITMAP_HELVETICA_10, GLUT_BITMAP_HELVETICA_12 или GLUT_BITMAP_HELVETICA_18) и другие - являются символом.
790790
791791Символ будет нарисован в текущей позиции растра. Чтобы установить желаемую позицию растра, мы вызываем функцию `glRasterPos`. `glRasterPos` может обрабатывать различные количества и типы параметров так же, как функция `glVertex`. Указанная координата преобразуется моделью и матрицей проекции, чтобы получить двухмерную координату, в которой будет находиться новая позиция растра. Поскольку мы вошли в режим 2D, координаты X и Y являются фактическими координатами 2D, в которых будет происходить рисование.
792792
793793Эти новые функции сделают рисование текста очень простым:
794794
795795``` pascal
796796 procedure DrawGLScene; cdecl;
797797 begin
798798 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
799799
800800 glLoadIdentity;
801801 glTranslatef(0, 0, -5);
802802 glRotatef(GetTotalTime * 10, 0, 0.5, 0.5);
803803
804804 glColor3f(1, 0, 0);
805805 glutSolidCube(2);
806806
807807 glEnter2D;
808808
809809 glColor3f(0.2, 0.8 + 0.2 * Sin(GetTotalTime * 5), 0);
810810 glWrite(20, glGetViewportHeight - 20, GLUT_BITMAP_8_BY_13,
811811 Format('OpenGL Tutorial :: Bitmap Fonts :: FPS - %.2f FPS', [FPS]));
812812
813813 glColor3f(1, 1, 1);
814814 glWrite(50, glGetViewportHeight - 60, GLUT_BITMAP_9_BY_15, 'GLUT_BITMAP_9_BY_15');
815815 glWrite(50, glGetViewportHeight - 90, GLUT_BITMAP_8_BY_13, 'GLUT_BITMAP_8_BY_13');
816816 glWrite(50, glGetViewportHeight - 120, GLUT_BITMAP_TIMES_ROMAN_10, 'GLUT_BITMAP_TIMES_ROMAN_10');
817817 glWrite(50, glGetViewportHeight - 150, GLUT_BITMAP_TIMES_ROMAN_24, 'GLUT_BITMAP_TIMES_ROMAN_24');
818818 glWrite(50, glGetViewportHeight - 180, GLUT_BITMAP_HELVETICA_10, 'GLUT_BITMAP_HELVETICA_10');
819819 glWrite(50, glGetViewportHeight - 210, GLUT_BITMAP_HELVETICA_12, 'GLUT_BITMAP_HELVETICA_12');
820820 glWrite(50, glGetViewportHeight - 240, GLUT_BITMAP_HELVETICA_18, 'GLUT_BITMAP_HELVETICA_18');
821821
822822 glColor3f(0.5, 0.5, 1);
823823 glWrite(
824824 glGetViewportWidth - glutBitmapLength(GLUT_BITMAP_9_BY_15, LazText) - 5,
825825 10, GLUT_BITMAP_9_BY_15, LazText);
826826
827827 glLeave2D;
828828
829829 glutSwapBuffers;
830830
831831 FrameRendered;
832832 end;
833833```
834834
835835![](/wiki-images/b/ba/BitmapFontsPic1.jpg)
836836
837837Мы рисуем красный куб и поворачиваем его, а также немного текста, чтобы показать, как выглядят различные растровые шрифты. Функция `glutBitmapLength` используется для определения ширины строки, чтобы ее можно было выровнять вправо. Код может быть легко изменен для выравнивая текста по центру.
838838
839839> **Note:** Посмотрите, как куб выглядит без света.
840840
841841Загрузите исходный код, исполняемый файл под Linux или Windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
842842
843843## Текстуры
844844
845845Пришло время использовать текстуры :)
846846
847847Этот урок покажет, как рисовать текстурированные полигоны и как смешивать текстуры, используя технику многопроходности. Поскольку в OpenGL нет встроенного механизма загрузки текстур, мы будем использовать внешнюю библиотеку: [Vampyre Imaging Library](http://imaginglib.sourceforge.net/). Мы будем использовать только вспомогательные функции OpenGL, но вам может пригодиться эта библиотека для некоторых других целей.
848848
849849Давайте начнем ... мы создадим список отображения для рисования текстурированного прямоугольника:
850850
851851``` pascal
852852 procedure CreateList;
853853 begin
854854 glNewList(LIST_OBJECT, GL_COMPILE);
855855 glBegin(GL_QUADS);
856856 glTexCoord2f(1, 0);
857857 glVertex3f( 2, 2, 0);
858858 glTexCoord2f(0, 0);
859859 glVertex3f(-2, 2, 0);
860860 glTexCoord2f(0, 1);
861861 glVertex3f(-2,-2, 0);
862862 glTexCoord2f(1, 1);
863863 glVertex3f( 2,-2, 0);
864864 glEnd;
865865 glEndList;
866866 end;
867867```
868868
869869Обратите внимание на функции `glTexCoord`. Они используются, чтобы указать, какая часть текстуры назначена вершине. Координаты, определенные в этих функциях, имеют значения от 0 до 1 (допустимы значения больше 1, но они могут давать разные результаты). 0 - это первый пиксель, а 1 - последний. Таким образом, 0.5 будет прямо в середине текстуры.
870870
871871Загрузка текстур чрезвычайно проста с Vampyre Imaging Library:
872872
873873``` pascal
874874 var
875875 Tex1, Tex2: GLuint;
876876
877877 procedure InitializeGL;
878878 begin
879879 glClearColor(0, 0, 0, 0);
880880 Tex1 := LoadGLTextureFromFile('ashwood.bmp');
881881 Tex2 := LoadGLTextureFromFile('Flare.bmp');
882882 glEnable(GL_TEXTURE_2D);
883883 end;
884884```
885885
886886`LoadGLTextureFromFile` загружает текстуру из файла и возвращает ее ID. Когда текстура загружена, она уже настроена на рендеринг. Последняя строка просто включает 2D текстуры.
887887
888888Чтобы нарисовать текстурированный многоугольник, вы должны связать текстуру и установить координаты текстуры (координаты текстуры задаются в списке отображения в этом уроке):
889889
890890``` pascal
891891 ...
892892 glLoadIdentity;
893893 glTranslatef(-5, 0, -15);
894894 glBindTexture(GL_TEXTURE_2D, Tex1);
895895 glCallList(LIST_OBJECT);
896896 ...
897897```
898898
899899Функция `glBindTexture` используется для выбора текстуры. Когда вы рисуете полигоны, на них будет выделена текстура. Это так просто :)
900900
901901Итак, использовать одну текстуру легко ... но как смешать две текстуры? Обычно вы рисуете полигон один раз с одной текстурой, настраиваете параметры смешивания и еще раз рисуете полигон с другой текстурой. Таким способом вы можете смешивать текстуры. Давайте посмотрим, как выглядит код для этого:
902902
903903``` pascal
904904 ...
905905 glLoadIdentity;
906906 glTranslatef(5, 0, -15);
907907 glBindTexture(GL_TEXTURE_2D, Tex1);
908908 glCallList(LIST_OBJECT);
909909
910910 glEnable(GL_BLEND);
911911 glBlendFunc(GL_ZERO, GL_SRC_COLOR);
912912 glLoadIdentity;
913913 glTranslatef(5, 0, -15);
914914 glBindTexture(GL_TEXTURE_2D, Tex2);
915915 glCallList(LIST_OBJECT);
916916 glDisable(GL_BLEND);
917917 ...
918918```
919919
920920Как видите, полигон рисуется впервые, как мы уже знаем. Перед вторым рисованием мы включаем смешивание, вызывая `glEnable(GL_BLEND)`. Смешивание означает, что конечный цвет пикселя рассчитывается следующим образом:
921921
922922``` pascal
923923 DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND
924924```
925925
926926`SRCBLEND` и `DESTBLEND` определяются с помощью функции `glBlendFunc`. В этом уроке мы устанавливаем для `SRCBLEND` значение `GL_ZERO` (ноль) и для `DESTBLENT` значение `GL_SRC_COLOR (DrawingColor)`, а затем окончательный цвет:
927927
928928``` pascal
929929 DrawingColor * 0 + BackgroundColor * DrawingColor
930930```
931931
932932![](/wiki-images/d/d4/TexturesPic1.jpg)
933933
934934Это означает, что фон будет темнее, когда вы рисуете темными цветами ... когда вы рисуете белым цветом, цвет фона не изменится. Результат будет выглядеть следующим образом.
935935
936936В следующий раз мы будем использовать расширения, чтобы показать, как использовать однопроходное мультитекстурирование.
937937
938938Загрузите исходный код, исполняемый файл для linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
939939
940940## Мультитекстурирование (расширения)
941941
942942Когда вы уже знаете многопроходное мультитекстурирование, однопроходный процесс очень прост. Текстурирование разделено на этапы. Первый этап настраивает и рисует первую текстуру, второй этап рисует еще одну и так далее. Все, что вам нужно сделать, это настроить этапы текстуры и визуализации объекта.
943943
944944Давайте посмотрим, как выглядит код:
945945
946946``` pascal
947947 procedure InitializeGL;
948948 begin
949949 Load_GL_ARB_multitexture;
950950 glClearColor(0, 0, 0, 0);
951951 Tex1 := LoadGLTextureFromFile('Lazarus.bmp');
952952 Tex2 := LoadGLTextureFromFile('Mask.bmp');
953953 glActiveTextureARB(GL_TEXTURE0_ARB);
954954 glEnable(GL_TEXTURE_2D);
955955 glBindTexture(GL_TEXTURE_2D, Tex1);
956956 glActiveTextureARB(GL_TEXTURE1_ARB);
957957 glEnable(GL_TEXTURE_2D);
958958 glBindTexture(GL_TEXTURE_2D, Tex2);
959959 end;
960960```
961961
962962Для начала нам нужно загрузить расширение OpenGL, которое позволит нам использовать мультитекстурные функции. `Load_GL_ARB_multitexture` попытается загрузить эти расширения и вернет TRUE, если операция прошла успешно.
963963
964964Чтобы выбрать стадию текстуры, над которой вы хотите работать, используйте функцию `glActiveTextureARB`. Она принимает только один параметр, который определяет, какая стадия вам нужна. После этого все текстурные функции (включение, отключение, связывание, создание ...) будут влиять на этот этап.
965965
966966Поскольку мы настраиваем все в функции инициализации, все, что нам нужно сделать, это нарисовать объект:
967967
968968``` pascal
969969 procedure DrawGLScene; cdecl;
970970 begin
971971 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
972972
973973 glLoadIdentity;
974974 glTranslatef(0, 0, -5);
975975
976976 glBegin(GL_QUADS);
977977 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
978978 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
979979 glVertex3f(2.516, 2, 0);
980980 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
981981 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
982982 glVertex3f(-2.516, 2, 0);
983983 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
984984 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
985985 glVertex3f(-2.516,-2, 0);
986986 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
987987 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
988988 glVertex3f(2.516,-2, 0);
989989 glEnd;
990990
991991 glutSwapBuffers;
992992 end;
993993```
994994
995995![](/wiki-images/5/52/MultitexturePic1.jpg) Как видите, разница только в определении текстурных координат. Теперь мы используем функцию `glMultiTexCoord2fARB`, которая принимает стадию текстуры и координаты текстуры. Все остальное без изменений.
996996
997997Сегодня почти все графические карты поддерживают как минимум 2 стадии текстур. Использование однопроходного мультитекстурирования выполняется быстрее, чем многопроходная версия, поскольку объекты рисуются только один раз за раз. Если оборудование поддерживает однопроходное мультитекстурирование (`Load_GL_ARB_multitexture` возвращает TRUE), используйте его.
998998
999999Загрузите исходный код, исполняемый файл под linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
10001000
10011001## Рендеринг в текстуру
10021002
10031003Этот будет кратко. OpenGL может захватывать текущую сцену в текстуру, чтобы вы могли использовать ее для текстурирования других объектов (экран телевизора, зеркало или что-то еще). Итак, просто визуализируйте сцену в текстуру и примените ее к вращающейся плоскости.
10041004
10051005Сначала мы должны создать пустую текстуру, которую мы будем использовать для захвата сцены:
10061006
10071007``` pascal
10081008 procedure SetupRenderTexture;
10091009 var
10101010 Data: Pointer;
10111011 begin
10121012 GetMem(Data, 256*256*3);
10131013 glGenTextures(1, @RenderTexture);
10141014 glBindTexture(GL_TEXTURE_2D, RenderTexture);
10151015 glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, Data);
10161016 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
10171017 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
10181018 FreeMem(Data);
10191019 end;
10201020```
10211021
10221022Создается буфер для изображения размером 256 * 256 RGB, который используется для настройки 2D-текстуры.
10231023
10241024Основная часть - в функции рисования:
10251025
10261026``` pascal
10271027 procedure DrawGLScene; cdecl;
10281028 var
10291029 TotalTime: Single;
10301030 begin
10311031 glClearColor(0, 0, 0, 0);
10321032 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
10331033 glEnable(GL_LIGHTING);
10341034 glDisable(GL_TEXTURE_2D);
10351035 glViewport(0, 0, 256, 256);
10361036
10371037 TotalTime := GetTotalTime;
10381038
10391039 glLoadIdentity;
10401040 glTranslatef(0, 0, -5);
10411041 glRotatef(50 * TotalTime, 1, 0, 0);
10421042 glRotatef(100 * TotalTime, 0, 1, 0);
10431043 glRotatef(50 * TotalTime, 0, 0, 1);
10441044
10451045 glColor3f(1, 1, 1);
10461046 glutSolidCube(2);
10471047
10481048 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);
10491049
10501050 glClearColor(0.18, 0.20, 0.66, 0);
10511051 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
10521052 glDisable(GL_LIGHTING);
10531053 glEnable(GL_TEXTURE_2D);
10541054 glViewport(0, 0, AppWidth, AppHeight);
10551055
10561056 glLoadIdentity;
10571057 glTranslatef(0, 0, -7);
10581058 glRotatef(20 * TotalTime, 1, 0, 0);
10591059 glRotatef(50 * TotalTime, 0, 1, 0);
10601060
10611061 glBegin(GL_QUADS);
10621062 glTexCoord2f(1, 0);
10631063 glVertex3f(2, 2, 0);
10641064 glTexCoord2f(0, 0);
10651065 glVertex3f(-2, 2, 0);
10661066 glTexCoord2f(0, 1);
10671067 glVertex3f(-2,-2, 0);
10681068 glTexCoord2f(1, 1);
10691069 glVertex3f(2,-2, 0);
10701070 glEnd;
10711071
10721072 glutSwapBuffers;
10731073 end;
10741074```
10751075
10761076![](/wiki-images/6/6e/RenderToTexturePic1.jpg) Во-первых, все настроено для сцены, которая будет захвачена. Окно просмотра уменьшено до 256*256, поэтому оно будет вписываться в текстуру, и сцена будет прорисована. `glCopyTexImage2D` используется для захвата сцены в текущую выбранную текстуру.
10771077
10781078Когда у нас есть сцена, захваченная в текстуру, все может быть очищено снова, область просмотра может быть возвращена к исходному размеру, а финальная сцена рисуется с использованием предыдущей сцены в качестве текстуры.
10791079
10801080> **Note:** Захваченную текстуру можно сохранить с помощью функции `SaveGLTextureToFile` из [Vampyre Imaging Library](http://imaginglib.sourceforge.net/).
10811081
10821082Загрузите исходный код, исполняемый файл для linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
10831083
10841084## Массив вершин
10851085
10861086OpenGL способен отображать примитивы, используя данные, которые хранятся в буферах, вызванных вызовом `glVertex`. Буферы могут использоваться для определения координат вершин и текстур, а также цветов (индекс и RGBA), нормалей и флагов ребер.
10871087
10881088В этом уроке будут использоваться только вершинные и цветовые буферы, и мы покажем неиндексированное и индексированное рисование. Неиндексированный режим рисует буферы в виде потоков. Индексированный режим будет рисовать элементы буфера в порядке, определенном в индексе буфера. Но хватит говорить ... давайте начнем кодить.
10891089
10901090Для начала давайте определим некоторые типы и константы:
10911091
10921092``` pascal
10931093 type
10941094 TVertex3f = record
10951095 X, Y, Z: Single;
10961096 end;
10971097
10981098 TColor3f = record
10991099 R, G, B: Single;
11001100 end;
11011101
11021102 VertexBuffer: array [0..5] of TVertex3f = (
11031103 (X : 1; Y : 1; Z : 0),
11041104 (X : -1; Y : 1; Z : 0),
11051105 (X : -1; Y : -1; Z : 0),
11061106 (X : 1; Y : 1; Z : 0),
11071107 (X : -1; Y : -1; Z : 0),
11081108 (X : 1; Y : -1; Z : 0)
11091109 );
11101110 ColorBuffer: array [0..5] of TColor3f = (
11111111 (R : 1; G : 0; B : 1),
11121112 (R : 0; G : 0; B : 1),
11131113 (R : 0; G : 1; B : 0),
11141114 (R : 1; G : 0; B : 1),
11151115 (R : 0; G : 1; B : 0),
11161116 (R : 1; G : 1; B : 0)
11171117 );
11181118```
11191119
11201120У нас есть два буфера. Один для координат вершин и один для цветов вершин. Эти 6 вершин определяют 2х треугольников, которые образуют прямоугольник.
11211121
11221122Рисовать примитивы с помощью буферов легко:
11231123
11241124``` pascal
11251125 glEnableClientState(GL_VERTEX_ARRAY);
11261126 glEnableClientState(GL_COLOR_ARRAY);
11271127 glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);
11281128 glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);
11291129
11301130 glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));
11311131
11321132 glDisableClientState(GL_VERTEX_ARRAY);
11331133 glDisableClientState(GL_COLOR_ARRAY);
11341134```
11351135
11361136Сначала мы включаем буферы, которые мы хотим использовать, используя функцию `glEnableClientState`. Затем мы можем выбрать буферы, которые мы хотим использовать. Каждый тип буфера имеет собственную функцию для выбора (`glColorPointer`, `glEdgeFlagPointer`, `glIndexPointer`, `glNormalPointer`, `glTexCoordPointer`, `glVertexPointer`).
11371137
11381138Первый параметр в этих функциях определяет, сколько чисел содержит каждый элемент. Например, давайте возьмем буфер вершин. Если этот параметр равен 2, то OpenGL ожидает, что каждый элемент в буфере содержит координаты x и y. Если этот параметр равен, например, 4, то каждый элемент должен содержать координаты x, y, z и w. Следующий параметр определяет, какой тип элемента данных содержит (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT или GL_DOUBLE). Далее определяется количество байтов между каждым элементом. Таким образом, вы можете иметь буфер, который содержит координаты вершины и некоторые пользовательские данные.
11391139
11401140Для произвольного типа данных этот параметр может быть рассчитан следующим образом:
11411141
11421142``` pascal
11431143 type
11441144 TBufferData = record
11451145 DataBefore: TDataBefore;
11461146 Vertex: TVertex;
11471147 DataAfter: TDataAfter;
11481148 end;
11491149```
11501150
1151-`Байты между элементами = SizeOf(TDataBefore) + SizeOf(TDataAfter)`
1151+```text
1152+Байты между элементами = SizeOf(TDataBefore) + SizeOf(TDataAfter)
1153+```
11521154
11531155Последний параметр - указатель на начало буфера.
11541156
11551157Когда буферы выбраны, мы можем нарисовать их, используя функции `glDrawArrays`. Все включенные буферы используются для рисования примитивов. Тип создаваемых полигонов определяется в первом параметре (так же, как в функции `glBegin`). Следующие два определяют подмножество буфера, который используется для рисования (start и count).
11561158
11571159Когда буферы не нужны, вы можете отключить их.
11581160
11591161Чтобы продемонстрировать индексированный режим, я создал простой класс сетки, который может загружать данные вершин, цветов и индексов из внешних файлов:
11601162
11611163``` pascal
11621164 type
11631165 TMesh = class
11641166 private
11651167 FVertices: array of TVertex3f;
11661168 FColors: array of TColor3f;
11671169 FIndices: array of Integer;
11681170 procedure FreeBuffers;
11691171 public
11701172 constructor Create;
11711173 destructor Destroy; override;
11721174 procedure LoadMesh(FileName: String);
11731175 procedure DrawMesh;
11741176 end;
11751177```
11761178
11771179Поле FVertices будет содержать данные о вершинах, поле FColors - данные о цвете, и поле FIndices - данные об индексах при загрузке внешнего файла.
11781180
11791181Сначала мы напишем некоторый код, который занимается созданием и уничтожением класса:
11801182
11811183``` pascal
11821184 procedure TMesh.FreeBuffers;
11831185 begin
11841186 FVertices := nil;
11851187 FColors := nil;
11861188 FIndices := nil;
11871189 end;
11881190
11891191 constructor TMesh.Create;
11901192 begin
11911193 FreeBuffers;
11921194 end;
11931195
11941196 destructor TMesh.Destroy;
11951197 begin
11961198 FreeBuffers;
11971199 inherited Destroy;
11981200 end;
11991201```
12001202
12011203Файл, который будет содержать данные сетки, является простым текстовым файлом. Первая строка будет содержать количество вершин и индексов, разделенных пробелом. После этого ряда появятся строки для каждой вершины и цвета. X, Y, Z, R, G и B все разделены пробелом. В конце будут строки для индексов ... каждый номер индекса будет записан в своей собственной строке ... поэтому для одного треугольника файл данных будет выглядеть примерно так:
12021204
12031205``` pascal
12041206 3 3
12051207 -1 -1 0 1 1 1
12061208 1 -1 0 1 1 1
12071209 0 1 0 1 1 1
12081210 0
12091211 1
12101212 2
12111213```
12121214
12131215Это означает, что в файле определены 3 вершины и 3 индекса. Первая вершина имеет значения -1, -1, 0 и цвет, имеющий значения 1, 1, 1 и т.д. Индексы определяют тот порядок, в котором рисуются вершины (в этом случае вершины рисуются в том же порядке, в котором они определены).
12141216
12151217Код для загрузки этих данных будет выглядеть так:
12161218
12171219``` pascal
12181220 procedure TMesh.LoadMesh(FileName: String);
12191221 var
12201222 MeshFile: TextFile;
12211223 VertexCount, IndexCount: Integer;
12221224 iV, iI: Integer;
12231225 begin
12241226 FreeBuffers;
12251227
12261228 AssignFile(MeshFile, FileName);
12271229 Reset(MeshFile);
12281230
12291231 ReadLn(MeshFile, VertexCount, IndexCount);
12301232
12311233 SetLength(FVertices, VertexCount);
12321234 SetLength(FColors, VertexCount);
12331235 SetLength(FIndices, IndexCount);
12341236
12351237 for iV := 0 to VertexCount - 1 do
12361238 ReadLn(MeshFile,
12371239 FVertices[iV].X, FVertices[iV].Y, FVertices[iV].Z,
12381240 FColors[iV].R, FColors[iV].G, FColors[iV].B);
12391241
12401242 for iI := 0 to IndexCount - 1 do
12411243 ReadLn(MeshFile, FIndices[iI]);
12421244
12431245 CloseFile(MeshFile);
12441246 end;
12451247```
12461248
12471249После загрузки данных у нас есть все для рисования:
12481250
12491251``` pascal
12501252 procedure TMesh.DrawMesh;
12511253 begin
12521254 glEnableClientState(GL_VERTEX_ARRAY);
12531255 glEnableClientState(GL_COLOR_ARRAY);
12541256 glVertexPointer(3, GL_FLOAT, 0, @FVertices[0]);
12551257 glColorPointer(3, GL_FLOAT, 0, @FColors[0]);
12561258
12571259 glDrawElements(GL_TRIANGLES, Length(FIndices), GL_UNSIGNED_INT, @FIndices[0]);
12581260
12591261 glDisableClientState(GL_VERTEX_ARRAY);
12601262 glDisableClientState(GL_COLOR_ARRAY);
12611263 end;
12621264```
12631265
12641266Как видите, почти все то же самое, что и для неиндексированного рисования, за исключением функции, которая фактически рисует многоугольники. В этом случае мы используем функцию `glDrawElements`. Для этого мы указываем, какой тип полигонов мы хотим нарисовать, сколько индексов находится в буфере индекса, тип данных в буфере индекса и указатель на начало буфера индекса.
12651267
12661268![](/wiki-images/7/77/VertexArrayPic1.jpg)Полный исходный код поставляется с файлом данных сетки, который этот класс может использовать для создания прямоугольника, идентичного прямоугольнику, нарисованному в неиндексированном режиме. Файл данных сетки выглядит так:
12671269
12681270``` pascal
12691271 4 6
12701272 1 1 0 1 0 1
12711273 -1 1 0 0 0 1
12721274 -1 -1 0 0 1 0
12731275 1 -1 0 1 1 0
12741276 0
12751277 1
12761278 2
12771279 0
12781280 2
12791281 3
12801282```
12811283
12821284Как видите, есть данные только для 4 вершин и 6 индексов. Итак, первый треугольник определяется вершинами 0, 1 и 2, а второй - вершинами 0, 2 и 3. Используя индексированный режим, нам не нужно дублировать вершины.
12831285
12841286Загрузите исходный код, исполняемый файл для linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
12851287
12861288# Современный OpenGL с использованием MacOS
12871289
12881290Отличная особенность Lazarus - «написав однажды, компилируй где угодно», где код должен работать как на MacOS, так и на Linux или Windows. Однако для поддержки всех трех из этих систем вам необходимо решить, поддерживать ли только устаревший OpenGL или использовать профиль «OpenGL Core».
12891291
12901292Для пользователей Linux и Windows последние версии OpenGL представляют собой расширенный набор старых версий. Поэтому пользователь Linux может смешивать и сопоставлять старый код OpenGL с современными шейдерами. Но только **не** для случая с пользователями Macintosh MacOS (OSX).
12911293
12921294MacOS обеспечивает две формы поддержки: унаследованный режим поддерживает все функции OpenGL до 2.1 (и GLSL 1.2). Кроме того, пользователь может выбрать современную **core**-версию OpenGL (тогда как большинство пользователей Linux и Windows имеют доступ к «совместимым» версиям OpenGL).
12931295
12941296[**Сore**-версия OpenGL](https://prideout.net/modern-opengl-prezo/) удаляет многие устаревшие унаследованные функции. Это означает, что все приведенные выше учебники будут компилироваться только в устаревшем режиме MacOS. Режим ядра не имеет конвейера с фиксированными функциями, и поэтому разработчик должен написать свои собственные шейдеры. Кроме того, режим Core удаляет некоторые базовые примитивы, такие как [`GL_QUADS`](https://www.khronos.org/opengl/wiki/Legacy_OpenGL), поэтому квадрат необходимо заменить двумя треугольниками (это действительно имеет смысл: все вершины треугольника копланарны (расположены в одной плоскости), но это не обязательно имеет место для четырехугольника, точно так же, как каждая ножка треноги всегда будет касаться земли, в то время как 4х-ногая табуретка может колебаться, если одна нога короче других).
12951297
12961298Преимущество базовой модели состоит в том, что она проста и, как правило, легко адаптируется к мобильным устройствам (которые используют аналогичную встроенную форму OpenGL). См. [веб-сайт Apple](Https://developer.apple.com/opengl/capabilities/), чтобы увидеть OpenGL, поддерживаемый вашей операционной системой. Еще одна проблема заключается в том, что основные режимы OpenGL поддерживаются только в наборе виджетов Cocoa, поэтому вы не можете использовать набор виджетов Carbon (который в настоящее время используется по умолчанию в Lazarus).
12971299
12981300Три демонстрационных проекта доступны на [Github OpenGLCoreTutorials](https://github.com/neurolabusc/OpenGLCoreTutorials) и будут компилироваться с использованием Lazarus 1.6.2 или более поздней версии для Linux, Windows или MacOS.
12991301
13001302# See also
13011303
13021304- [[Creating bindings for C libraries/ru|Создание привязок для библиотек C]]
13031305- [[OpenGL]]
13041306
13051307# Внешние ссылки
13061308
13071309- [OpenGL Light Tutorial](http://www.falloutsoftware.com/tutorials/gl/gl8.htm) - Учебное пособие по OpenGL, в котором описывается, как создавать источники света и управлять ими, а также справляться с отражениями поверхности полигона с помощью нормалей.
13081310- [opengles3-book](https://github.com/danginsburg/opengles3-book)
13091311- [opengl-tutorials](https://github.com/zilongshanren/opengl-tutorials)
13101312
13111313---
13121314*From the Free Pascal wiki, page "OpenGL Tutorial/ru", as it stood on 2019-11-06 after a change by Zoltanleo, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
13131315Category: Code/ru
13141316Category: Example programs/ru
13151317Category: Graphics/ru
13161318Category: OpenGL/ru
13171319Category: Russian
13181320Category: Russian (unfinished translation)
13191321Category: Tutorials/ru

Back to history