Sign in Join
Wiki copytemporary

OpenGL Tutorial/ru: what changed

Revision 1 to 2. 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
104-Для того чтобы использовать GLUT, сначала необходимо инициализировать его. Это делается с помощью функции <b>glutInit</b>. Эта функция может разобрать командную строку и установить параметры для главного окна, но она ожидает, что ввод будет в стиле C/C++. Вы можете написать собственную функцию, которая будет преобразовывать ParamCount и ParamStr в стиль C/C++.
104+Для того чтобы использовать 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
139-Наконец, окна должны быть созданы с использованием функции <b>glutCreateWindow</b>. Это создаст его с названием, переданным в параметре. Функция возвращает handle окна. Это можно использовать в других функциях, которые требуют handle.
139+Наконец, окна должны быть созданы с использованием функции 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
590-<figure>
591-<img src="FullScreenAnimationPic1.jpg" title="FullScreenAnimationPic1.jpg" />
592-<figcaption>FullScreenAnimationPic1.jpg</figcaption>
593-</figure>
590+![](/wiki-images/5/51/FullScreenAnimationPic1.jpg)
594591
595592`glPushMatrix` и `glPopMatrix` используются для сохранения и восстановления состояния матрицы. Как видите, мы сохраняем состояние матрицы, затем меняем матрицу, чтобы нарисовать объект в нужном месте, а затем восстанавливаем старое состояние матрицы.
596593
597594Вы можете спросить, для чего нужна переменная `T`? Ну, она используется для определения скорости анимации. Каждое изменение, зависящее от времени, умножается на `T`. Таким образом, скорость анимации постоянна на каждой частоте кадров.
598595
599596Функция `glutGet` с параметром `GLUT_ELAPSED_TIME` возвращает время в миллисекундах из `glutInit`. Разделив ее значение на 1000, мы получим время в секундах.
600597
601598Функция `glRotatef` создает матрицу вращения. Первый параметр - это угол в градусах, а последние 3 параметра определяют ось, вокруг которой будет выполняться вращение. Поскольку вы умножили угол на `T`, объект будет повернут на этот угол ровно за 1 секунду.
602599
603600Загрузите исходный код, исполняемый файл linux или исполняемый файл windows из [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
604601
605602## Освещение
606603
607604Этот урок познакомит вас с освещением сцены. Вы сделаете вращающийся куб и один источник света, который добавит реалистичности сцене, но сначала давайте сделаем несколько вспомогательных модулей.
608605
609606На данный момент урок будет иметь только базовые функции, которые помогут нам получить текущее время и дельту (время, прошедшее от одного рендеринга до другого вызова рендеринга), и рассчитать количество кадров в секунду.
610607
611608``` pascal
612609 unit utils;
613610
614611 {$mode objfpc}{$H+}
615612
616613 interface
617614
618615 uses
619616 glut;
620617
621618 function GetTotalTime: Single;
622619 function GetDeltaTime: Single;
623620 procedure FrameRendered(Count: Integer = 1);
624621 function GetFPS: Single;
625622
626623 implementation
627624
628625 var
629626 OldTime: Integer = 0;
630627 FPSTime: Integer = 0;
631628 FPSCount: Integer = 0;
632629
633630 function GetTotalTime: Single;
634631 begin
635632 Result := glutGet(GLUT_ELAPSED_TIME) / 1000;
636633 end;
637634
638635 function GetDeltaTime: Single;
639636 var
640637 NewTime: Integer;
641638 begin
642639 NewTime := glutGet(GLUT_ELAPSED_TIME);
643640 Result := (NewTime - OldTime) / 1000;
644641 OldTime := NewTime;
645642 end;
646643
647644 procedure FrameRendered(Count: Integer);
648645 begin
649646 Inc(FPSCount, Count);
650647 end;
651648
652649 function GetFPS: Single;
653650 var
654651 NewTime: Integer;
655652 begin
656653 NewTime := glutGet(GLUT_ELAPSED_TIME);
657654
658655 Result := FPSCount / ((NewTime - FPSTime) / 1000);
659656
660657 FPSTime := NewTime;
661658 FPSCount := 0;
662659 end;
663660
664661 end.
665662```
666663
667664Как видите, в этом модуле нет ничего сложного. Время просто сохраняется между вызовами, а разница возвращается. FrameRendered должен вызываться каждый раз, когда вы рисуете сцену, чтобы функция могла рассчитывать FPS.
668665
669666Теперь давайте повеселимся с освещением. В OpenGL есть несколько типов света: ambient (окружающий), diffuse (рассеянный), point(точечный), spot (пятно), specular (зеркальный) и emissive (излучение).
670667
671668Окружающий свет (ambient) - это что-то вроде Солнца. Когда солнечные лучи проходят через окно комнаты, они ударяются о стены и отражаются во всех направлениях, что в среднем осветляет всю комнату. Все вершины освещены окружающим светом.
672669
673670Рассеянный свет(diffuse) может быть представлен как параллельные световые лучи, исходящие издалека. Они будут освещать только вершины, ориентированные на источник света.
674671
675672Точечный свет(point) освещает все вокруг себя. Он похож на огненный шар, он посылает световые лучи вокруг себя и освещает вершины, которые ориентированы на источник света и достаточно близки.
676673
677674Пятно (spot) - как свет от фонарика. Это просто точечный источник света с небольшим радиусом светового конуса. Все вершины, которые попадают внутрь конуса и находятся достаточно близко, подсвечиваются.
678675
679676Зеркальный свет (specular), как и рассеянный свет, является направленным типом света. Это происходит из одного конкретного направления. Разница между ними заключается в том, что зеркальный свет отражается от поверхности острым и равномерным образом. Рендеринг зеркального света зависит от угла между зрителем и источником света. С точки зрения зрителя зеркальный свет создает выделенную область на поверхности наблюдаемого объекта, известную как зеркальное выделение или зеркальное отражение.
680677
681678Излучение (emissive) - свет немного отличается от любых других ранее объясненных компонентов света. Этот свет исходит от объекта, который вы рисуете, но не освещаете другие объекты поблизости.
682679
683680Для простоты мы будем использовать только рассеянный свет в этом уроке. Позже, некоторые другие источники света могут появиться в уроках :)
684681
685682Давайте посмотрим, как включить освещение в сцене:
686683
687684``` pascal
688685 const
689686 DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
690687
691688 glEnable(GL_LIGHTING);
692689 glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
693690 glEnable(GL_LIGHT0);
694691```
695692
696693Как видите, мы включаем подсветку в OpenGL, чтобы свет влиял на сцену, которую вы рендерили. Параметры освещения задаются функцией `glLightfv`. Потребуется 3 параметра ... один для числа освещения, которое вы хотите изменить (OpenGL поддерживает до 8 источников света), другой сообщает OpenGL, какой параметр освещения изменить, а последний - новый параметр для освещения. В этом уроке вы установите только рассеянный свет для освещения. После этого вы можете включить освещение, и на сцене будет свет ... но ... это еще не все.
697694
698695Подробнее о `glLightfv`: <http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html>
699696
700697Если вы хотите использовать источники света, вы не можете просто задать цвет для вершины ... вы также должны задать и материал для вершин. Давайте настроим материал для рисования:
701698
702699``` pascal
703700 glEnable(GL_COLOR_MATERIAL);
704701 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
705702```
706703
707-<figure>
708-<img src="LightPic1.jpg" title="LightPic1.jpg" />
709-<figcaption>LightPic1.jpg</figcaption>
710-</figure>
704+![](/wiki-images/e/ed/LightPic1.jpg)
711705
712706Вы ожидали чего-то более сложного? :) Ну, этот код позволит нам использовать функцию `glColor`, чтобы задать материал для вершин. Используя функцию `glEnable` и флаг `GL_COLOR_MATERIAL`, вы можете определить, какие свойства материала будут изменять `glColor`. `glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE)` сообщает OpenGL, что `glColor` изменяет окружающий и рассеянный материал. Мы обсудим материалы больше в следующих уроках.
713707
714708Еще одна вещь, которая важна при использовании источников света ... каждая вершина должна иметь нормаль, связанную с ней. Нормаль используется для определения направления вершины, чтобы свет можно было правильно рассчитать. Вы будете использовать функцию GLUT, чтобы нарисовать куб, и он предоставит нам нормали, так что на этот раз мы просто пройдемся по нормали.
715709
716710После всех этих настроек, свет будет сиять в вашем кубе :)
717711
718712Часть текста скопирована из [The OpenGL Light Bible](http://www.falloutsoftware.com/tutorials/gl/gl8.htm)
719713
720714Скачать исходный код, исполняемые файлы linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
721715
722716## Растровые шрифты
723717
724718Игры и программы, как правило, должны писать текст на экране. GLUT предоставляет несколько функций, которые не зависят от платформы, для рисования символов.
725719
726720Сначала мы покажем, как использовать стандартные растровые шрифты. Почти все дополнения кода будут сделаны в модуле utils.pas.
727721
728722Поскольку текст будет отображаться в 2D, нам нужно знать ширину и высоту области просмотра ... поэтому мы напишем две функции для этого:
729723
730724``` pascal
731725 function glGetViewportWidth: Integer;
732726 var
733727 Rect: array[0..3] of Integer;
734728 begin
735729 glGetIntegerv(GL_VIEWPORT, @Rect);
736730 Result := Rect[2] - Rect[0];
737731 end;
738732
739733 function glGetViewportHeight: Integer;
740734 var
741735 Rect: array[0..3] of Integer;
742736 begin
743737 glGetIntegerv(GL_VIEWPORT, @Rect);
744738 Result := Rect[3] - Rect[1];
745739 end;
746740```
747741
748742Мы просто получаем left/right, top/bottom и вычисляем width/height, вычитая их.
749743
750744Должны быть функции для входа и выхода из режима 2D:
751745
752746``` pascal
753747 procedure glEnter2D;
754748 begin
755749 glMatrixMode(GL_PROJECTION);
756750 glPushMatrix;
757751 glLoadIdentity;
758752 gluOrtho2D(0, glGetViewportWidth, 0, glGetViewportHeight);
759753
760754 glMatrixMode(GL_MODELVIEW);
761755 glPushMatrix;
762756 glLoadIdentity;
763757
764758 glDisable(GL_DEPTH_TEST);
765759 end;
766760
767761 procedure glLeave2D;
768762 begin
769763 glMatrixMode(GL_PROJECTION);
770764 glPopMatrix;
771765 glMatrixMode(GL_MODELVIEW);
772766 glPopMatrix;
773767
774768 glEnable(GL_DEPTH_TEST);
775769 end;
776770```
777771
778772При входе в режим 2D мы сохраняем текущие матрицы и устанавливаем 2D-матрицу с помощью функции `gluOrtho2D`. Таким образом, если мы нарисуем какую-либо штуку в координатах (100,100), она будет нарисована точно на 100 пикселей от левого края окна и на 100 пикселей от нижнего края формы (положительные значения по оси Y направлены вверх). Также мы отключаем ZBuffer. Таким образом, текст не изменит ZBuffer.
779773
780774Выход из режима 2D просто возвращает старые матрицы и включает ZBuffer.
781775
782776Теперь мы можем создать функцию для рисования текста:
783777
784778``` pascal
785779 procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
786780 var
787781 I: Integer;
788782 begin
789783 glRasterPos2f(X, Y);
790784 for I := 1 to Length(Text) do
791785 glutBitmapCharacter(Font, Integer(Text[I]));
792786 end;
793787```
794788
795789`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) и другие - являются символом.
796790
797791Символ будет нарисован в текущей позиции растра. Чтобы установить желаемую позицию растра, мы вызываем функцию `glRasterPos`. `glRasterPos` может обрабатывать различные количества и типы параметров так же, как функция `glVertex`. Указанная координата преобразуется моделью и матрицей проекции, чтобы получить двухмерную координату, в которой будет находиться новая позиция растра. Поскольку мы вошли в режим 2D, координаты X и Y являются фактическими координатами 2D, в которых будет происходить рисование.
798792
799793Эти новые функции сделают рисование текста очень простым:
800794
801795``` pascal
802796 procedure DrawGLScene; cdecl;
803797 begin
804798 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
805799
806800 glLoadIdentity;
807801 glTranslatef(0, 0, -5);
808802 glRotatef(GetTotalTime * 10, 0, 0.5, 0.5);
809803
810804 glColor3f(1, 0, 0);
811805 glutSolidCube(2);
812806
813807 glEnter2D;
814808
815809 glColor3f(0.2, 0.8 + 0.2 * Sin(GetTotalTime * 5), 0);
816810 glWrite(20, glGetViewportHeight - 20, GLUT_BITMAP_8_BY_13,
817811 Format('OpenGL Tutorial :: Bitmap Fonts :: FPS - %.2f FPS', [FPS]));
818812
819813 glColor3f(1, 1, 1);
820814 glWrite(50, glGetViewportHeight - 60, GLUT_BITMAP_9_BY_15, 'GLUT_BITMAP_9_BY_15');
821815 glWrite(50, glGetViewportHeight - 90, GLUT_BITMAP_8_BY_13, 'GLUT_BITMAP_8_BY_13');
822816 glWrite(50, glGetViewportHeight - 120, GLUT_BITMAP_TIMES_ROMAN_10, 'GLUT_BITMAP_TIMES_ROMAN_10');
823817 glWrite(50, glGetViewportHeight - 150, GLUT_BITMAP_TIMES_ROMAN_24, 'GLUT_BITMAP_TIMES_ROMAN_24');
824818 glWrite(50, glGetViewportHeight - 180, GLUT_BITMAP_HELVETICA_10, 'GLUT_BITMAP_HELVETICA_10');
825819 glWrite(50, glGetViewportHeight - 210, GLUT_BITMAP_HELVETICA_12, 'GLUT_BITMAP_HELVETICA_12');
826820 glWrite(50, glGetViewportHeight - 240, GLUT_BITMAP_HELVETICA_18, 'GLUT_BITMAP_HELVETICA_18');
827821
828822 glColor3f(0.5, 0.5, 1);
829823 glWrite(
830824 glGetViewportWidth - glutBitmapLength(GLUT_BITMAP_9_BY_15, LazText) - 5,
831825 10, GLUT_BITMAP_9_BY_15, LazText);
832826
833827 glLeave2D;
834828
835829 glutSwapBuffers;
836830
837831 FrameRendered;
838832 end;
839833```
840834
841-<figure>
842-<img src="BitmapFontsPic1.jpg" title="BitmapFontsPic1.jpg" />
843-<figcaption>BitmapFontsPic1.jpg</figcaption>
844-</figure>
835+![](/wiki-images/b/ba/BitmapFontsPic1.jpg)
845836
846837Мы рисуем красный куб и поворачиваем его, а также немного текста, чтобы показать, как выглядят различные растровые шрифты. Функция `glutBitmapLength` используется для определения ширины строки, чтобы ее можно было выровнять вправо. Код может быть легко изменен для выравнивая текста по центру.
847838
848839> **Note:** Посмотрите, как куб выглядит без света.
849840
850841Загрузите исходный код, исполняемый файл под Linux или Windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
851842
852843## Текстуры
853844
854845Пришло время использовать текстуры :)
855846
856847Этот урок покажет, как рисовать текстурированные полигоны и как смешивать текстуры, используя технику многопроходности. Поскольку в OpenGL нет встроенного механизма загрузки текстур, мы будем использовать внешнюю библиотеку: [Vampyre Imaging Library](http://imaginglib.sourceforge.net/). Мы будем использовать только вспомогательные функции OpenGL, но вам может пригодиться эта библиотека для некоторых других целей.
857848
858849Давайте начнем ... мы создадим список отображения для рисования текстурированного прямоугольника:
859850
860851``` pascal
861852 procedure CreateList;
862853 begin
863854 glNewList(LIST_OBJECT, GL_COMPILE);
864855 glBegin(GL_QUADS);
865856 glTexCoord2f(1, 0);
866857 glVertex3f( 2, 2, 0);
867858 glTexCoord2f(0, 0);
868859 glVertex3f(-2, 2, 0);
869860 glTexCoord2f(0, 1);
870861 glVertex3f(-2,-2, 0);
871862 glTexCoord2f(1, 1);
872863 glVertex3f( 2,-2, 0);
873864 glEnd;
874865 glEndList;
875866 end;
876867```
877868
878869Обратите внимание на функции `glTexCoord`. Они используются, чтобы указать, какая часть текстуры назначена вершине. Координаты, определенные в этих функциях, имеют значения от 0 до 1 (допустимы значения больше 1, но они могут давать разные результаты). 0 - это первый пиксель, а 1 - последний. Таким образом, 0.5 будет прямо в середине текстуры.
879870
880871Загрузка текстур чрезвычайно проста с Vampyre Imaging Library:
881872
882873``` pascal
883874 var
884875 Tex1, Tex2: GLuint;
885876
886877 procedure InitializeGL;
887878 begin
888879 glClearColor(0, 0, 0, 0);
889880 Tex1 := LoadGLTextureFromFile('ashwood.bmp');
890881 Tex2 := LoadGLTextureFromFile('Flare.bmp');
891882 glEnable(GL_TEXTURE_2D);
892883 end;
893884```
894885
895886`LoadGLTextureFromFile` загружает текстуру из файла и возвращает ее ID. Когда текстура загружена, она уже настроена на рендеринг. Последняя строка просто включает 2D текстуры.
896887
897888Чтобы нарисовать текстурированный многоугольник, вы должны связать текстуру и установить координаты текстуры (координаты текстуры задаются в списке отображения в этом уроке):
898889
899890``` pascal
900891 ...
901892 glLoadIdentity;
902893 glTranslatef(-5, 0, -15);
903894 glBindTexture(GL_TEXTURE_2D, Tex1);
904895 glCallList(LIST_OBJECT);
905896 ...
906897```
907898
908899Функция `glBindTexture` используется для выбора текстуры. Когда вы рисуете полигоны, на них будет выделена текстура. Это так просто :)
909900
910901Итак, использовать одну текстуру легко ... но как смешать две текстуры? Обычно вы рисуете полигон один раз с одной текстурой, настраиваете параметры смешивания и еще раз рисуете полигон с другой текстурой. Таким способом вы можете смешивать текстуры. Давайте посмотрим, как выглядит код для этого:
911902
912903``` pascal
913904 ...
914905 glLoadIdentity;
915906 glTranslatef(5, 0, -15);
916907 glBindTexture(GL_TEXTURE_2D, Tex1);
917908 glCallList(LIST_OBJECT);
918909
919910 glEnable(GL_BLEND);
920911 glBlendFunc(GL_ZERO, GL_SRC_COLOR);
921912 glLoadIdentity;
922913 glTranslatef(5, 0, -15);
923914 glBindTexture(GL_TEXTURE_2D, Tex2);
924915 glCallList(LIST_OBJECT);
925916 glDisable(GL_BLEND);
926917 ...
927918```
928919
929920Как видите, полигон рисуется впервые, как мы уже знаем. Перед вторым рисованием мы включаем смешивание, вызывая `glEnable(GL_BLEND)`. Смешивание означает, что конечный цвет пикселя рассчитывается следующим образом:
930921
931922``` pascal
932923 DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND
933924```
934925
935926`SRCBLEND` и `DESTBLEND` определяются с помощью функции `glBlendFunc`. В этом уроке мы устанавливаем для `SRCBLEND` значение `GL_ZERO` (ноль) и для `DESTBLENT` значение `GL_SRC_COLOR (DrawingColor)`, а затем окончательный цвет:
936927
937928``` pascal
938929 DrawingColor * 0 + BackgroundColor * DrawingColor
939930```
940931
941-<figure>
942-<img src="TexturesPic1.jpg" title="TexturesPic1.jpg" />
943-<figcaption>TexturesPic1.jpg</figcaption>
944-</figure>
932+![](/wiki-images/d/d4/TexturesPic1.jpg)
945933
946934Это означает, что фон будет темнее, когда вы рисуете темными цветами ... когда вы рисуете белым цветом, цвет фона не изменится. Результат будет выглядеть следующим образом.
947935
948936В следующий раз мы будем использовать расширения, чтобы показать, как использовать однопроходное мультитекстурирование.
949937
950938Загрузите исходный код, исполняемый файл для linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
951939
952940## Мультитекстурирование (расширения)
953941
954942Когда вы уже знаете многопроходное мультитекстурирование, однопроходный процесс очень прост. Текстурирование разделено на этапы. Первый этап настраивает и рисует первую текстуру, второй этап рисует еще одну и так далее. Все, что вам нужно сделать, это настроить этапы текстуры и визуализации объекта.
955943
956944Давайте посмотрим, как выглядит код:
957945
958946``` pascal
959947 procedure InitializeGL;
960948 begin
961949 Load_GL_ARB_multitexture;
962950 glClearColor(0, 0, 0, 0);
963951 Tex1 := LoadGLTextureFromFile('Lazarus.bmp');
964952 Tex2 := LoadGLTextureFromFile('Mask.bmp');
965953 glActiveTextureARB(GL_TEXTURE0_ARB);
966954 glEnable(GL_TEXTURE_2D);
967955 glBindTexture(GL_TEXTURE_2D, Tex1);
968956 glActiveTextureARB(GL_TEXTURE1_ARB);
969957 glEnable(GL_TEXTURE_2D);
970958 glBindTexture(GL_TEXTURE_2D, Tex2);
971959 end;
972960```
973961
974962Для начала нам нужно загрузить расширение OpenGL, которое позволит нам использовать мультитекстурные функции. `Load_GL_ARB_multitexture` попытается загрузить эти расширения и вернет TRUE, если операция прошла успешно.
975963
976964Чтобы выбрать стадию текстуры, над которой вы хотите работать, используйте функцию `glActiveTextureARB`. Она принимает только один параметр, который определяет, какая стадия вам нужна. После этого все текстурные функции (включение, отключение, связывание, создание ...) будут влиять на этот этап.
977965
978966Поскольку мы настраиваем все в функции инициализации, все, что нам нужно сделать, это нарисовать объект:
979967
980968``` pascal
981969 procedure DrawGLScene; cdecl;
982970 begin
983971 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
984972
985973 glLoadIdentity;
986974 glTranslatef(0, 0, -5);
987975
988976 glBegin(GL_QUADS);
989977 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
990978 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
991979 glVertex3f(2.516, 2, 0);
992980 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
993981 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
994982 glVertex3f(-2.516, 2, 0);
995983 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
996984 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
997985 glVertex3f(-2.516,-2, 0);
998986 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
999987 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
1000988 glVertex3f(2.516,-2, 0);
1001989 glEnd;
1002990
1003991 glutSwapBuffers;
1004992 end;
1005993```
1006994
1007995![](/wiki-images/5/52/MultitexturePic1.jpg) Как видите, разница только в определении текстурных координат. Теперь мы используем функцию `glMultiTexCoord2fARB`, которая принимает стадию текстуры и координаты текстуры. Все остальное без изменений.
1008996
1009997Сегодня почти все графические карты поддерживают как минимум 2 стадии текстур. Использование однопроходного мультитекстурирования выполняется быстрее, чем многопроходная версия, поскольку объекты рисуются только один раз за раз. Если оборудование поддерживает однопроходное мультитекстурирование (`Load_GL_ARB_multitexture` возвращает TRUE), используйте его.
1010998
1011999Загрузите исходный код, исполняемый файл под linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
10121000
10131001## Рендеринг в текстуру
10141002
10151003Этот будет кратко. OpenGL может захватывать текущую сцену в текстуру, чтобы вы могли использовать ее для текстурирования других объектов (экран телевизора, зеркало или что-то еще). Итак, просто визуализируйте сцену в текстуру и примените ее к вращающейся плоскости.
10161004
10171005Сначала мы должны создать пустую текстуру, которую мы будем использовать для захвата сцены:
10181006
10191007``` pascal
10201008 procedure SetupRenderTexture;
10211009 var
10221010 Data: Pointer;
10231011 begin
10241012 GetMem(Data, 256*256*3);
10251013 glGenTextures(1, @RenderTexture);
10261014 glBindTexture(GL_TEXTURE_2D, RenderTexture);
10271015 glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, Data);
10281016 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
10291017 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
10301018 FreeMem(Data);
10311019 end;
10321020```
10331021
10341022Создается буфер для изображения размером 256 * 256 RGB, который используется для настройки 2D-текстуры.
10351023
10361024Основная часть - в функции рисования:
10371025
10381026``` pascal
10391027 procedure DrawGLScene; cdecl;
10401028 var
10411029 TotalTime: Single;
10421030 begin
10431031 glClearColor(0, 0, 0, 0);
10441032 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
10451033 glEnable(GL_LIGHTING);
10461034 glDisable(GL_TEXTURE_2D);
10471035 glViewport(0, 0, 256, 256);
10481036
10491037 TotalTime := GetTotalTime;
10501038
10511039 glLoadIdentity;
10521040 glTranslatef(0, 0, -5);
10531041 glRotatef(50 * TotalTime, 1, 0, 0);
10541042 glRotatef(100 * TotalTime, 0, 1, 0);
10551043 glRotatef(50 * TotalTime, 0, 0, 1);
10561044
10571045 glColor3f(1, 1, 1);
10581046 glutSolidCube(2);
10591047
10601048 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);
10611049
10621050 glClearColor(0.18, 0.20, 0.66, 0);
10631051 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
10641052 glDisable(GL_LIGHTING);
10651053 glEnable(GL_TEXTURE_2D);
10661054 glViewport(0, 0, AppWidth, AppHeight);
10671055
10681056 glLoadIdentity;
10691057 glTranslatef(0, 0, -7);
10701058 glRotatef(20 * TotalTime, 1, 0, 0);
10711059 glRotatef(50 * TotalTime, 0, 1, 0);
10721060
10731061 glBegin(GL_QUADS);
10741062 glTexCoord2f(1, 0);
10751063 glVertex3f(2, 2, 0);
10761064 glTexCoord2f(0, 0);
10771065 glVertex3f(-2, 2, 0);
10781066 glTexCoord2f(0, 1);
10791067 glVertex3f(-2,-2, 0);
10801068 glTexCoord2f(1, 1);
10811069 glVertex3f(2,-2, 0);
10821070 glEnd;
10831071
10841072 glutSwapBuffers;
10851073 end;
10861074```
10871075
10881076![](/wiki-images/6/6e/RenderToTexturePic1.jpg) Во-первых, все настроено для сцены, которая будет захвачена. Окно просмотра уменьшено до 256*256, поэтому оно будет вписываться в текстуру, и сцена будет прорисована. `glCopyTexImage2D` используется для захвата сцены в текущую выбранную текстуру.
10891077
10901078Когда у нас есть сцена, захваченная в текстуру, все может быть очищено снова, область просмотра может быть возвращена к исходному размеру, а финальная сцена рисуется с использованием предыдущей сцены в качестве текстуры.
10911079
10921080> **Note:** Захваченную текстуру можно сохранить с помощью функции `SaveGLTextureToFile` из [Vampyre Imaging Library](http://imaginglib.sourceforge.net/).
10931081
10941082Загрузите исходный код, исполняемый файл для linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
10951083
10961084## Массив вершин
10971085
10981086OpenGL способен отображать примитивы, используя данные, которые хранятся в буферах, вызванных вызовом `glVertex`. Буферы могут использоваться для определения координат вершин и текстур, а также цветов (индекс и RGBA), нормалей и флагов ребер.
10991087
11001088В этом уроке будут использоваться только вершинные и цветовые буферы, и мы покажем неиндексированное и индексированное рисование. Неиндексированный режим рисует буферы в виде потоков. Индексированный режим будет рисовать элементы буфера в порядке, определенном в индексе буфера. Но хватит говорить ... давайте начнем кодить.
11011089
11021090Для начала давайте определим некоторые типы и константы:
11031091
11041092``` pascal
11051093 type
11061094 TVertex3f = record
11071095 X, Y, Z: Single;
11081096 end;
11091097
11101098 TColor3f = record
11111099 R, G, B: Single;
11121100 end;
11131101
11141102 VertexBuffer: array [0..5] of TVertex3f = (
11151103 (X : 1; Y : 1; Z : 0),
11161104 (X : -1; Y : 1; Z : 0),
11171105 (X : -1; Y : -1; Z : 0),
11181106 (X : 1; Y : 1; Z : 0),
11191107 (X : -1; Y : -1; Z : 0),
11201108 (X : 1; Y : -1; Z : 0)
11211109 );
11221110 ColorBuffer: array [0..5] of TColor3f = (
11231111 (R : 1; G : 0; B : 1),
11241112 (R : 0; G : 0; B : 1),
11251113 (R : 0; G : 1; B : 0),
11261114 (R : 1; G : 0; B : 1),
11271115 (R : 0; G : 1; B : 0),
11281116 (R : 1; G : 1; B : 0)
11291117 );
11301118```
11311119
11321120У нас есть два буфера. Один для координат вершин и один для цветов вершин. Эти 6 вершин определяют 2х треугольников, которые образуют прямоугольник.
11331121
11341122Рисовать примитивы с помощью буферов легко:
11351123
11361124``` pascal
11371125 glEnableClientState(GL_VERTEX_ARRAY);
11381126 glEnableClientState(GL_COLOR_ARRAY);
11391127 glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);
11401128 glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);
11411129
11421130 glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));
11431131
11441132 glDisableClientState(GL_VERTEX_ARRAY);
11451133 glDisableClientState(GL_COLOR_ARRAY);
11461134```
11471135
11481136Сначала мы включаем буферы, которые мы хотим использовать, используя функцию `glEnableClientState`. Затем мы можем выбрать буферы, которые мы хотим использовать. Каждый тип буфера имеет собственную функцию для выбора (`glColorPointer`, `glEdgeFlagPointer`, `glIndexPointer`, `glNormalPointer`, `glTexCoordPointer`, `glVertexPointer`).
11491137
11501138Первый параметр в этих функциях определяет, сколько чисел содержит каждый элемент. Например, давайте возьмем буфер вершин. Если этот параметр равен 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). Далее определяется количество байтов между каждым элементом. Таким образом, вы можете иметь буфер, который содержит координаты вершины и некоторые пользовательские данные.
11511139
11521140Для произвольного типа данных этот параметр может быть рассчитан следующим образом:
11531141
11541142``` pascal
11551143 type
11561144 TBufferData = record
11571145 DataBefore: TDataBefore;
11581146 Vertex: TVertex;
11591147 DataAfter: TDataAfter;
11601148 end;
11611149```
11621150
11631151`Байты между элементами = SizeOf(TDataBefore) + SizeOf(TDataAfter)`
11641152
11651153Последний параметр - указатель на начало буфера.
11661154
11671155Когда буферы выбраны, мы можем нарисовать их, используя функции `glDrawArrays`. Все включенные буферы используются для рисования примитивов. Тип создаваемых полигонов определяется в первом параметре (так же, как в функции `glBegin`). Следующие два определяют подмножество буфера, который используется для рисования (start и count).
11681156
11691157Когда буферы не нужны, вы можете отключить их.
11701158
11711159Чтобы продемонстрировать индексированный режим, я создал простой класс сетки, который может загружать данные вершин, цветов и индексов из внешних файлов:
11721160
11731161``` pascal
11741162 type
11751163 TMesh = class
11761164 private
11771165 FVertices: array of TVertex3f;
11781166 FColors: array of TColor3f;
11791167 FIndices: array of Integer;
11801168 procedure FreeBuffers;
11811169 public
11821170 constructor Create;
11831171 destructor Destroy; override;
11841172 procedure LoadMesh(FileName: String);
11851173 procedure DrawMesh;
11861174 end;
11871175```
11881176
11891177Поле FVertices будет содержать данные о вершинах, поле FColors - данные о цвете, и поле FIndices - данные об индексах при загрузке внешнего файла.
11901178
11911179Сначала мы напишем некоторый код, который занимается созданием и уничтожением класса:
11921180
11931181``` pascal
11941182 procedure TMesh.FreeBuffers;
11951183 begin
11961184 FVertices := nil;
11971185 FColors := nil;
11981186 FIndices := nil;
11991187 end;
12001188
12011189 constructor TMesh.Create;
12021190 begin
12031191 FreeBuffers;
12041192 end;
12051193
12061194 destructor TMesh.Destroy;
12071195 begin
12081196 FreeBuffers;
12091197 inherited Destroy;
12101198 end;
12111199```
12121200
12131201Файл, который будет содержать данные сетки, является простым текстовым файлом. Первая строка будет содержать количество вершин и индексов, разделенных пробелом. После этого ряда появятся строки для каждой вершины и цвета. X, Y, Z, R, G и B все разделены пробелом. В конце будут строки для индексов ... каждый номер индекса будет записан в своей собственной строке ... поэтому для одного треугольника файл данных будет выглядеть примерно так:
12141202
12151203``` pascal
12161204 3 3
12171205 -1 -1 0 1 1 1
12181206 1 -1 0 1 1 1
12191207 0 1 0 1 1 1
12201208 0
12211209 1
12221210 2
12231211```
12241212
12251213Это означает, что в файле определены 3 вершины и 3 индекса. Первая вершина имеет значения -1, -1, 0 и цвет, имеющий значения 1, 1, 1 и т.д. Индексы определяют тот порядок, в котором рисуются вершины (в этом случае вершины рисуются в том же порядке, в котором они определены).
12261214
12271215Код для загрузки этих данных будет выглядеть так:
12281216
12291217``` pascal
12301218 procedure TMesh.LoadMesh(FileName: String);
12311219 var
12321220 MeshFile: TextFile;
12331221 VertexCount, IndexCount: Integer;
12341222 iV, iI: Integer;
12351223 begin
12361224 FreeBuffers;
12371225
12381226 AssignFile(MeshFile, FileName);
12391227 Reset(MeshFile);
12401228
12411229 ReadLn(MeshFile, VertexCount, IndexCount);
12421230
12431231 SetLength(FVertices, VertexCount);
12441232 SetLength(FColors, VertexCount);
12451233 SetLength(FIndices, IndexCount);
12461234
12471235 for iV := 0 to VertexCount - 1 do
12481236 ReadLn(MeshFile,
12491237 FVertices[iV].X, FVertices[iV].Y, FVertices[iV].Z,
12501238 FColors[iV].R, FColors[iV].G, FColors[iV].B);
12511239
12521240 for iI := 0 to IndexCount - 1 do
12531241 ReadLn(MeshFile, FIndices[iI]);
12541242
12551243 CloseFile(MeshFile);
12561244 end;
12571245```
12581246
12591247После загрузки данных у нас есть все для рисования:
12601248
12611249``` pascal
12621250 procedure TMesh.DrawMesh;
12631251 begin
12641252 glEnableClientState(GL_VERTEX_ARRAY);
12651253 glEnableClientState(GL_COLOR_ARRAY);
12661254 glVertexPointer(3, GL_FLOAT, 0, @FVertices[0]);
12671255 glColorPointer(3, GL_FLOAT, 0, @FColors[0]);
12681256
12691257 glDrawElements(GL_TRIANGLES, Length(FIndices), GL_UNSIGNED_INT, @FIndices[0]);
12701258
12711259 glDisableClientState(GL_VERTEX_ARRAY);
12721260 glDisableClientState(GL_COLOR_ARRAY);
12731261 end;
12741262```
12751263
12761264Как видите, почти все то же самое, что и для неиндексированного рисования, за исключением функции, которая фактически рисует многоугольники. В этом случае мы используем функцию `glDrawElements`. Для этого мы указываем, какой тип полигонов мы хотим нарисовать, сколько индексов находится в буфере индекса, тип данных в буфере индекса и указатель на начало буфера индекса.
12771265
12781266![](/wiki-images/7/77/VertexArrayPic1.jpg)Полный исходный код поставляется с файлом данных сетки, который этот класс может использовать для создания прямоугольника, идентичного прямоугольнику, нарисованному в неиндексированном режиме. Файл данных сетки выглядит так:
12791267
12801268``` pascal
12811269 4 6
12821270 1 1 0 1 0 1
12831271 -1 1 0 0 0 1
12841272 -1 -1 0 0 1 0
12851273 1 -1 0 1 1 0
12861274 0
12871275 1
12881276 2
12891277 0
12901278 2
12911279 3
12921280```
12931281
12941282Как видите, есть данные только для 4 вершин и 6 индексов. Итак, первый треугольник определяется вершинами 0, 1 и 2, а второй - вершинами 0, 2 и 3. Используя индексированный режим, нам не нужно дублировать вершины.
12951283
12961284Загрузите исходный код, исполняемый файл для linux или windows с [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
12971285
12981286# Современный OpenGL с использованием MacOS
12991287
13001288Отличная особенность Lazarus - «написав однажды, компилируй где угодно», где код должен работать как на MacOS, так и на Linux или Windows. Однако для поддержки всех трех из этих систем вам необходимо решить, поддерживать ли только устаревший OpenGL или использовать профиль «OpenGL Core».
13011289
13021290Для пользователей Linux и Windows последние версии OpenGL представляют собой расширенный набор старых версий. Поэтому пользователь Linux может смешивать и сопоставлять старый код OpenGL с современными шейдерами. Но только **не** для случая с пользователями Macintosh MacOS (OSX).
13031291
13041292MacOS обеспечивает две формы поддержки: унаследованный режим поддерживает все функции OpenGL до 2.1 (и GLSL 1.2). Кроме того, пользователь может выбрать современную **core**-версию OpenGL (тогда как большинство пользователей Linux и Windows имеют доступ к «совместимым» версиям OpenGL).
13051293
13061294[**Сore**-версия OpenGL](https://prideout.net/modern-opengl-prezo/) удаляет многие устаревшие унаследованные функции. Это означает, что все приведенные выше учебники будут компилироваться только в устаревшем режиме MacOS. Режим ядра не имеет конвейера с фиксированными функциями, и поэтому разработчик должен написать свои собственные шейдеры. Кроме того, режим Core удаляет некоторые базовые примитивы, такие как [`GL_QUADS`](https://www.khronos.org/opengl/wiki/Legacy_OpenGL), поэтому квадрат необходимо заменить двумя треугольниками (это действительно имеет смысл: все вершины треугольника копланарны (расположены в одной плоскости), но это не обязательно имеет место для четырехугольника, точно так же, как каждая ножка треноги всегда будет касаться земли, в то время как 4х-ногая табуретка может колебаться, если одна нога короче других).
13071295
13081296Преимущество базовой модели состоит в том, что она проста и, как правило, легко адаптируется к мобильным устройствам (которые используют аналогичную встроенную форму OpenGL). См. [веб-сайт Apple](Https://developer.apple.com/opengl/capabilities/), чтобы увидеть OpenGL, поддерживаемый вашей операционной системой. Еще одна проблема заключается в том, что основные режимы OpenGL поддерживаются только в наборе виджетов Cocoa, поэтому вы не можете использовать набор виджетов Carbon (который в настоящее время используется по умолчанию в Lazarus).
13091297
13101298Три демонстрационных проекта доступны на [Github OpenGLCoreTutorials](https://github.com/neurolabusc/OpenGLCoreTutorials) и будут компилироваться с использованием Lazarus 1.6.2 или более поздней версии для Linux, Windows или MacOS.
13111299
13121300# See also
13131301
13141302- [[Creating bindings for C libraries/ru|Создание привязок для библиотек C]]
13151303- [[OpenGL]]
13161304
13171305# Внешние ссылки
13181306
13191307- [OpenGL Light Tutorial](http://www.falloutsoftware.com/tutorials/gl/gl8.htm) - Учебное пособие по OpenGL, в котором описывается, как создавать источники света и управлять ими, а также справляться с отражениями поверхности полигона с помощью нормалей.
13201308- [opengles3-book](https://github.com/danginsburg/opengles3-book)
13211309- [opengl-tutorials](https://github.com/zilongshanren/opengl-tutorials)
13221310
13231311---
13241312*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).*
13251313Category: Code/ru
13261314Category: Example programs/ru
13271315Category: Graphics/ru
13281316Category: OpenGL/ru
13291317Category: Russian
13301318Category: Russian (unfinished translation)
13311319Category: Tutorials/ru

Back to history