Sign in Join
Wiki copytemporary

OpenGL Tutorial/zh CN: what changed

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

OldNewLine
11OpenGL 是开发可移植的交互式2D和3D图形应用的首选环境。自从1992年问世以来,OpenGL已成为业内最广泛使用和最广为支持的2D和3D图形应用编程接口(API),为各种计算平台带来成千的应用。OpenGL通过包含一系列渲染,纹理映射,特殊效果和其他强大的视觉功能极大促进了创新并加速应用开发。开发人员可以在各种主流桌面及工作站平台发挥OpenGL的强大功能确保广泛部署。
22
33在以下联接您会得到更多关于OpenGl信息。[here](http://www.opengl.org/about/overview/).
44
55# GLUT
66
77GLUT (pronounced like the glut in gluttony) 全称 OpenGL Utility Toolkit, 写OpenGL 程序的窗口系统独立工具包。 它实现了一个简单的OpenGL窗口的应用程序编程接口(API). GLUT 让OpenGL编程更加容易理解,GLUT 具有可移植性,可以在不同平台编译。
88
99你能得到更多关于 GLUT的信息: [here](http://www.opengl.org/resources/libraries/glut/).
1010
1111许多系统已预安装了GLUT,但如果你的系统没有的话你可以轻松用Google搜索到 [Google](http://www.google.com/).
1212
1313编译好的Windows可执行文件可在这里找到 [www.xmission.com](http://www.xmission.com/~nate/glut.html).
1414
15-`GLUT FPC 单元信息在这里 `[[OpenGL]]`.`
15+```text
16+GLUT FPC 单元信息在这里 OpenGL.
17+```
1618
1719# LCL
1820
1921Lzazrus组件库也可以使用OpenGL。Lazarus包含了一个OpenGL的LCL控件TOpenGLControl。Lazarus包的位置:lazarus/components/opengl/lazopenglcontext.lpk。有一个实例是:lazarus/examples/openglcontrol/openglcontrol_demo.lpi。
2022
2123# LCL / GLUT
2224
2325你应该使用GLUT还是LCL?
2426
2527- 如果你想自己画所有的东西,GLUT会更好。
2628- 一般应用的话LCL更好。例如一个3D编辑器需要亦歌OpenGL窗口和剩余的一般应用如使用一般的按钮,组合框,窗口,窗口模块等。
2729
2830OpenGL部分大致相同。 在windows下GLUT需要一个dll(动态链接库) , where LCL typically runs out of the box, but a LCL executable is bigger.
2931
3032# 建立您的首个 GLUT 程序
3133
3234为了使用GLUT,您必须首先初始化它。这可以用glutInit函数完成。该函数可处理主窗体的命令行和设置其参数,但需要使用C/C++风格的输入。您需要编写自己的函数来完成从ParamCount 和 ParamStr 到C/C++风格命令行参数的转换。
3335
3436``` pascal
3537 procedure glutInitPascal(ParseCmdLine: Boolean);
3638 var
3739 Cmd: array of PChar;
3840 CmdCount, I: Integer;
3941 begin
4042 if ParseCmdLine then
4143 CmdCount := ParamCount + 1
4244 else
4345 CmdCount := 1;
4446 SetLength(Cmd, CmdCount);
4547 for I := 0 to CmdCount - 1 do
4648 Cmd[I] := PChar(ParamStr(I));
4749 glutInit(@CmdCount, @Cmd);
4850 end;
4951```
5052
5153本质上,您创建一个数组,并用来自ParamStr的字符串填充。该函数也接受可控制传递到glutInit的参数,无论是整个命令行或仅仅是可执行文件名
5254
5355ToDo: probably **glutInit(@argc, @argv);** is enough.
5456
5557更多有关 glutInit 函数: <http://www.opengl.org/resources/libraries/glut/spec3/node10.html>
5658
5759下一步,您需要创建主窗体 使用glutInitDisplayMode设置主窗体显示模式。该函数只接受一个标志位组合为参数。通常您只需要GLUT_DOUBLE 或 GLUT_RGB 或 GLUT_DEPTH 。
5860
5961更多关于 glutInitDisplayMode 参见: <http://www.opengl.org/resources/libraries/glut/spec3/node12.html>
6062
6163窗体位置和大小使用glutInitWindowPosition 和 glutInitWindowSize来控制。他们接受2个参数。前一个参数是X和Y坐标,后一个是宽度和长度。您可使用glutGet以返回窗体大小和中心。
6264
6365更多关于 glutInitWindowPosition, glutInitWindowSize 和 glutGet: <http://www.opengl.org/resources/libraries/glut/spec3/node11.html> <http://www.opengl.org/documentation/specs/glut/spec3/node70.html>
6466
6567最后,窗体应该采用glutCreateWindow创建。该函数将创建窗体并通过一参数设置其标题。因此它将返回该窗体的句柄。句柄可在其他需要该句柄的函数中使用。
6668
6769更多关于 glutCreateWindow: <http://www.opengl.org/resources/libraries/glut/spec3/node16.html>
6870
6971在您的程序进入主循环前,您必须设置些回调。您需要一个回调来画窗体,改变窗体尺寸及获取键盘输入。这些回调使用glutDisplayFunc, glutReshapeFunc 和 glutKeyboardFunc来设置。
7072
7173更多有关设置回调,请参见: <http://www.opengl.org/resources/libraries/glut/spec3/node45.html#SECTION00080000000000000000>
7274
7375您的绘图函数可能看起来像这样子:
7476
75-`procedure DrawGLScene; cdecl;`\
76-`begin`\
77-`  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);`\
78-`  glutSwapBuffers;`\
79-`end;`
77+``` pascal
78+procedure DrawGLScene; cdecl;
79+begin
80+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
81+ glutSwapBuffers;
82+end;
83+```
8084
8185这只能清空窗体为背景色并复位zbuffer。(别担心不懂zbuffer... 更多内容讲解在后面)
8286
8387您的窗体大小调整函数可能像这样:
8488
85-`procedure ReSizeGLScene(Width, Height: Integer); cdecl;`\
86-`begin`\
87-`  if Height = 0 then`\
88-`    Height := 1;`\
89+``` pascal
90+procedure ReSizeGLScene(Width, Height: Integer); cdecl;
91+begin
92+ if Height = 0 then
93+ Height := 1;
8994
90-`  glViewport(0, 0, Width, Height);`\
91-`  glMatrixMode(GL_PROJECTION);`\
92-`  glLoadIdentity;`\
93-`  gluPerspective(45, Width / Height, 0.1, 1000);`\
95+ glViewport(0, 0, Width, Height);
96+ glMatrixMode(GL_PROJECTION);
97+ glLoadIdentity;
98+ gluPerspective(45, Width / Height, 0.1, 1000);
9499
95-`  glMatrixMode(GL_MODELVIEW);`\
96-`  glLoadIdentity;`\
97-`end;`
100+ glMatrixMode(GL_MODELVIEW);
101+ glLoadIdentity;
102+end;
103+```
98104
99105这个代码告诉OpenGL窗体应该绘制在哪里,并且将矩阵设为期望值。(矩阵函数将会在后面介绍)
100106
101107键盘输入使用以下回调执行:
102108
103-`procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;`\
104-`begin`\
105-`  if Key = 27 then`\
106-`    Halt(0);`\
107-`end;`
109+``` pascal
110+procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
111+begin
112+ if Key = 27 then
113+ Halt(0);
114+end;
115+```
108116
109117该函数将告诉您的程序,若是按下ESC键就推出。GLUT为事件驱动,终止您程序的唯一方式就是调用您的回调函数中的Halt。若您以其他方式关闭窗体,窗体是消失了,但程序仍然会在主过程内继续无限地循环。
110118
111119要启动主循环,调用glutMainLoop.这将进入调用您回调函数的无限循环。
112120
113121您程序的主部分可能看起来如下:
114122
115-`const `\
116-`  AppWidth = 640; `\
117-`  AppHeight = 480; `\
123+``` pascal
124+const
125+ AppWidth = 640;
126+ AppHeight = 480;
118127
119-`procedure InitializeGL; `\
120-`begin `\
121-`  glClearColor(0.18, 0.20, 0.66, 0); `\
122-`end; `\
128+procedure InitializeGL;
129+begin
130+ glClearColor(0.18, 0.20, 0.66, 0);
131+end;
123132
124-`var `\
125-`  ScreenWidth, ScreenHeight: Integer; `\
126-`begin `\
127-`  glutInitPascal(True); `\
128-`  glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH); `\
129-`  glutInitWindowSize(AppWidth, AppHeight); `\
130-`  ScreenWidth := glutGet(GLUT_SCREEN_WIDTH); `\
131-`  ScreenHeight := glutGet(GLUT_SCREEN_HEIGHT); `\
132-`  glutInitWindowPosition((ScreenWidth - AppWidth) div 2,`\
133-`    (ScreenHeight - AppHeight) div 2); `\
134-`  glutCreateWindow('OpenGL Tutorial 1'); `\
133+var
134+ ScreenWidth, ScreenHeight: Integer;
135+begin
136+ glutInitPascal(True);
137+ glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
138+ glutInitWindowSize(AppWidth, AppHeight);
139+ ScreenWidth := glutGet(GLUT_SCREEN_WIDTH);
140+ ScreenHeight := glutGet(GLUT_SCREEN_HEIGHT);
141+ glutInitWindowPosition((ScreenWidth - AppWidth) div 2,
142+ (ScreenHeight - AppHeight) div 2);
143+ glutCreateWindow('OpenGL Tutorial 1');
135144
136-`  InitializeGL; `\
145+ InitializeGL;
137146
138-`  glutDisplayFunc(@DrawGLScene); `\
139-`  glutReshapeFunc(@ReSizeGLScene); `\
140-`  glutKeyboardFunc(@GLKeyboard); `\
147+ glutDisplayFunc(@DrawGLScene);
148+ glutReshapeFunc(@ReSizeGLScene);
149+ glutKeyboardFunc(@GLKeyboard);
141150
142-`  glutMainLoop; `\
143-`end.`
151+ glutMainLoop;
152+end.
153+```
144154
145155下一个教程将添加一些绘制简单图形的代码.
146156
147157从这里[Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145)下载源代码或者Linux/Win可执行文件。
148158
149159# 绘制简单图形
150160
151161**Note:** The following parts are almost only OpenGL, so they run under GLUT and LCL. You can recognize GLUT specific functions with the prefix 'glu'.
152162
153163This time we shall add just a few lines of code and focus on explanation of some of the OpenGL functions.
154164
155165Let us explain code you allready have.
156166
157-`  .`\
158-`  .`\
159-`  .`\
160-`  glMatrixMode(GL_PROJECTION);`\
161-`  glLoadIdentity;`\
162-`  gluPerspective(45, Width / Height, 0.1, 1000);`\
167+``` pascal
168+ .
169+ .
170+ .
171+ glMatrixMode(GL_PROJECTION);
172+ glLoadIdentity;
173+ gluPerspective(45, Width / Height, 0.1, 1000);
163174
164-`  glMatrixMode(GL_MODELVIEW);`\
165-`  glLoadIdentity;`\
166-`end;`
175+ glMatrixMode(GL_MODELVIEW);
176+ glLoadIdentity;
177+end;
178+```
167179
168180Using glMatrixMode function you chose which matrix you want to change. OpenGL works with 3 matrices: GL_MODELVIEW: this one is used to move vertex to model space. GL_PROJECTION: this one is used to convert 3d coordinate to 2d coordinate for finall pixel position. GL_TEXTURE: this one is used to alter texture coordinates.
169181
170182Once you chose matrix you want to change, you can call functions that affect matrix values. glLoadIdentity will reset matrix so it doesn't affect vertex position. Since almost all matrix functions multiply current matrix with a generated one, you sometimes need to clear matrix with this function.
171183
172184In order to set perspective matrix, you can use gluPerspective function. Four parameters present the field of view, aspect ratio, near and far plane. It's that simple.
173185
174186Now, you'll change model matrix... for this time, you just set it to identity.
175187
176188OK... and now, the code for drawing the first shape:
177189
178-`procedure DrawGLScene; cdecl;`\
179-`begin`\
180-`  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);`\
190+``` pascal
191+procedure DrawGLScene; cdecl;
192+begin
193+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
181194
182-`  glLoadIdentity;`\
183-`  glTranslatef(0, 0, -5);`\
195+ glLoadIdentity;
196+ glTranslatef(0, 0, -5);
184197
185-`  glBegin(GL_TRIANGLES);`\
186-`    glColor3f(1, 0, 0);`\
187-`    glVertex3f(-1, -1, 0);`\
198+ glBegin(GL_TRIANGLES);
199+ glColor3f(1, 0, 0);
200+ glVertex3f(-1, -1, 0);
188201
189-`    glColor3f(0, 1, 0);`\
190-`    glVertex3f(1, -1, 0);`\
202+ glColor3f(0, 1, 0);
203+ glVertex3f(1, -1, 0);
191204
192-`    glColor3f(0, 0, 1);`\
193-`    glVertex3f(0, 1, 0);`\
194-`  glEnd;`\
205+ glColor3f(0, 0, 1);
206+ glVertex3f(0, 1, 0);
207+ glEnd;
195208
196-`  glutSwapBuffers;`\
197-`end;`
209+ glutSwapBuffers;
210+end;
211+```
198212
199213We have allready used glClear function. It will just reset buffers. We'll skip next two functions and head for drawing ones.
200214
201215glBegin marks beginning of drawing block. After this function you can start entering vertices. Parameter describes how are vertices used when drawing: GL_POINTS: Treats each vertex as a single point. Vertex n defines point n. N points are drawn.
202216
203217GL_LINES: Treats each pair of vertices as an independent line segment. Vertices 2n-1 and 2n define line n. n/2 lines are drawn.
204218
205219GL_LINE_STRIP: Draws a connected group of line segments from the first vertex to the last. n-1 lines are drawn.
206220
207221GL_LINE_LOOP: Draws a connected group of line segments from the first vertex to the last, then back to the first. Vertices n and n+1 define line n. The last line, however, is defined by vertices n and 1. n lines are drawn.
208222
209223GL_TRIANGLES: Treats each triplet of vertices as an independent triangle. Vertices 3n-2, 3n-1 and 3n define triangle n. n/3 triangles are drawn.
210224
211225GL_TRIANGLE_STRIP: Draws a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. For odd n, vertices n, n+1 and n+2 define triangle n. For even n, vertices n+1, n and n+2 define triangle n. n-2 triangles are drawn.
212226
213227GL_TRIANGLE_FAN: Draws a connected group of triangles. One triangle is defined for each vertex presented after the first two vertices. Vertices 1. n+1 and n+2 define triangle n. n-2 triangles are drawn.
214228
215229GL_QUADS: Treats each group of four vertices as an independent quadrilateral. Vertices 4n-3, 4n-2, 4n-1 and 4n define quadrilateral n. n/4 quadrilaterals are drawn.
216230
217231GL_QUAD_STRIP: Draws a connected group of quadrilaterals. One quadrilateral is defined for each pair of vertices presented after the first pair. Vertices 2n-1, 2n, 2n+2 and 2n+1 define quadrilateral n. n/2-1 quadrilaterals are drawn. Note that the order in which vertices are used to construct a quadrilateral from strip data is different from that used with independent data.
218232
219233GL_POLYGON: Draws a single, convex polygon. Vertices 1 through n define this polygon.
220234
221235![](/wiki-images/f/f3/SimpleShapePic1.jpg) You'll draw single triangle and for that GL_TRIANGLES flag will do the trick. glVertex3f function defines the position of a vertex you want to draw. There are more glVertex* functions. Only difference is number and type of parameters they take. For instance... glVertex2i takes two parameters (x and y) of integer type. glVertex3f will almost always be just what you need.
222236
223237Before glVertex you can set color, material, texture... For simplicity you'll just specify color for each vertex in this tutorial. Color is set using glColor3f function. glColor can also take different set of parameters like glVertex.
224238
225239As we look through code we can see that Z is set to 0 for all vertices. Since you set near plane to 0.1, triangle will not be visible. That is where those two functions we skipped in the beginning jump in. We already know that glLoadIdentity reset matrix. glTranslatef moves triangles by X, Y and Z values you provide. Since you set Z to -5 (negative Z is farther from camera) all vertices will be drawn 5 units far from point of view and will be visible.
226240
227241In the end you call glEnd functions that finishes drawing. You could now start another drawing block with new glBegin function if you wish.
228242
229243Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
230244
231245# Using display lists
232246
233247Sometimes you'll need to draw some object multiple times on scene. OpenGL has ability to build display lists which make drawing a bit faster. Creating display list is very easy... just draw vertices as you did in previous tutorial and enclose them with glNewList and glEndList calls.
234248
235-`const`\
236-`  LIST_OBJECT = 1;`\
249+``` pascal
250+const
251+ LIST_OBJECT = 1;
237252
238-`procedure CreateList;`\
239-`begin`\
240-`  glNewList(LIST_OBJECT, GL_COMPILE);`\
241-`    glBegin(GL_TRIANGLE_FAN);`\
242-`      glColor3f(1, 0, 0);`\
243-`      glVertex3f(0, 0.5, 0);`\
253+procedure CreateList;
254+begin
255+ glNewList(LIST_OBJECT, GL_COMPILE);
256+ glBegin(GL_TRIANGLE_FAN);
257+ glColor3f(1, 0, 0);
258+ glVertex3f(0, 0.5, 0);
244259
245-`      glColor3f(1, 1, 0);`\
246-`      glVertex3f(-0.5, -0.5, 0.5);`\
260+ glColor3f(1, 1, 0);
261+ glVertex3f(-0.5, -0.5, 0.5);
247262
248-`      glColor3f(1, 1, 1);`\
249-`      glVertex3f(0.5, -0.5, 0.5);`\
263+ glColor3f(1, 1, 1);
264+ glVertex3f(0.5, -0.5, 0.5);
250265
251-`      glColor3f(0, 1, 1);`\
252-`      glVertex3f(0.5, -0.5, -0.5);`\
266+ glColor3f(0, 1, 1);
267+ glVertex3f(0.5, -0.5, -0.5);
253268
254-`      glColor3f(0, 0, 1);`\
255-`      glVertex3f(-0.5, -0.5, -0.5);`\
269+ glColor3f(0, 0, 1);
270+ glVertex3f(-0.5, -0.5, -0.5);
256271
257-`      glColor3f(0, 1, 0);`\
258-`      glVertex3f(-0.5, -0.5, 0.5);`\
259-`    glEnd;`\
272+ glColor3f(0, 1, 0);
273+ glVertex3f(-0.5, -0.5, 0.5);
274+ glEnd;
260275
261-`    glBegin(GL_QUADS);`\
262-`      glColor3f(1, 1, 0);`\
263-`      glVertex3f(-0.5, -0.5, 0.5);`\
276+ glBegin(GL_QUADS);
277+ glColor3f(1, 1, 0);
278+ glVertex3f(-0.5, -0.5, 0.5);
264279
265-`      glColor3f(1, 1, 1);`\
266-`      glVertex3f(0.5, -0.5, 0.5);`\
280+ glColor3f(1, 1, 1);
281+ glVertex3f(0.5, -0.5, 0.5);
267282
268-`      glColor3f(0, 1, 1);`\
269-`      glVertex3f(0.5, -0.5, -0.5);`\
283+ glColor3f(0, 1, 1);
284+ glVertex3f(0.5, -0.5, -0.5);
270285
271-`      glColor3f(0, 0, 1);`\
272-`      glVertex3f(-0.5, -0.5, -0.5);`\
286+ glColor3f(0, 0, 1);
287+ glVertex3f(-0.5, -0.5, -0.5);
273288
274-`      glColor3f(0, 1, 0);`\
275-`      glVertex3f(-0.5, -0.5, 0.5);`\
276-`    glEnd;`\
277-`  glEndList;`\
278-`end;`
289+ glColor3f(0, 1, 0);
290+ glVertex3f(-0.5, -0.5, 0.5);
291+ glEnd;
292+ glEndList;
293+end;
294+```
279295
280296glNewList creates new display list and all drawing functions will be recorded until glEndList is called. The first parameter for glNewList function is list ID. Every list is defined by it's ID. If list with given ID is already created it fill be cleared before recording. If the second parameter is GL_COMPILE then all drawing functions are just recorded, but if it is GL_COMPILE_AND_EXECUTE then they are recorded and executed automaticly.
281297
282298glIsList function can help you with display lists. It can tell if some list ID is already filled with data. Another useful function is glGenLists. It will create multiple empty display lists. You pass number of display lists you need and you get ID of the first one. If you require n lists, and get r ID, generated display lists are: r, r+1, r+2,..., r+n-1
283299
284300All created lists should be deleted. You will do that before program exits:
285301
286-`procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;`\
287-`begin`\
288-`  if Key = 27 then`\
289-`  begin`\
290-`    glDeleteLists(LIST_OBJECT, 1);`\
291-`    Halt(0);`\
292-`  end;`\
293-`end;`
302+``` pascal
303+procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
304+begin
305+ if Key = 27 then
306+ begin
307+ glDeleteLists(LIST_OBJECT, 1);
308+ Halt(0);
309+ end;
310+end;
311+```
294312
295313glDeleteLists takes 2 parameters, ID of display list and number of lists to delete. If ID is r, and number of lists to delete is n, deleted lists are: r, r+1, r+2,..., r+n-1
296314
297315Now you know how to create and delete display lists... let's see how to draw them:
298316
299-`procedure DrawGLScene; cdecl;`\
300-`begin`\
301-`  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);`\
317+``` pascal
318+procedure DrawGLScene; cdecl;
319+begin
320+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
302321
303-`  glLoadIdentity;`\
304-`  glTranslatef(-2, 0, -5);`\
305-`  glRotatef(40, 1, 0, 1);`\
306-`  glCallList(LIST_OBJECT);`\
322+ glLoadIdentity;
323+ glTranslatef(-2, 0, -5);
324+ glRotatef(40, 1, 0, 1);
325+ glCallList(LIST_OBJECT);
307326
308-`  glLoadIdentity;`\
309-`  glTranslatef(1, -2, -10);`\
310-`  glRotatef(62, 0, 1, 0);`\
311-`  glCallList(LIST_OBJECT);`\
327+ glLoadIdentity;
328+ glTranslatef(1, -2, -10);
329+ glRotatef(62, 0, 1, 0);
330+ glCallList(LIST_OBJECT);
312331
313-`  glLoadIdentity;`\
314-`  glTranslatef(-4, 0.5, -15);`\
315-`  glRotatef(200, 1, 0, 0);`\
316-`  glCallList(LIST_OBJECT);`\
332+ glLoadIdentity;
333+ glTranslatef(-4, 0.5, -15);
334+ glRotatef(200, 1, 0, 0);
335+ glCallList(LIST_OBJECT);
317336
318-`  glutSwapBuffers;`\
319-`end;`
337+ glutSwapBuffers;
338+end;
339+```
320340
321341![](/wiki-images/a/a5/DisplayListsPic1.jpg) Using glCallList you can draw only one display list. In this tutorial, before drawing display list, you change model matrix and draw object in different places. Some times you would like to draw multiple lists at once. That is possible using glCallLists function. It takes number of lists you want to draw, type of array that contains display list IDs and array with display list IDs. Type of list can be one of the following:
322342
323343GL_BYTE: list is treated as an array of signed bytes, each in the range -128 through 127.
324344
325345GL_UNSIGNED_BYTE: list is treated as an array of unsigned bytes, each in the range 0 through 255.
326346
327347GL_SHORT: list is treated as an array of signed two-byte integers, each in the range -32768 through 32767.
328348
329349GL_UNSIGNED_SHORT: list is treated as an array of unsigned two-byte integers, each in the range 0 through 65535.
330350
331351GL_INT: lists is treated as an array of signed four-byte integers.
332352
333353GL_UNSIGNED_INT: list is treated as an array of unsigned four-byte integers.
334354
335355GL_FLOAT: list is treated as an array of four-byte floating-point values.
336356
337357GL_2_BYTES: list is treated as an array of unsigned bytes. Each pair of bytes specifies a single display list ID. The value of the pair is computed as 256 times the unsigned value of the first byte plus the unsigned value of the second byte.
338358
339359GL_3_BYTES: list is treated as an array of unsigned bytes. Each triplet of bytes specifies a single display list ID. The value of the triplet is computed as 65536 times the unsigned value of the first byte, plus 256 times the unsigned value of the second byte, plus the unsigned value of the third byte.
340360
341361GL_4_BYTES: list is treated as an array of unsigned bytes. Each quadruplet of bytes specifies a single display list ID. The value of the quadruplet is computed as 16777216 times the unsigned value of the first byte, plus 65536 times the unsigned value of the second byte, plus 256 times the unsigned value of the third byte, plus the unsigned value of the fourth byte.
342362
343363That is for now. Next tutorial will show how to create little planetary system. We'll talk about matrices and how to make animated scene that doesn't depend of number of frames per second.
344364
345365Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
346366
347367# Full screen animation
348368
349369Entering full screen mode is easy with GLUT. Let's change main part of the program:
350370
351-`const`\
352-`  FSMode = '800x600:32@75';`\
371+``` pascal
372+const
373+ FSMode = '800x600:32@75';
353374
354-`begin`\
355-`  glutInitPascal(False);`\
356-`  glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);`\
357-`  glutGameModeString(FSMode);`\
358-`  glutEnterGameMode;`\
359-`  glutSetCursor(GLUT_CURSOR_NONE);`\
375+begin
376+ glutInitPascal(False);
377+ glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
378+ glutGameModeString(FSMode);
379+ glutEnterGameMode;
380+ glutSetCursor(GLUT_CURSOR_NONE);
360381
361-`  InitializeGL;`\
382+ InitializeGL;
362383
363-`  glutDisplayFunc(@DrawGLScene);`\
364-`  glutReshapeFunc(@ReSizeGLScene);`\
365-`  glutKeyboardFunc(@GLKeyboard);`\
366-`  glutIdleFunc(@DrawGLScene);`\
384+ glutDisplayFunc(@DrawGLScene);
385+ glutReshapeFunc(@ReSizeGLScene);
386+ glutKeyboardFunc(@GLKeyboard);
387+ glutIdleFunc(@DrawGLScene);
367388
368-`  glutMainLoop;`\
369-`end.`
389+ glutMainLoop;
390+end.
391+```
370392
371393Since we don't want GLUT to parse command line this time we call glutInitPascal with False parameter. As you can see, there is no code for window creation. GLUT have glutEnterGameMode that create full screen window. To specify what kind of full screen mode you want, you call glutGameModeString function which takes string that defines mode you like. Format of that string is:
372394
373-`[width "x" height][":" bpp]["@" hertz]`
395+```text
396+[width "x" height][":" bpp]["@" hertz]
397+```
374398
375399In FSMode string we declared that full screen mode should be 800x600, with 32bit pallete and 75Hz refresh. It is possible to skip one of the group. If you omit size, GLUT will try to use current one or first smaller that can work. That policy is used and for other parameters.
376400
377401Usually in full screen mode cursor is not visible. To hide cursor you use glutSetCursor function. It takes only one parameter which describes cursor you would like to see:
378402
379-`GLUT_CURSOR_RIGHT_ARROW`\
380-`GLUT_CURSOR_LEFT_ARROW`\
381-`GLUT_CURSOR_INFO`\
382-`GLUT_CURSOR_DESTROY`\
383-`GLUT_CURSOR_HELP`\
384-`GLUT_CURSOR_CYCLE`\
385-`GLUT_CURSOR_SPRAY`\
386-`GLUT_CURSOR_WAIT`\
387-`GLUT_CURSOR_TEXT`\
388-`GLUT_CURSOR_CROSSHAIR`\
389-`GLUT_CURSOR_UP_DOWN`\
390-`GLUT_CURSOR_LEFT_RIGHT`\
391-`GLUT_CURSOR_TOP_SIDE`\
392-`GLUT_CURSOR_BOTTOM_SIDE`\
393-`GLUT_CURSOR_LEFT_SIDE`\
394-`GLUT_CURSOR_RIGHT_SIDE`\
395-`GLUT_CURSOR_TOP_LEFT_CORNER`\
396-`GLUT_CURSOR_TOP_RIGHT_CORNER`\
397-`GLUT_CURSOR_BOTTOM_RIGHT_CORNER`\
398-`GLUT_CURSOR_BOTTOM_LEFT_CORNER`\
399-`GLUT_CURSOR_FULL_CROSSHAIR`\
400-`GLUT_CURSOR_NONE`\
401-`GLUT_CURSOR_INHERIT`
403+```text
404+GLUT_CURSOR_RIGHT_ARROW
405+GLUT_CURSOR_LEFT_ARROW
406+GLUT_CURSOR_INFO
407+GLUT_CURSOR_DESTROY
408+GLUT_CURSOR_HELP
409+GLUT_CURSOR_CYCLE
410+GLUT_CURSOR_SPRAY
411+GLUT_CURSOR_WAIT
412+GLUT_CURSOR_TEXT
413+GLUT_CURSOR_CROSSHAIR
414+GLUT_CURSOR_UP_DOWN
415+GLUT_CURSOR_LEFT_RIGHT
416+GLUT_CURSOR_TOP_SIDE
417+GLUT_CURSOR_BOTTOM_SIDE
418+GLUT_CURSOR_LEFT_SIDE
419+GLUT_CURSOR_RIGHT_SIDE
420+GLUT_CURSOR_TOP_LEFT_CORNER
421+GLUT_CURSOR_TOP_RIGHT_CORNER
422+GLUT_CURSOR_BOTTOM_RIGHT_CORNER
423+GLUT_CURSOR_BOTTOM_LEFT_CORNER
424+GLUT_CURSOR_FULL_CROSSHAIR
425+GLUT_CURSOR_NONE
426+GLUT_CURSOR_INHERIT
427+```
402428
403429glutIdleFunc defines callback function that you want to be called every time you program has no messages to process. Since we just want to render new frame if there is nothing to do, just set idle function to DrawGLScene. Some other tutorials show that idle function should send refresh message insted of drawing, but that way I have 50-100 frames less than using method I described.
404430
405431Now, let's look at the program termination where you need to exit full screen mode:
406432
407-`procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;`\
408-`begin`\
409-`  if Key = 27 then`\
410-`  begin`\
411-`    glutLeaveGameMode;`\
412-`    Halt(0);`\
413-`  end;`\
414-`end;`
433+``` pascal
434+procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
435+begin
436+ if Key = 27 then
437+ begin
438+ glutLeaveGameMode;
439+ Halt(0);
440+ end;
441+end;
442+```
415443
416444As you can see, all you need to do is to call glutLeaveGameMode.
417445
418446Now, we'll introduce some new matrix functions. First, let's change ReSizeGLScene function:
419447
420-`procedure ReSizeGLScene(Width, Height: Integer); cdecl;`\
421-`begin`\
422-`  .`\
423-`  .`\
424-`  .`
448+``` pascal
449+procedure ReSizeGLScene(Width, Height: Integer); cdecl;
450+begin
451+ .
452+ .
453+ .
454+```
425455
426-`  glMatrixMode(GL_MODELVIEW);`\
427-`  glLoadIdentity;`\
428-`  gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);`\
429-`end;`
456+``` pascal
457+ glMatrixMode(GL_MODELVIEW);
458+ glLoadIdentity;
459+ gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
460+end;
461+```
430462
431463gluLookAt create matrix that will define from where are you look to objects. First 3 parameters are X, Y and Z coordinate of position of camera. Next 3 parameters are X, Y and Z coordinate of point where camera look at, and last 3 parameters defines "up" vector (where is "up" for the camera). Usually, up is positive y axis.
432464
433465OK, let's draw now. Since you set matrix with gluLookAt that should be used with all objects, you can't just use glLoadIdentity to reset matrix for next object... you'll save previous matrix state and restore it after object is drawn:
434466
435-`procedure DrawGLScene; cdecl;`\
436-`var`\
437-`  T: Single;`\
438-`begin`\
439-`  T := glutGet(GLUT_ELAPSED_TIME) / 1000;`\
440-`  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);`\
467+``` pascal
468+procedure DrawGLScene; cdecl;
469+var
470+ T: Single;
471+begin
472+ T := glutGet(GLUT_ELAPSED_TIME) / 1000;
473+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
441474
442-`  glPushMatrix;`\
443-`    glRotatef(5 * T, 0, 1, 0);`\
444-`    glColor3f(1, 1, 0);`\
445-`    glutWireSphere(2, 20, 20);`\
446-`  glPopMatrix;`\
475+ glPushMatrix;
476+ glRotatef(5 * T, 0, 1, 0);
477+ glColor3f(1, 1, 0);
478+ glutWireSphere(2, 20, 20);
479+ glPopMatrix;
447480
448-`  glPushMatrix;`\
449-`    glRotatef(90 * T, 0, 1, 0);`\
450-`    glTranslatef(5, 0, 0);`\
451-`    glRotatef(40 * T, 0, 1, 0);`\
452-`    glColor3f(1, 0, 0);`\
453-`    glutWireSphere(0.6, 10, 10);`\
454-`  glPopMatrix;`\
481+ glPushMatrix;
482+ glRotatef(90 * T, 0, 1, 0);
483+ glTranslatef(5, 0, 0);
484+ glRotatef(40 * T, 0, 1, 0);
485+ glColor3f(1, 0, 0);
486+ glutWireSphere(0.6, 10, 10);
487+ glPopMatrix;
455488
456-`  glPushMatrix;`\
457-`    glRotatef(60 * T, 0, 1, 0);`\
458-`    glTranslatef(-3, 0, 9);`\
459-`    glRotatef(50 * T, 0, 1, 0);`\
460-`    glColor3f(0, 1, 0);`\
461-`    glutWireSphere(1, 16, 16);`\
489+ glPushMatrix;
490+ glRotatef(60 * T, 0, 1, 0);
491+ glTranslatef(-3, 0, 9);
492+ glRotatef(50 * T, 0, 1, 0);
493+ glColor3f(0, 1, 0);
494+ glutWireSphere(1, 16, 16);
462495
463-`    glPushMatrix;`\
464-`      glRotatef(360 * T, 0, 1, 0);`\
465-`      glTranslatef(-1.7, 0, 0);`\
466-`      glRotatef(50 * T, 0, 1, 0);`\
467-`      glColor3f(0, 0, 1);`\
468-`      glutWireSphere(0.4, 10, 10);`\
469-`    glPopMatrix;`\
496+ glPushMatrix;
497+ glRotatef(360 * T, 0, 1, 0);
498+ glTranslatef(-1.7, 0, 0);
499+ glRotatef(50 * T, 0, 1, 0);
500+ glColor3f(0, 0, 1);
501+ glutWireSphere(0.4, 10, 10);
502+ glPopMatrix;
470503
471-`  glPopMatrix;`\
504+ glPopMatrix;
472505
473-`  glutSwapBuffers;`\
474-`end;`
506+ glutSwapBuffers;
507+end;
508+```
475509
476510![](/wiki-images/5/51/FullScreenAnimationPic1.jpg) glPushMatrix i glPopMatrix are used to save and restore matrix state. As you can see, we save matrix state, then change matrix in order to draw object in right place, and then restore old matrix state.
477511
478512You may wonder what is T variable for. Well, it is used to determen animation speed. Every change that depends on time is multiplied with T. That way animation speed is constant on every frame rate. glutGet function with GLUT_ELAPSED_TIME parameter returns time in milliseconds from glutInit is called. By dividing that value with 1000, we get time in seconds.
479513
480514glRotatef function create rotation matrix. First parameter is angle in degrees, and last 3 parameters defines axis around which rotation will be done. Since you multiplied angle with T, object will be rotated by that angle in exactly 1 second.
481515
482516Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
483517
484518# Light
485519
486520This tutorial will introduce some light to the scene. You'll make rotating cube and one light which will add some realism to the scene, but first let's make some utility unit.
487521
488522For now it will have only basic functions to help us getting current and delta (time that elapsed from one render to other render call) times and for calculating frames per second.
489523
490-`unit utils;`\
524+``` pascal
525+unit utils;
491526
492-`{$mode objfpc}{$H+}`\
527+{$mode objfpc}{$H+}
493528
494-`interface`\
529+interface
495530
496-`uses`\
497-`  glut;`\
531+uses
532+ glut;
498533
499-`function GetTotalTime: Single;`\
500-`function GetDeltaTime: Single;`\
501-`procedure FrameRendered(Count: Integer = 1);`\
502-`function GetFPS: Single;`\
534+function GetTotalTime: Single;
535+function GetDeltaTime: Single;
536+procedure FrameRendered(Count: Integer = 1);
537+function GetFPS: Single;
503538
504-`implementation`\
539+implementation
505540
506-`var`\
507-`  OldTime: Integer = 0;`\
508-`  FPSTime: Integer = 0;`\
509-`  FPSCount: Integer = 0;`\
541+var
542+ OldTime: Integer = 0;
543+ FPSTime: Integer = 0;
544+ FPSCount: Integer = 0;
510545
511-`function GetTotalTime: Single;`\
512-`begin`\
513-`  Result := glutGet(GLUT_ELAPSED_TIME) / 1000;`\
514-`end;`\
546+function GetTotalTime: Single;
547+begin
548+ Result := glutGet(GLUT_ELAPSED_TIME) / 1000;
549+end;
515550
516-`function GetDeltaTime: Single;`\
517-`var`\
518-`  NewTime: Integer;`\
519-`begin`\
520-`  NewTime := glutGet(GLUT_ELAPSED_TIME);`\
521-`  Result := (NewTime - OldTime) / 1000;`\
522-`  OldTime := NewTime;`\
523-`end;`\
551+function GetDeltaTime: Single;
552+var
553+ NewTime: Integer;
554+begin
555+ NewTime := glutGet(GLUT_ELAPSED_TIME);
556+ Result := (NewTime - OldTime) / 1000;
557+ OldTime := NewTime;
558+end;
524559
525-`procedure FrameRendered(Count: Integer);`\
526-`begin`\
527-`  Inc(FPSCount, Count);`\
528-`end;`\
560+procedure FrameRendered(Count: Integer);
561+begin
562+ Inc(FPSCount, Count);
563+end;
529564
530-`function GetFPS: Single;`\
531-`var`\
532-`  NewTime: Integer;`\
533-`begin`\
534-`  NewTime := glutGet(GLUT_ELAPSED_TIME);`\
565+function GetFPS: Single;
566+var
567+ NewTime: Integer;
568+begin
569+ NewTime := glutGet(GLUT_ELAPSED_TIME);
535570
536-`  Result := FPSCount / ((NewTime - FPSTime) / 1000);`\
571+ Result := FPSCount / ((NewTime - FPSTime) / 1000);
537572
538-`  FPSTime := NewTime;`\
539-`  FPSCount := 0;`\
540-`end;`\
573+ FPSTime := NewTime;
574+ FPSCount := 0;
575+end;
541576
542-`end.`
577+end.
578+```
543579
544580As you can see, there is nothing complicated in this unit. Time is simply saved betwen calls and difference is returned. FrameRendered should be called every time you draw scene so function can calculate FPS.
545581
546582Now, let's have fun with lights.
547583
548584OpenGL have several types of light... ambient, diffuse, point, spot, specular and emissive light.
549585
550586Ambient light is something like Sun. When sun rays pass through the window of a room they hit the walls and are reflected and scattered into all different directions which averagely brightens up the whole room. All vertices are lit with ambient light.
551587
552588Diffuse light can be represented as parallel light rays comming from far away. They will lit only vertices that are oriented towards the light source.
553589
554590Point light lights all around it. It is like a fire ball, it send light rays all around it and lights vertices that are oriented towards light source and that are close enough.
555591
556592Spot light is like light from flashlight. It is simply a point light source with a small light cone radius. All vertices that falls inside of cone and are close enough are lit.
557593
558594Just like Diffuse light, Specular light is a directional type of light. It comes from one particular direction. The difference between the two is that specular light reflects off the surface in a sharp and uniform way. The rendering of specular light relies on the angle between the viewer and the light source. From the viewer’s standpoint specular light creates a highlighted area on the surface of the viewed object known as specular highlight or specular reflection.
559595
560596Emissive light is a little different than any other previously explained light components. This light comes out of object you draw but don't lit other objects in nearby.
561597
562598For simplicity we'll use only diffuse light in this tutorial. Later on, some other lights may appear in tutorials :)
563599
564600Let's see how to enable light in scene:
565601
566-`const`\
567-`  DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);`\
602+``` pascal
603+const
604+ DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
568605
569-`  glEnable(GL_LIGHTING);`\
570-`  glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);`\
571-`  glEnable(GL_LIGHT0);`
606+ glEnable(GL_LIGHTING);
607+ glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
608+ glEnable(GL_LIGHT0);
609+```
572610
573611As you see, we enable lighting in OpenGL so lights affect scene you are rendering. Light parameters are set with glLightfv function. It takes 3 parameters... one for light number you want to change (OpenGL suports up to 8 lights), next tells OpenGL what light parameter to change, and the last one is new parameter for light. You'll set just diffuse color for light in this tutorial. After that, you can enable light and there will be light in the scene... but... that is not all.
574612
575613More about glLightfv: <http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html>
576614
577615If you want to use lights you can't just set color for vertex... you must set material for vertices. Let's setup material for drawing:
578616
579-`glEnable(GL_COLOR_MATERIAL);`\
580-`glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);`
617+```text
618+glEnable(GL_COLOR_MATERIAL);
619+glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
620+```
581621
582622![](/wiki-images/e/ed/LightPic1.jpg) You expected something more complicated, do you? :) Well, this code allows us to use glColor function to set material to vertices. By using glEnable function and GL_COLOR_MATERIAL flag, you can define what material properties will glColor change. glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE) tells OpenGL that glColor changes ambient and diffuse material. We'll discus materials more in later tutorials.
583623
584624One more thing that is important when using lights... every vertex must have normal associated with it. Normal is used to find the direction of vertex so light can be calculated properly. You'll use GLUT function to draw cube and it provides normals for us, so this time we'll just walk by normals.
585625
586626After all those setting ups, light will shine up your cube :)
587627
588628Part of the text is copied from [The OpenGL Light Bible](http://www.falloutsoftware.com/tutorials/gl/gl8.htm)
589629
590630Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
591631
592632# Bitmap fonts
593633
594634Games and programs usually need to write some text on screen. GLUT provides several functions for drawing chars that are platform independent.
595635
596636First, we'll show how to use default bitmap fonts. Almost all code additions will be made to utils.pas unit.
597637
598638Since text will be drawn in 2D, we'll need to know width and height of viewport... so, we'll write two functions for that:
599639
600-`function glGetViewportWidth: Integer;`\
601-`var`\
602-`  Rect: array[0..3] of Integer;`\
603-`begin`\
604-`  glGetIntegerv(GL_VIEWPORT, @Rect);`\
605-`  Result := Rect[2] - Rect[0];`\
606-`end;`\
640+``` pascal
641+function glGetViewportWidth: Integer;
642+var
643+ Rect: array[0..3] of Integer;
644+begin
645+ glGetIntegerv(GL_VIEWPORT, @Rect);
646+ Result := Rect[2] - Rect[0];
647+end;
607648
608-`function glGetViewportHeight: Integer;`\
609-`var`\
610-`  Rect: array[0..3] of Integer;`\
611-`begin`\
612-`  glGetIntegerv(GL_VIEWPORT, @Rect);`\
613-`  Result := Rect[3] - Rect[1];`\
614-`end;`
649+function glGetViewportHeight: Integer;
650+var
651+ Rect: array[0..3] of Integer;
652+begin
653+ glGetIntegerv(GL_VIEWPORT, @Rect);
654+ Result := Rect[3] - Rect[1];
655+end;
656+```
615657
616658We just get left/right, top/bottom and calculate width/height by subtracting them.
617659
618660There must be functions for entering and leaving 2D mode:
619661
620-`procedure glEnter2D;`\
621-`begin`\
622-`  glMatrixMode(GL_PROJECTION);`\
623-`  glPushMatrix;`\
624-`  glLoadIdentity;`\
625-`  gluOrtho2D(0, glGetViewportWidth, 0, glGetViewportHeight);`\
662+``` pascal
663+procedure glEnter2D;
664+begin
665+ glMatrixMode(GL_PROJECTION);
666+ glPushMatrix;
667+ glLoadIdentity;
668+ gluOrtho2D(0, glGetViewportWidth, 0, glGetViewportHeight);
626669
627-`  glMatrixMode(GL_MODELVIEW);`\
628-`  glPushMatrix;`\
629-`  glLoadIdentity;`\
670+ glMatrixMode(GL_MODELVIEW);
671+ glPushMatrix;
672+ glLoadIdentity;
630673
631-`  glDisable(GL_DEPTH_TEST);`\
632-`end;`\
674+ glDisable(GL_DEPTH_TEST);
675+end;
633676
634-`procedure glLeave2D;`\
635-`begin`\
636-`  glMatrixMode(GL_PROJECTION);`\
637-`  glPopMatrix;`\
638-`  glMatrixMode(GL_MODELVIEW);`\
639-`  glPopMatrix;`\
677+procedure glLeave2D;
678+begin
679+ glMatrixMode(GL_PROJECTION);
680+ glPopMatrix;
681+ glMatrixMode(GL_MODELVIEW);
682+ glPopMatrix;
640683
641-`  glEnable(GL_DEPTH_TEST);`\
642-`end;`
684+ glEnable(GL_DEPTH_TEST);
685+end;
686+```
643687
644688When entering 2D mode, we save current matrices and set 2D matrix using gluOrtho2D function. This way if we draw some thing on 100, 100 it will be drawn on exactly 100 pixels from left edge of window, and 100 pixels form bottom edge (positive Y is up). Also, we disable ZBuffer. This way text won't alter ZBuffer.
645689
646690Leaving 2D mode just returns old matrices and enable ZBuffer.
647691
648692Now, we can create function for text drawing:
649693
650-`procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);`\
651-`var`\
652-`  I: Integer;`\
653-`begin`\
654-`  glRasterPos2f(X, Y);`\
655-`  for I := 1 to Length(Text) do`\
656-`    glutBitmapCharacter(Font, Integer(Text[I]));`\
657-`end;`
694+``` pascal
695+procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
696+var
697+ I: Integer;
698+begin
699+ glRasterPos2f(X, Y);
700+ for I := 1 to Length(Text) do
701+ glutBitmapCharacter(Font, Integer(Text[I]));
702+end;
703+```
658704
659705glutBitmapCharacter can draw only one character of selected font. First parameter is desired font (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 or GLUT_BITMAP_HELVETICA_18) and other one is character.
660706
661707Character will be drawn at current raster position. To set desired raster position we call glRasterPos function. glRasterPos can handle different number and types of parameters just like glVertex function. Coordinate specified is transformed by model and projection matrix to get 2D coordinate where new raster position will be. Since we entered 2D mode, X and Y coordinates are actual 2D coordinates where drawing will occur.
662708
663709This new functions will make text drawing very easy:
664710
665-`procedure DrawGLScene; cdecl;`\
666-`begin`\
667-`  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);`\
711+``` pascal
712+procedure DrawGLScene; cdecl;
713+begin
714+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
668715
669-`  glLoadIdentity;`\
670-`  glTranslatef(0, 0, -5);`\
671-`  glRotatef(GetTotalTime * 10, 0, 0.5, 0.5);`\
716+ glLoadIdentity;
717+ glTranslatef(0, 0, -5);
718+ glRotatef(GetTotalTime * 10, 0, 0.5, 0.5);
672719
673-`  glColor3f(1, 0, 0);`\
674-`  glutSolidCube(2);`\
720+ glColor3f(1, 0, 0);
721+ glutSolidCube(2);
675722
676-`  glEnter2D;`\
723+ glEnter2D;
677724
678-`  glColor3f(0.2, 0.8 + 0.2 * Sin(GetTotalTime * 5), 0);`\
679-`  glWrite(20, glGetViewportHeight - 20, GLUT_BITMAP_8_BY_13,`\
680-`    Format('OpenGL Tutorial :: Bitmap Fonts :: FPS - %.2f FPS', [FPS]));`\
725+ glColor3f(0.2, 0.8 + 0.2 * Sin(GetTotalTime * 5), 0);
726+ glWrite(20, glGetViewportHeight - 20, GLUT_BITMAP_8_BY_13,
727+ Format('OpenGL Tutorial :: Bitmap Fonts :: FPS - %.2f FPS', [FPS]));
681728
682-`  glColor3f(1, 1, 1);`\
683-`  glWrite(50, glGetViewportHeight - 60, GLUT_BITMAP_9_BY_15, 'GLUT_BITMAP_9_BY_15');`\
684-`  glWrite(50, glGetViewportHeight - 90, GLUT_BITMAP_8_BY_13, 'GLUT_BITMAP_8_BY_13');`\
685-`  glWrite(50, glGetViewportHeight - 120, GLUT_BITMAP_TIMES_ROMAN_10, 'GLUT_BITMAP_TIMES_ROMAN_10');`\
686-`  glWrite(50, glGetViewportHeight - 150, GLUT_BITMAP_TIMES_ROMAN_24, 'GLUT_BITMAP_TIMES_ROMAN_24');`\
687-`  glWrite(50, glGetViewportHeight - 180, GLUT_BITMAP_HELVETICA_10, 'GLUT_BITMAP_HELVETICA_10');`\
688-`  glWrite(50, glGetViewportHeight - 210, GLUT_BITMAP_HELVETICA_12, 'GLUT_BITMAP_HELVETICA_12');`\
689-`  glWrite(50, glGetViewportHeight - 240, GLUT_BITMAP_HELVETICA_18, 'GLUT_BITMAP_HELVETICA_18');`\
729+ glColor3f(1, 1, 1);
730+ glWrite(50, glGetViewportHeight - 60, GLUT_BITMAP_9_BY_15, 'GLUT_BITMAP_9_BY_15');
731+ glWrite(50, glGetViewportHeight - 90, GLUT_BITMAP_8_BY_13, 'GLUT_BITMAP_8_BY_13');
732+ glWrite(50, glGetViewportHeight - 120, GLUT_BITMAP_TIMES_ROMAN_10, 'GLUT_BITMAP_TIMES_ROMAN_10');
733+ glWrite(50, glGetViewportHeight - 150, GLUT_BITMAP_TIMES_ROMAN_24, 'GLUT_BITMAP_TIMES_ROMAN_24');
734+ glWrite(50, glGetViewportHeight - 180, GLUT_BITMAP_HELVETICA_10, 'GLUT_BITMAP_HELVETICA_10');
735+ glWrite(50, glGetViewportHeight - 210, GLUT_BITMAP_HELVETICA_12, 'GLUT_BITMAP_HELVETICA_12');
736+ glWrite(50, glGetViewportHeight - 240, GLUT_BITMAP_HELVETICA_18, 'GLUT_BITMAP_HELVETICA_18');
690737
691-`  glColor3f(0.5, 0.5, 1);`\
692-`  glWrite(`\
693-`    glGetViewportWidth - glutBitmapLength(GLUT_BITMAP_9_BY_15, LazText) - 5,`\
694-`    10, GLUT_BITMAP_9_BY_15, LazText);`\
738+ glColor3f(0.5, 0.5, 1);
739+ glWrite(
740+ glGetViewportWidth - glutBitmapLength(GLUT_BITMAP_9_BY_15, LazText) - 5,
741+ 10, GLUT_BITMAP_9_BY_15, LazText);
695742
696-`  glLeave2D;`\
743+ glLeave2D;
697744
698-`  glutSwapBuffers;`\
745+ glutSwapBuffers;
699746
700-`  FrameRendered;`\
701-`end;`
747+ FrameRendered;
748+end;
749+```
702750
703751![](/wiki-images/b/ba/BitmapFontsPic1.jpg) We draw red cube and rotate it, and some text to show how various bitmap fonts look like. glutBitmapLength function is used to find width of string so it could be aligned to right. Code can easily be altered to center text.
704752
705753Note: See how cube looks without light.
706754
707755Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
708756
709757# Textures
710758
711759It's time to use textures :)
712760
713761This tutorial will show how to draw textured polygons and how to blend textures using multipass technic. Since OpenGL has no builtin mechanism for loading textures, we'll use external library: [Vampyre Imaging Library](http://imaginglib.sourceforge.net/). We'll use just OpenGL helper functions, but you may find this lib handy for some other things to.
714762
715763Let's get started... we'll create display list for drawing textured rectangle:
716764
717-`procedure CreateList;`\
718-`begin`\
719-`  glNewList(LIST_OBJECT, GL_COMPILE);`\
720-`    glBegin(GL_QUADS);`\
721-`      glTexCoord2f(1, 0);`\
722-`      glVertex3f( 2, 2, 0);`\
723-`      glTexCoord2f(0, 0);`\
724-`      glVertex3f(-2, 2, 0);`\
725-`      glTexCoord2f(0, 1);`\
726-`      glVertex3f(-2,-2, 0);`\
727-`      glTexCoord2f(1, 1);`\
728-`      glVertex3f( 2,-2, 0);`\
729-`    glEnd;`\
730-`  glEndList;`\
731-`end;`
765+``` pascal
766+procedure CreateList;
767+begin
768+ glNewList(LIST_OBJECT, GL_COMPILE);
769+ glBegin(GL_QUADS);
770+ glTexCoord2f(1, 0);
771+ glVertex3f( 2, 2, 0);
772+ glTexCoord2f(0, 0);
773+ glVertex3f(-2, 2, 0);
774+ glTexCoord2f(0, 1);
775+ glVertex3f(-2,-2, 0);
776+ glTexCoord2f(1, 1);
777+ glVertex3f( 2,-2, 0);
778+ glEnd;
779+ glEndList;
780+end;
781+```
732782
733783Notice glTexCoord functions. They are used to specify which part of texture is assigned to vertex. Coordinates defined in this functions are from 0 to 1 (values greater than 1 are allowed but can generate different results). 0 is first pixel and 1 is last pixel. So, 0.5 will be right in the middle of texture.
734784
735785Texture loading is extremely easy with Vampyre Imaging Library:
736786
737-`var`\
738-`  Tex1, Tex2: GLuint;`\
787+``` pascal
788+var
789+ Tex1, Tex2: GLuint;
739790
740-`procedure InitializeGL;`\
741-`begin`\
742-`  glClearColor(0, 0, 0, 0);`\
743-`  Tex1 := LoadGLTextureFromFile('ashwood.bmp');`\
744-`  Tex2 := LoadGLTextureFromFile('Flare.bmp');`\
745-`  glEnable(GL_TEXTURE_2D);`\
746-`end;`
791+procedure InitializeGL;
792+begin
793+ glClearColor(0, 0, 0, 0);
794+ Tex1 := LoadGLTextureFromFile('ashwood.bmp');
795+ Tex2 := LoadGLTextureFromFile('Flare.bmp');
796+ glEnable(GL_TEXTURE_2D);
797+end;
798+```
747799
748800LoadGLTextureFromFile loads texture from file and returns it's ID. When texture is loaded it is allready setup for rendering. Last line just enables 2D textures.
749801
750802To draw textured polygon you have to bind texture and setup texture coordinations (texture coordinations are set in display list in this tutorial):
751803
752-`  ...`\
753-`  glLoadIdentity;`\
754-`  glTranslatef(-5, 0, -15);`\
755-`  glBindTexture(GL_TEXTURE_2D, Tex1);`\
756-`  glCallList(LIST_OBJECT);`\
757-`  ...`
804+```text
805+ ...
806+ glLoadIdentity;
807+ glTranslatef(-5, 0, -15);
808+ glBindTexture(GL_TEXTURE_2D, Tex1);
809+ glCallList(LIST_OBJECT);
810+ ...
811+```
758812
759813glBindTexture function is used to select texture. When you draw polygins they will have selected texture on them. It's that easy :)
760814
761815So, using one texture is easy... but how to blend two textures. Basicly you draw polygon once with one texture, setup blending parameters, and draw polygon once more time with other texture. You can blend houndreds of textures this way. Let's see how code for this looks:
762816
763-`  ...`\
764-`  glLoadIdentity;`\
765-`  glTranslatef(5, 0, -15);`\
766-`  glBindTexture(GL_TEXTURE_2D, Tex1);`\
767-`  glCallList(LIST_OBJECT);`\
817+```text
818+ ...
819+ glLoadIdentity;
820+ glTranslatef(5, 0, -15);
821+ glBindTexture(GL_TEXTURE_2D, Tex1);
822+ glCallList(LIST_OBJECT);
768823
769-`  glEnable(GL_BLEND);`\
770-`  glBlendFunc(GL_ZERO, GL_SRC_COLOR);`\
771-`  glLoadIdentity;`\
772-`  glTranslatef(5, 0, -15);`\
773-`  glBindTexture(GL_TEXTURE_2D, Tex2);`\
774-`  glCallList(LIST_OBJECT);`\
775-`  glDisable(GL_BLEND);`\
776-`...`
824+ glEnable(GL_BLEND);
825+ glBlendFunc(GL_ZERO, GL_SRC_COLOR);
826+ glLoadIdentity;
827+ glTranslatef(5, 0, -15);
828+ glBindTexture(GL_TEXTURE_2D, Tex2);
829+ glCallList(LIST_OBJECT);
830+ glDisable(GL_BLEND);
831+...
832+```
777833
778834As you can see, polygon is drawn first time like we allready know. Before second drawing we enable blending by calling glEnable(GL_BLEND). Blending means that finall pixel color is calculated like this:
779835
780-`DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND`
836+```text
837+DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND
838+```
781839
782840SRCBLEND and DESTBLEND are defined using glBlendFunc function. In this tutorial we set SRCBLEND to GL_ZERO (zero) and DESTBLENT to GL_SRC_COLOR (DrawingColor) and finall color is then:
783841
784-`DrawingColor * 0 + BackgroundColor * DrawingColor`
842+```text
843+DrawingColor * 0 + BackgroundColor * DrawingColor
844+```
785845
786846![](/wiki-images/d/d4/TexturesPic1.jpg)
787847
788848This means that background will get darker when you draw with dark colors... when you draw with white color, background color will not be changed. The result will look like this
789849
790850Next time, we'll use extensions to show how to use singlepass multitexturing.
791851
792852Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
793853
794854# Multitexturing (extensions)
795855
796856When youknow multipass multi texturing, singlepass is very easy. Texturing is separated in stages. First stage setup and draw first texture, second stage draws another one and so on. All you have to do is to setup texture stages and to render object.
797857
798858Let's see how code looks like:
799859
800-`procedure InitializeGL;`\
801-`begin`\
802-`  Load_GL_ARB_multitexture;`\
803-`  glClearColor(0, 0, 0, 0);`\
804-`  Tex1 := LoadGLTextureFromFile('Lazarus.bmp');`\
805-`  Tex2 := LoadGLTextureFromFile('Mask.bmp');`\
806-`  glActiveTextureARB(GL_TEXTURE0_ARB);`\
807-`  glEnable(GL_TEXTURE_2D);`\
808-`  glBindTexture(GL_TEXTURE_2D, Tex1);`\
809-`  glActiveTextureARB(GL_TEXTURE1_ARB);`\
810-`  glEnable(GL_TEXTURE_2D);`\
811-`  glBindTexture(GL_TEXTURE_2D, Tex2);`\
812-`end;`
860+``` pascal
861+procedure InitializeGL;
862+begin
863+ Load_GL_ARB_multitexture;
864+ glClearColor(0, 0, 0, 0);
865+ Tex1 := LoadGLTextureFromFile('Lazarus.bmp');
866+ Tex2 := LoadGLTextureFromFile('Mask.bmp');
867+ glActiveTextureARB(GL_TEXTURE0_ARB);
868+ glEnable(GL_TEXTURE_2D);
869+ glBindTexture(GL_TEXTURE_2D, Tex1);
870+ glActiveTextureARB(GL_TEXTURE1_ARB);
871+ glEnable(GL_TEXTURE_2D);
872+ glBindTexture(GL_TEXTURE_2D, Tex2);
873+end;
874+```
813875
814876First we need load OpenGL extension that will allow us to use multitexture functions. Load_GL_ARB_multitexture will try to load those extensions and will return TRUE if operation was successful.
815877
816878To select texture stage you want to work on, use glActiveTextureARB function. It takes only one parameter that define which stage you want. After that all texture functions (enabling, disabling, binding, creating...) will affect that stage.
817879
818880Since we setup every thing in initialization function, all we have to do is to draw object:
819881
820-`procedure DrawGLScene; cdecl;`\
821-`begin`\
822-`  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);`\
882+``` pascal
883+procedure DrawGLScene; cdecl;
884+begin
885+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
823886
824-`  glLoadIdentity;`\
825-`  glTranslatef(0, 0, -5);`\
887+ glLoadIdentity;
888+ glTranslatef(0, 0, -5);
826889
827-`  glBegin(GL_QUADS);`\
828-`    glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);`\
829-`    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);`\
830-`    glVertex3f(2.516, 2, 0);`\
831-`    glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);`\
832-`    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);`\
833-`    glVertex3f(-2.516, 2, 0);`\
834-`    glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);`\
835-`    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);`\
836-`    glVertex3f(-2.516,-2, 0);`\
837-`    glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);`\
838-`    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);`\
839-`    glVertex3f(2.516,-2, 0);`\
840-`  glEnd;`\
890+ glBegin(GL_QUADS);
891+ glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
892+ glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
893+ glVertex3f(2.516, 2, 0);
894+ glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
895+ glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
896+ glVertex3f(-2.516, 2, 0);
897+ glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
898+ glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
899+ glVertex3f(-2.516,-2, 0);
900+ glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
901+ glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
902+ glVertex3f(2.516,-2, 0);
903+ glEnd;
841904
842-`  glutSwapBuffers;`\
843-`end;`
905+ glutSwapBuffers;
906+end;
907+```
844908
845909![](/wiki-images/5/52/MultitexturePic1.jpg) As you can see, difference is only in defining texture coordinations. We now use glMultiTexCoord2fARB function that takes texture stage and texture coordinations. Every thing else is unchanged.
846910
847911Today almost all graphic cards supports at least 2 texture stages. Using singlepass multitexturing is faster than multipass version since you draw objects only once. If hardware supports singlepass multitexturing (Load_GL_ARB_multitexture returns TRUE) use it.
848912
849913Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
850914
851915# Render to texture
852916
853917This one will be short. OpenGL can capture current scene to texture so you can use it for texturing other objects (TV screen, mirror or some thing else). Well just render scene to texture and apply it to rotating plane.
854918
855919First, we must create empty texture which we'll use to capture scene:
856920
857-`procedure SetupRenderTexture;`\
858-`var`\
859-`  Data: Pointer;`\
860-`begin`\
861-`  GetMem(Data, 256*256*3);`\
862-`  glGenTextures(1, @RenderTexture);`\
863-`  glBindTexture(GL_TEXTURE_2D, RenderTexture);`\
864-`  glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, Data);`\
865-`  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);`\
866-`  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);`\
867-`  FreeMem(Data);`\
868-`end;`
921+``` pascal
922+procedure SetupRenderTexture;
923+var
924+ Data: Pointer;
925+begin
926+ GetMem(Data, 256*256*3);
927+ glGenTextures(1, @RenderTexture);
928+ glBindTexture(GL_TEXTURE_2D, RenderTexture);
929+ glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, Data);
930+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
931+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
932+ FreeMem(Data);
933+end;
934+```
869935
870936Buffer for 256*256 RGB image is created and it is used to setup 2D texture.
871937
872938Main part is in drawing function:
873939
874-`procedure DrawGLScene; cdecl;`\
875-`var`\
876-`  TotalTime: Single;`\
877-`begin`\
878-`  glClearColor(0, 0, 0, 0);`\
879-`  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);`\
880-`  glEnable(GL_LIGHTING);`\
881-`  glDisable(GL_TEXTURE_2D);`\
882-`  glViewport(0, 0, 256, 256);`\
940+``` pascal
941+procedure DrawGLScene; cdecl;
942+var
943+ TotalTime: Single;
944+begin
945+ glClearColor(0, 0, 0, 0);
946+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
947+ glEnable(GL_LIGHTING);
948+ glDisable(GL_TEXTURE_2D);
949+ glViewport(0, 0, 256, 256);
883950
884-`  TotalTime := GetTotalTime;`\
951+ TotalTime := GetTotalTime;
885952
886-`  glLoadIdentity;`\
887-`  glTranslatef(0, 0, -5);`\
888-`  glRotatef(50 * TotalTime, 1, 0, 0);`\
889-`  glRotatef(100 * TotalTime, 0, 1, 0);`\
890-`  glRotatef(50 * TotalTime, 0, 0, 1);`\
953+ glLoadIdentity;
954+ glTranslatef(0, 0, -5);
955+ glRotatef(50 * TotalTime, 1, 0, 0);
956+ glRotatef(100 * TotalTime, 0, 1, 0);
957+ glRotatef(50 * TotalTime, 0, 0, 1);
891958
892-`  glColor3f(1, 1, 1);`\
893-`  glutSolidCube(2);`\
959+ glColor3f(1, 1, 1);
960+ glutSolidCube(2);
894961
895-`  glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);`\
962+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);
896963
897-`  glClearColor(0.18, 0.20, 0.66, 0);`\
898-`  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);`\
899-`  glDisable(GL_LIGHTING);`\
900-`  glEnable(GL_TEXTURE_2D);`\
901-`  glViewport(0, 0, AppWidth, AppHeight);`\
964+ glClearColor(0.18, 0.20, 0.66, 0);
965+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
966+ glDisable(GL_LIGHTING);
967+ glEnable(GL_TEXTURE_2D);
968+ glViewport(0, 0, AppWidth, AppHeight);
902969
903-`  glLoadIdentity;`\
904-`  glTranslatef(0, 0, -7);`\
905-`  glRotatef(20 * TotalTime, 1, 0, 0);`\
906-`  glRotatef(50 * TotalTime, 0, 1, 0);`\
970+ glLoadIdentity;
971+ glTranslatef(0, 0, -7);
972+ glRotatef(20 * TotalTime, 1, 0, 0);
973+ glRotatef(50 * TotalTime, 0, 1, 0);
907974
908-`  glBegin(GL_QUADS);`\
909-`    glTexCoord2f(1, 0);`\
910-`    glVertex3f(2, 2, 0);`\
911-`    glTexCoord2f(0, 0);`\
912-`    glVertex3f(-2, 2, 0);`\
913-`    glTexCoord2f(0, 1);`\
914-`    glVertex3f(-2,-2, 0);`\
915-`    glTexCoord2f(1, 1);`\
916-`    glVertex3f(2,-2, 0);`\
917-`  glEnd;`\
975+ glBegin(GL_QUADS);
976+ glTexCoord2f(1, 0);
977+ glVertex3f(2, 2, 0);
978+ glTexCoord2f(0, 0);
979+ glVertex3f(-2, 2, 0);
980+ glTexCoord2f(0, 1);
981+ glVertex3f(-2,-2, 0);
982+ glTexCoord2f(1, 1);
983+ glVertex3f(2,-2, 0);
984+ glEnd;
918985
919-`  glutSwapBuffers;`\
920-`end;`
986+ glutSwapBuffers;
987+end;
988+```
921989
922990![](/wiki-images/6/6e/RenderToTexturePic1.jpg) First, everything is setup for scene that will be captured. Viewport is reduced to 256*256 so it will fit into texture and scene is drawn. glCopyTexImage2D is used to capture scene to currently selected texture.
923991
924992When we have scene captured to texture, everything can be cleared again, viewport can be returned to original size and final scene is drawn using previous scene as texture.
925993
926994P.S. Captured texture can be saved using SaveGLTextureToFile function from [Vampyre Imaging Library](http://imaginglib.sourceforge.net/).
927995
928996Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
929997
930998# Vertex array
931999
9321000OpenGL is capable of rendering primitives using data that is stored in buffers insted of calling glVertex. Buffers can be used to define vertex and texture coordinates, and colors (index and RGBA), normals and edge flags.
9331001
9341002In this tutorial well use only vertex and color buffers, and we'll show non-indexed and indexed drawing. Non-indexed mode draws buffers as streams. Indexed mode will draw buffer elements in order that is defined in index buffer. But enough talking... let's start coding.
9351003
9361004First, let's define some types and constants:
9371005
938-`type`\
939-`  TVertex3f = record`\
940-`    X, Y, Z: Single;`\
941-`  end;`\
1006+``` pascal
1007+type
1008+ TVertex3f = record
1009+ X, Y, Z: Single;
1010+ end;
9421011
943-`  TColor3f = record`\
944-`   R, G, B: Single;`\
945-`  end;`\
1012+ TColor3f = record
1013+ R, G, B: Single;
1014+ end;
9461015
947-`  VertexBuffer: array [0..5] of TVertex3f = (`\
948-`    (X : 1; Y : 1; Z : 0),`\
949-`    (X : -1; Y : 1; Z : 0),`\
950-`    (X : -1; Y : -1; Z : 0),`\
951-`    (X : 1; Y : 1; Z : 0),`\
952-`    (X : -1; Y : -1; Z : 0),`\
953-`    (X : 1; Y : -1; Z : 0)`\
954-`  );`\
955-`  ColorBuffer: array [0..5] of TColor3f = (`\
956-`    (R : 1; G : 0; B : 1),`\
957-`    (R : 0; G : 0; B : 1),`\
958-`    (R : 0; G : 1; B : 0),`\
959-`    (R : 1; G : 0; B : 1),`\
960-`    (R : 0; G : 1; B : 0),`\
961-`    (R : 1; G : 1; B : 0)`\
962-`  );`
1016+ VertexBuffer: array [0..5] of TVertex3f = (
1017+ (X : 1; Y : 1; Z : 0),
1018+ (X : -1; Y : 1; Z : 0),
1019+ (X : -1; Y : -1; Z : 0),
1020+ (X : 1; Y : 1; Z : 0),
1021+ (X : -1; Y : -1; Z : 0),
1022+ (X : 1; Y : -1; Z : 0)
1023+ );
1024+ ColorBuffer: array [0..5] of TColor3f = (
1025+ (R : 1; G : 0; B : 1),
1026+ (R : 0; G : 0; B : 1),
1027+ (R : 0; G : 1; B : 0),
1028+ (R : 1; G : 0; B : 1),
1029+ (R : 0; G : 1; B : 0),
1030+ (R : 1; G : 1; B : 0)
1031+ );
1032+```
9631033
9641034We have two buffers. One for vertex coordinates and one for vertex colors. This 6 vertices defines 2 triangles that forms rectangle.
9651035
9661036Drawing primitives using buffers is easy:
9671037
968-`  glEnableClientState(GL_VERTEX_ARRAY);`\
969-`  glEnableClientState(GL_COLOR_ARRAY);`\
970-`  glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);`\
971-`  glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);`\
1038+```text
1039+ glEnableClientState(GL_VERTEX_ARRAY);
1040+ glEnableClientState(GL_COLOR_ARRAY);
1041+ glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);
1042+ glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);
9721043
973-`  glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));`\
1044+ glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));
9741045
975-`  glDisableClientState(GL_VERTEX_ARRAY);`\
976-`  glDisableClientState(GL_COLOR_ARRAY);`
1046+ glDisableClientState(GL_VERTEX_ARRAY);
1047+ glDisableClientState(GL_COLOR_ARRAY);
1048+```
9771049
9781050First we enable buffers we want to use using glEnableClientState function. Than we can select buffers we want to use. Every buffer type has own function for selecting (glColorPointer, glEdgeFlagPointer, glIndexPointer, glNormalPointer, glTexCoordPointer, glVertexPointer). First parameter in those functions defines how many numbers every element contains. For example, let's take vertex buffer. If this parameter is 2 than OpenGL expects that every element in buffer contains x and y coordinate. If this parameter is, for example, 4, than every element should contains x, y, z and w coordinate. Next parameter defines what type of data element contains (GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT or GL_DOUBLE). Next one defines how many bytes are between each element. This way you can have buffer that contains vertex coordinates and some custom data. For arbitrary data type, this parameter can be calculated like this:
9791051
980-`type`\
981-`  TBufferData = record`\
982-`    DataBefore: TDataBefore;`\
983-`    Vertex: TVertex;`\
984-`    DataAfter: TDataAfter;`\
985-`  end;`\
1052+``` pascal
1053+type
1054+ TBufferData = record
1055+ DataBefore: TDataBefore;
1056+ Vertex: TVertex;
1057+ DataAfter: TDataAfter;
1058+ end;
9861059
987-`Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)`
1060+Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)
1061+```
9881062
9891063Last parameter if pointer to the begginig of buffer.
9901064
9911065When buffers are selected we can draw them using glDrawArrays functions. All enabled buffers are used to draw primitives. What kind of polygons are being generated is defined in first parameter (same as in glBegin function). Next two defines subset of buffer which is used for drawing (start and count).
9921066
9931067When buffers are not needed you can disable them.
9941068
9951069To demonstrate indexed mode, I made some simple mesh class that can load vertex, color and index data from external files:
9961070
997-`type`\
998-`  TMesh = class`\
999-`  private`\
1000-`    FVertices: array of TVertex3f;`\
1001-`    FColors: array of TColor3f;`\
1002-`    FIndices: array of Integer;`\
1003-`    procedure FreeBuffers;`\
1004-`  public`\
1005-`    constructor Create;`\
1006-`    destructor Destroy; override;`\
1007-`    procedure LoadMesh(FileName: String);`\
1008-`    procedure DrawMesh;`\
1009-`  end;`
1071+``` pascal
1072+type
1073+ TMesh = class
1074+ private
1075+ FVertices: array of TVertex3f;
1076+ FColors: array of TColor3f;
1077+ FIndices: array of Integer;
1078+ procedure FreeBuffers;
1079+ public
1080+ constructor Create;
1081+ destructor Destroy; override;
1082+ procedure LoadMesh(FileName: String);
1083+ procedure DrawMesh;
1084+ end;
1085+```
10101086
10111087FVertices will contain data about vertices, FColors data about color and FIndices data about indices when external file is loaded.
10121088
10131089First we'll write some code that deals with creation and destruction of class:
10141090
1015-`procedure TMesh.FreeBuffers;`\
1016-`begin`\
1017-`  FVertices := nil;`\
1018-`  FColors := nil;`\
1019-`  FIndices := nil;`\
1020-`end;`\
1091+``` pascal
1092+procedure TMesh.FreeBuffers;
1093+begin
1094+ FVertices := nil;
1095+ FColors := nil;
1096+ FIndices := nil;
1097+end;
10211098
1022-`constructor TMesh.Create;`\
1023-`begin`\
1024-`  FreeBuffers;`\
1025-`end;`\
1099+constructor TMesh.Create;
1100+begin
1101+ FreeBuffers;
1102+end;
10261103
1027-`destructor TMesh.Destroy;`\
1028-`begin`\
1029-`  FreeBuffers;`\
1030-`  inherited Destroy;`\
1031-`end;`
1104+destructor TMesh.Destroy;
1105+begin
1106+ FreeBuffers;
1107+ inherited Destroy;
1108+end;
1109+```
10321110
10331111File that will contain mesh data is simple text file. First row will contain number of vertices and indices separated by space character. After that row will come rows for every vertex and color. X, Y, Z, R, G and B all separated by space character. In the end, there will be rows for indices... every index number is written in its own row... so, for one triangle, data file will look like this:
10341112
1035-`3 3`\
1036-`-1 -1 0 1 1 1`\
1037-`1 -1 0 1 1 1`\
1038-`0 1 0 1 1 1`\
1039-`0`\
1040-`1`\
1041-`2`
1113+```text
1114+3 3
1115+-1 -1 0 1 1 1
1116+1 -1 0 1 1 1
1117+0 1 0 1 1 1
1118+0
1119+1
1120+2
1121+```
10421122
10431123This means that there is 3 vertices and 3 indices defined in file. First vrtex is at -1, -1, 0 and has color 1, 1, 1 and so on. Indices defines that order in which vertices are drawn (in this case vertices are drawn in the same order as they are defined).
10441124
10451125Code for loading this data will loke like this:
10461126
1047-`procedure TMesh.LoadMesh(FileName: String);`\
1048-`var`\
1049-`  MeshFile: TextFile;`\
1050-`  VertexCount, IndexCount: Integer;`\
1051-`  iV, iI: Integer;`\
1052-`begin`\
1053-`  FreeBuffers;`\
1127+``` pascal
1128+procedure TMesh.LoadMesh(FileName: String);
1129+var
1130+ MeshFile: TextFile;
1131+ VertexCount, IndexCount: Integer;
1132+ iV, iI: Integer;
1133+begin
1134+ FreeBuffers;
10541135
1055-`  AssignFile(MeshFile, FileName);`\
1056-`  Reset(MeshFile);`\
1136+ AssignFile(MeshFile, FileName);
1137+ Reset(MeshFile);
10571138
1058-`  ReadLn(MeshFile, VertexCount, IndexCount);`\
1139+ ReadLn(MeshFile, VertexCount, IndexCount);
10591140
1060-`  SetLength(FVertices, VertexCount);`\
1061-`  SetLength(FColors, VertexCount);`\
1062-`  SetLength(FIndices, IndexCount);`\
1141+ SetLength(FVertices, VertexCount);
1142+ SetLength(FColors, VertexCount);
1143+ SetLength(FIndices, IndexCount);
10631144
1064-`  for iV := 0 to VertexCount - 1 do`\
1065-`    ReadLn(MeshFile,`\
1066-`      FVertices[iV].X, FVertices[iV].Y, FVertices[iV].Z,`\
1067-`      FColors[iV].R, FColors[iV].G, FColors[iV].B);`\
1145+ for iV := 0 to VertexCount - 1 do
1146+ ReadLn(MeshFile,
1147+ FVertices[iV].X, FVertices[iV].Y, FVertices[iV].Z,
1148+ FColors[iV].R, FColors[iV].G, FColors[iV].B);
10681149
1069-`  for iI := 0 to IndexCount - 1 do`\
1070-`    ReadLn(MeshFile, FIndices[iI]);`\
1150+ for iI := 0 to IndexCount - 1 do
1151+ ReadLn(MeshFile, FIndices[iI]);
10711152
1072-`  CloseFile(MeshFile);`\
1073-`end;`
1153+ CloseFile(MeshFile);
1154+end;
1155+```
10741156
10751157After loading data, we have everything for drawing:
10761158
1077-`procedure TMesh.DrawMesh;`\
1078-`begin`\
1079-`  glEnableClientState(GL_VERTEX_ARRAY);`\
1080-`  glEnableClientState(GL_COLOR_ARRAY);`\
1081-`  glVertexPointer(3, GL_FLOAT, 0, @FVertices[0]);`\
1082-`  glColorPointer(3, GL_FLOAT, 0, @FColors[0]);`\
1159+``` pascal
1160+procedure TMesh.DrawMesh;
1161+begin
1162+ glEnableClientState(GL_VERTEX_ARRAY);
1163+ glEnableClientState(GL_COLOR_ARRAY);
1164+ glVertexPointer(3, GL_FLOAT, 0, @FVertices[0]);
1165+ glColorPointer(3, GL_FLOAT, 0, @FColors[0]);
10831166
1084-`  glDrawElements(GL_TRIANGLES, Length(FIndices), GL_UNSIGNED_INT, @FIndices[0]);`\
1167+ glDrawElements(GL_TRIANGLES, Length(FIndices), GL_UNSIGNED_INT, @FIndices[0]);
10851168
1086-`  glDisableClientState(GL_VERTEX_ARRAY);`\
1087-`  glDisableClientState(GL_COLOR_ARRAY);`\
1088-`end;`
1169+ glDisableClientState(GL_VERTEX_ARRAY);
1170+ glDisableClientState(GL_COLOR_ARRAY);
1171+end;
1172+```
10891173
10901174As you can see, allmost everything is the same as for non-indexed drawing, except function that actually draw polygons. In this case we use glDrawElements function. For this one we specify what kind of polygons we want, how many indices are in index buffer, type of data in index buffer and pointer to the beginning of index buffer.
10911175
10921176![](/wiki-images/7/77/VertexArrayPic1.jpg)Full source code comes with mesh data file that this class can use to generates rectangle that is identical with one that is drawn using non-indexed mode. Mesh data file looks like this:
10931177
1094-`4 6`\
1095-`1 1 0 1 0 1`\
1096-`-1 1 0 0 0 1`\
1097-`-1 -1 0 0 1 0`\
1098-`1 -1 0 1 1 0`\
1099-`0`\
1100-`1`\
1101-`2`\
1102-`0`\
1103-`2`\
1104-`3`
1178+```text
1179+4 6
1180+1 1 0 1 0 1
1181+-1 1 0 0 0 1
1182+-1 -1 0 0 1 0
1183+1 -1 0 1 1 0
1184+0
1185+1
1186+2
1187+0
1188+2
1189+3
1190+```
11051191
11061192As you can see, there is data for only 4 vertices and 6 indices. So, first triangle is defined by vertices 0, 1 and 2, and the seccond one by vertices 0, 2 and 3. By using indexed mode we don't have to duplicate vertices.
11071193
11081194Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/project/showfiles.php?group_id=92177&package_id=199145).
11091195
11101196------------------------------------------------------------------------
11111197
11121198[[Creating bindings for C libraries]]
11131199
11141200---
11151201*From the Free Pascal wiki, page "OpenGL Tutorial/zh CN", as it stood on 2020-02-23 after a change by Trev, 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).*

Back to history