Sign in Join
Wiki copytemporary

OpenGL Tutorial: what changed

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

OldNewLine
11## Introduction
22
33OpenGL is the premier environment for developing portable, interactive 2D and 3D graphics applications. Since its introduction in 1992, OpenGL has become the industry's most widely used and supported 2D and 3D graphics application programming interface (API), bringing thousands of applications to a wide variety of computer platforms. OpenGL fosters innovation and speeds application development by incorporating a broad set of rendering, texture mapping, special effects, and other powerful visualization functions. Developers can leverage the power of OpenGL across all popular desktop and workstation platforms, ensuring wide application deployment.
44
55You can find more information about OpenGL [here](http://www.opengl.org/about/overview/).
66
77### GLUT
88
99**Note:** GLUT is deprecated.
1010
1111GLUT (pronounced like the glut in gluttony) is the OpenGL Utility Toolkit, a window system independent toolkit for writing OpenGL programs. It implements a simple windowing application programming interface (API) for OpenGL. GLUT makes it considerably easier to learn about and explore OpenGL programming. GLUT provides a portable API so you can write a single OpenGL program that works across all PC and workstation OS platforms.
1212
1313You can find more information about GLUT [here](http://www.opengl.org/resources/libraries/glut/).
1414
1515Many Operating Systems come with preinstalled GLUT, but if yours does not have one you can easily find it using [Google](http://www.google.com/).
1616
1717Windows binaries can be downloaded from [www.xmission.com](http://www.xmission.com/~nate/glut.html).
1818
1919The GLUT FPC units information is here [[OpenGL]].
2020
2121### GLFW
2222
2323See <http://www.glfw.org/>
2424
2525### LCL
2626
2727The Lazarus Component Library can be used with OpenGL too. Lazarus includes a TOpenGLControl - a LCL control with an OpenGL context. The lazarus package LazOpenGLContext can be found lazarus/components/opengl/lazopenglcontext.lpk. An example can be found in lazarus/examples/openglcontrol/openglcontrol_demo.lpi.
2828
2929### LCL / GLFW / GLUT
3030
3131When should you use GLUT, when LCL?
3232
3333- GLUT is better if you want to draw everything yourself.
3434- LCL is better for normal applications. For example a 3D editor needs a few OpenGL windows and the rest is a normal application using normal buttons, comboboxes, windows, modal windows, etc.
3535
3636The OpenGL part is pretty much the same. GLUT needs a dll under windows, where LCL typically runs out of the box, but a LCL executable is bigger.
3737
3838## Code samples
3939
4040### Creating your first LCL program
4141
4242Using LCL is typically the easiest way to access OpenGL with Lazarus. Since GLUT is deprecated, using the LCL is generally a good idea for a new OpenGL Lazarus project. Most of the code samples described below for GLUT are easy to translate into the LCL code, though you will have to find equivalents for the functions with the 'glut' prefix, for example instead of "glutSwapBuffers" we will use the LCL's "SwapBuffers" property to display our rendering. The one great feature that GLUT provides that is hard to do with the LCL is showing text on the screen (see GLUT's "Bitmap Fonts" section below). However, since this is your first LCL program, we will keep it simple by not showing any text.
4343
4444Lazarus comes with a example OpenGL program, you can find it in the folder Lazarus/Examples/openglcontrol. That example demonstrates many powerful features for creating an animated OpenGL image. However, it is also a relatively complicated program. Below is a minimal Lazarus project that mimics some of the features described in the GLUT samples described below. To create this, launch Lazarus and choose Project/NewProject to create a new application. Choose the Project/ProjectInspector menu item, click the 'Add..' button, go to the 'New Requirement' and and the "LazOpenGLContext" package. Next, paste the code below into your 'unit1.pas'. Then, click on your form and in the events tab of the object inspector link the "OnCreate" event to the function "FormCreate". You should now be able to run your new application by choosing the Run/Run menu item.
4545
4646The code creates a new OpenGL panel that fills the form. OpenGL draws a simple triangle in the form. Note that when you run the application you can resize the form and the triangle rescales proportionally to fill the form.
4747
4848``` pascal
4949unit Unit1;
5050
5151{$mode objfpc}{$H+}
5252
5353interface
5454
5555uses
5656 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, OpenGLContext, gl;
5757
5858type
5959 TForm1 = class(TForm)
6060 procedure FormCreate(Sender: TObject);
6161 procedure GLboxPaint(Sender: TObject);
6262 private
6363 GLBox: TOpenGLControl;
6464 public
6565 end;
6666
6767var
6868 Form1: TForm1;
6969
7070implementation
7171
7272{$R *.lfm}
7373
7474procedure TForm1.GLboxPaint(Sender: TObject);
7575begin
7676 glClearColor(0.27, 0.53, 0.71, 1.0); // Set blue background
7777 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
7878 glLoadIdentity;
7979 glBegin(GL_TRIANGLES);
8080 glColor3f(1, 0, 0);
8181 glVertex3f( 0.0, 1.0, 0.0);
8282 glColor3f(0, 1, 0);
8383 glVertex3f(-1.0,-1.0, 0.0);
8484 glColor3f(0, 0, 1);
8585 glVertex3f( 1.0,-1.0, 0.0);
8686 glEnd;
8787 GLbox.SwapBuffers;
8888end;
8989
9090procedure TForm1.FormCreate(Sender: TObject);
9191begin
9292 GLbox := TOpenGLControl.Create(Self);
9393 GLbox.AutoResizeViewport := true;
9494 GLBox.Parent := Self;
9595 GLBox.MultiSampling := 4;
9696 GLBox.Align := alClient;
9797 GLBox.OnPaint := @GLboxPaint; // for "mode delphi" this would be "GLBox.OnPaint := GLboxPaint"
9898 GLBox.invalidate;
9999end;
100100
101101end.
102102```
103103
104104### Creating your first GLUT program
105105
106-In order to use GLUT, you must first include **unit** glut and then initialize it. This is done using <b>glutInit</b> function. This function can parse the command line and set parameters for the main window, but it expects input in C/C++ style. You'll have to write your own function to make the conversion from ParamCount and ParamStr to C/C++ like command line parameters.
106+In order to use GLUT, you must first include **unit** glut and then initialize it. This is done using glutInit function. This function can parse the command line and set parameters for the main window, but it expects input in C/C++ style. You'll have to write your own function to make the conversion from ParamCount and ParamStr to C/C++ like command line parameters.
107107
108108``` pascal
109109procedure glutInitPascal(ParseCmdLine: Boolean);
110110var
111111 Cmd: array of PChar;
112112 CmdCount, I: Integer;
113113begin
114114 if ParseCmdLine then
115115 CmdCount := ParamCount + 1
116116 else
117117 CmdCount := 1;
118118 SetLength(Cmd, CmdCount);
119119 for I := 0 to CmdCount - 1 do
120120 Cmd[I] := PChar(ParamStr(I));
121121 glutInit(@CmdCount, @Cmd);
122122end;
123123```
124124
125125In essence, you create an array and fill it with strings from ParamStr. This function also takes a parameter that can control what is passed to glutInit -- either the whole command line or just the executable file name.
126126
127127ToDo: probably **glutInit(@argc, @argv);** is enough.
128128
129129More about glutInit: <http://www.opengl.org/resources/libraries/glut/spec3/node10.html>
130130
131-Next, you need to create a main window. Set the display mode for the main window using <b>glutInitDisplayMode</b>. It only takes one parameter which is a combination of flags. Usually <b>GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH</b> is all you will need.
131+Next, you need to create a main window. Set the display mode for the main window using glutInitDisplayMode. It only takes one parameter which is a combination of flags. Usually GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH is all you will need.
132132
133133More about glutInitDisplayMode: <http://www.opengl.org/resources/libraries/glut/spec3/node12.html>
134134
135-The position and size of the window is controlled using <b>glutInitWindowPosition</b> and <b>glutInitWindowSize</b>. They take 2 parameters. X and Y coordinates in the former, and width and height in the latter. You can use <b>glutGet</b> to find the screen size and center the window.
135+The position and size of the window is controlled using glutInitWindowPosition and glutInitWindowSize. They take 2 parameters. X and Y coordinates in the former, and width and height in the latter. You can use glutGet to find the screen size and center the window.
136136
137137More about glutInitWindowPosition, glutInitWindowSize and glutGet: <http://www.opengl.org/resources/libraries/glut/spec3/node11.html> <http://www.opengl.org/documentation/specs/glut/spec3/node70.html>
138138
139-Finally, the window should be created using the <b>glutCreateWindow</b> function. It will create the window and set its caption through a parameter. As a result it will return the window's handle. This can be used with other functions that require it.
139+Finally, the window should be created using the glutCreateWindow function. It will create the window and set its caption through a parameter. As a result it will return the window's handle. This can be used with other functions that require it.
140140
141141More about glutCreateWindow: <http://www.opengl.org/resources/libraries/glut/spec3/node16.html>
142142
143-Before your program can enter the main loop, you must set some callbacks. You will a need callback for drawing the window, for resizing and for getting keyboard input. These callbacks are set using <b>glutDisplayFunc</b>, <b>glutReshapeFunc</b> and <b>glutKeyboardFunc</b>.
143+Before your program can enter the main loop, you must set some callbacks. You will a need callback for drawing the window, for resizing and for getting keyboard input. These callbacks are set using glutDisplayFunc, glutReshapeFunc and glutKeyboardFunc.
144144
145145More about setting callbacks: <http://www.opengl.org/resources/libraries/glut/spec3/node45.html#SECTION00080000000000000000>
146146
147147Your drawing function might look like this:
148148
149149``` pascal
150150procedure DrawGLScene; cdecl;
151151begin
152152 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
153153 glutSwapBuffers;
154154end;
155155```
156156
157157This will only clear the window to the background color and reset the zbuffer (don't worry about zbuffer... more about that later).
158158
159159Your resize function might look like this:
160160
161161``` pascal
162162procedure ReSizeGLScene(Width, Height: Integer); cdecl;
163163begin
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;
174174end;
175175```
176176
177177With this code, you tell OpenGL where in the window it should draw and set matrices to the desired values (matrix functions will be explained later).
178178
179179Keyboard input is evaluated with the following callback:
180180
181181``` pascal
182182procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
183183begin
184184 if Key = 27 then
185185 Halt(0);
186186end;
187187```
188188
189-This function will instruct your program to exit if you press ESC key. GLUT is event driven and the only way to terminate your program is to call <b>Halt</b> inside one of your callback functions. If you close the window in some other way, it will disappear, but the program will continue to loop through the main routine indefinitely.
189+This function will instruct your program to exit if you press ESC key. GLUT is event driven and the only way to terminate your program is to call Halt inside one of your callback functions. If you close the window in some other way, it will disappear, but the program will continue to loop through the main routine indefinitely.
190190
191-To start the main loop, call <b>glutMainLoop</b>. It will enter a loop that never ends, which calls all your callback functions.
191+To start the main loop, call glutMainLoop. It will enter a loop that never ends, which calls all your callback functions.
192192
193193The main part of your program might look like this:
194194
195195``` pascal
196196const
197197 AppWidth = 640;
198198 AppHeight = 480;
199199
200200procedure InitializeGL;
201201begin
202202 glClearColor(0.18, 0.20, 0.66, 0);
203203end;
204204
205205var
206206 ScreenWidth, ScreenHeight: Integer;
207207begin
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;
224224end.
225225```
226226
227227The next tutorial will add some code that will draw a simple shape.
228228
229229Download source code or a linux/windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
230230
231231### Drawing a simple shape
232232
233233**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'.
234234
235235This time we shall add just a few lines of code and focus on explanation of some of the OpenGL functions.
236236
237237Let us explain code you already have.
238238
239239``` pascal
240240 ...
241241 glMatrixMode(GL_PROJECTION);
242242 glLoadIdentity;
243243 gluPerspective(45, Width / Height, 0.1, 1000);
244244
245245 glMatrixMode(GL_MODELVIEW);
246246 glLoadIdentity;
247247end;
248248```
249249
250250Using **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.
251251
252-Once you chose matrix you want to change, you can call functions that affect matrix values. <b>glLoadIdentity</b> 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.
252+Once 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.
253253
254-In order to set perspective matrix, you can use <b>gluPerspective</b> function. Four parameters present the field of view, aspect ratio, near and far plane. It's that simple.
254+In 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.
255255
256256Now, you'll change model matrix... for this time, you just set it to identity.
257257
258258OK... and now, the code for drawing the first shape:
259259
260260``` pascal
261261procedure DrawGLScene; cdecl;
262262begin
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;
280280end;
281281```
282282
283283We have allready used glClear function. It will just reset buffers. We'll skip next two functions and head for drawing ones.
284284
285285**glBegin** marks beginning of drawing block. After this function you can start entering vertices. Parameter describes how are vertices used when drawing:
286286
287287GL_POINTS: Treats each vertex as a single point. Vertex n defines point n. N points are drawn.
288288
289289GL_LINES: Treats each pair of vertices as an independent line segment. Vertices 2n-1 and 2n define line n. n/2 lines are drawn.
290290
291291GL_LINE_STRIP: Draws a connected group of line segments from the first vertex to the last. n-1 lines are drawn.
292292
293293GL_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.
294294
295295GL_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.
296296
297297GL_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.
298298
299299GL_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.
300300
301301GL_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.
302302
303303GL_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.
304304
305305GL_POLYGON: Draws a single, convex polygon. Vertices 1 through n define this polygon.
306306
307-![](/wiki-images/f/f3/SimpleShapePic1.jpg) You'll draw single triangle and for that GL_TRIANGLES flag will do the trick. <b>glVertex3f</b> 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.
307+![](/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.
308308
309-Before glVertex you can set color, material, texture... For simplicity you'll just specify color for each vertex in this tutorial. Color is set using <b>glColor3f</b> function. glColor can also take different set of parameters like glVertex.
309+Before 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.
310310
311-As 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. <b>glTranslatef</b> 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.
311+As 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.
312312
313-In the end you call <b>glEnd</b> functions that finishes drawing. You could now start another drawing block with new glBegin function if you wish.
313+In the end you call glEnd functions that finishes drawing. You could now start another drawing block with new glBegin function if you wish.
314314
315315Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
316316
317317### Using display lists
318318
319-Sometimes you'll need to draw some object multiple times on scene. OpenGL has ability to build <b>display lists</b> 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 <b>glNewList</b> and <b>glEndList</b> calls.
319+Sometimes 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.
320320
321321``` pascal
322322const
323323 LIST_OBJECT = 1;
324324
325325procedure CreateList;
326326begin
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;
365365end;
366366```
367367
368-**glNewList** creates new display list and all drawing functions will be recorded until <b>glEndList</b> 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 automatically.
368+**glNewList** 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 automatically.
369369
370-**glIsList** function can help you with display lists. It can tell if some list ID is already filled with data. Another useful function is <b>glGenLists</b>. 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
370+**glIsList** 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
371371
372372All created lists should be deleted. You will do that before program exits:
373373
374374``` pascal
375375procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
376376begin
377377 if Key = 27 then
378378 begin
379379 glDeleteLists(LIST_OBJECT, 1);
380380 Halt(0);
381381 end;
382382end;
383383```
384384
385385**glDeleteLists** 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
386386
387387Now you know how to create and delete display lists... let's see how to draw them:
388388
389389``` pascal
390390procedure DrawGLScene; cdecl;
391391begin
392392 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
393393
394394 glLoadIdentity;
395395 glTranslatef(-2, 0, -5);
396396 glRotatef(40, 1, 0, 1);
397397 glCallList(LIST_OBJECT);
398398
399399 glLoadIdentity;
400400 glTranslatef(1, -2, -10);
401401 glRotatef(62, 0, 1, 0);
402402 glCallList(LIST_OBJECT);
403403
404404 glLoadIdentity;
405405 glTranslatef(-4, 0.5, -15);
406406 glRotatef(200, 1, 0, 0);
407407 glCallList(LIST_OBJECT);
408408
409409 glutSwapBuffers;
410410end;
411411```
412412
413-![](/wiki-images/a/a5/DisplayListsPic1.jpg) Using <b>glCallList</b> 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:
413+![](/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:
414414
415415GL_BYTE: list is treated as an array of signed bytes, each in the range -128 through 127.
416416
417417GL_UNSIGNED_BYTE: list is treated as an array of unsigned bytes, each in the range 0 through 255.
418418
419419GL_SHORT: list is treated as an array of signed two-byte integers, each in the range -32768 through 32767.
420420
421421GL_UNSIGNED_SHORT: list is treated as an array of unsigned two-byte integers, each in the range 0 through 65535.
422422
423423GL_INT: lists is treated as an array of signed four-byte integers.
424424
425425GL_UNSIGNED_INT: list is treated as an array of unsigned four-byte integers.
426426
427427GL_FLOAT: list is treated as an array of four-byte floating-point values.
428428
429429GL_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.
430430
431431GL_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.
432432
433433GL_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.
434434
435435That 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.
436436
437437Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
438438
439439### Full screen animation
440440
441441Entering full screen mode is easy with GLUT. Let's change main part of the program:
442442
443443``` pascal
444444const
445445 FSMode = '800x600:32@75';
446446
447447begin
448448 glutInitPascal(False);
449449 glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH);
450450 glutGameModeString(FSMode);
451451 glutEnterGameMode;
452452 glutSetCursor(GLUT_CURSOR_NONE);
453453
454454 InitializeGL;
455455
456456 glutDisplayFunc(@DrawGLScene);
457457 glutReshapeFunc(@ReSizeGLScene);
458458 glutKeyboardFunc(@GLKeyboard);
459459 glutIdleFunc(@DrawGLScene);
460460
461461 glutMainLoop;
462462end.
463463```
464464
465-Since 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 <b>glutEnterGameMode</b> that create full screen window. To specify what kind of full screen mode you want, you call <b>glutGameModeString</b> function which takes string that defines mode you like. Format of that string is:
465+Since 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:
466466
467467`[width "x" height][":" bpp]["@" hertz]`
468468
469469In 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.
470470
471471Usually 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:
472472
473473`GLUT_CURSOR_RIGHT_ARROW`\
474474`GLUT_CURSOR_LEFT_ARROW`\
475475`GLUT_CURSOR_INFO`\
476476`GLUT_CURSOR_DESTROY`\
477477`GLUT_CURSOR_HELP`\
478478`GLUT_CURSOR_CYCLE`\
479479`GLUT_CURSOR_SPRAY`\
480480`GLUT_CURSOR_WAIT`\
481481`GLUT_CURSOR_TEXT`\
482482`GLUT_CURSOR_CROSSHAIR`\
483483`GLUT_CURSOR_UP_DOWN`\
484484`GLUT_CURSOR_LEFT_RIGHT`\
485485`GLUT_CURSOR_TOP_SIDE`\
486486`GLUT_CURSOR_BOTTOM_SIDE`\
487487`GLUT_CURSOR_LEFT_SIDE`\
488488`GLUT_CURSOR_RIGHT_SIDE`\
489489`GLUT_CURSOR_TOP_LEFT_CORNER`\
490490`GLUT_CURSOR_TOP_RIGHT_CORNER`\
491491`GLUT_CURSOR_BOTTOM_RIGHT_CORNER`\
492492`GLUT_CURSOR_BOTTOM_LEFT_CORNER`\
493493`GLUT_CURSOR_FULL_CROSSHAIR`\
494494`GLUT_CURSOR_NONE`\
495495`GLUT_CURSOR_INHERIT`
496496
497497**glutIdleFunc** 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.
498498
499499Now, let's look at the program termination where you need to exit full screen mode:
500500
501501``` pascal
502502procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
503503begin
504504 if Key = 27 then
505505 begin
506506 glutLeaveGameMode;
507507 Halt(0);
508508 end;
509509end;
510510```
511511
512-As you can see, all you need to do is to call <b>glutLeaveGameMode</b>.
512+As you can see, all you need to do is to call glutLeaveGameMode.
513513
514514Now, we'll introduce some new matrix functions. First, let's change ReSizeGLScene function:
515515
516516``` pascal
517517procedure ReSizeGLScene(Width, Height: Integer); cdecl;
518518begin
519519 ...
520520 glMatrixMode(GL_MODELVIEW);
521521 glLoadIdentity;
522522 gluLookAt(0, 20, 25, 0, 0, 0, 0, 1, 0);
523523end;
524524```
525525
526526**gluLookAt** 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.
527527
528528OK, 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:
529529
530530``` pascal
531531procedure DrawGLScene; cdecl;
532532var
533533 T: Single;
534534begin
535535 T := glutGet(GLUT_ELAPSED_TIME) / 1000;
536536 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
537537
538538 glPushMatrix;
539539 glRotatef(5 * T, 0, 1, 0);
540540 glColor3f(1, 1, 0);
541541 glutWireSphere(2, 20, 20);
542542 glPopMatrix;
543543
544544 glPushMatrix;
545545 glRotatef(90 * T, 0, 1, 0);
546546 glTranslatef(5, 0, 0);
547547 glRotatef(40 * T, 0, 1, 0);
548548 glColor3f(1, 0, 0);
549549 glutWireSphere(0.6, 10, 10);
550550 glPopMatrix;
551551
552552 glPushMatrix;
553553 glRotatef(60 * T, 0, 1, 0);
554554 glTranslatef(-3, 0, 9);
555555 glRotatef(50 * T, 0, 1, 0);
556556 glColor3f(0, 1, 0);
557557 glutWireSphere(1, 16, 16);
558558
559559 glPushMatrix;
560560 glRotatef(360 * T, 0, 1, 0);
561561 glTranslatef(-1.7, 0, 0);
562562 glRotatef(50 * T, 0, 1, 0);
563563 glColor3f(0, 0, 1);
564564 glutWireSphere(0.4, 10, 10);
565565 glPopMatrix;
566566
567567 glPopMatrix;
568568
569569 glutSwapBuffers;
570570end;
571571```
572572
573-![](/wiki-images/5/51/FullScreenAnimationPic1.jpg) <b>glPushMatrix</b> i <b>glPopMatrix</b> 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.
573+![](/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.
574574
575-You may wonder what is <b>T</b> 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. <b>glutGet</b> function with <b>GLUT_ELAPSED_TIME</b> parameter returns time in milliseconds from glutInit is called. By dividing that value with 1000, we get time in seconds.
575+You 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.
576576
577-<b>glRotatef</b> 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.
577+glRotatef 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.
578578
579579Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
580580
581581### Light
582582
583583This 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.
584584
585585For 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.
586586
587587``` pascal
588588unit utils;
589589
590590{$mode objfpc}{$H+}
591591
592592interface
593593
594594uses
595595 glut;
596596
597597function GetTotalTime: Single;
598598function GetDeltaTime: Single;
599599procedure FrameRendered(Count: Integer = 1);
600600function GetFPS: Single;
601601
602602implementation
603603
604604var
605605 OldTime: Integer = 0;
606606 FPSTime: Integer = 0;
607607 FPSCount: Integer = 0;
608608
609609function GetTotalTime: Single;
610610begin
611611 Result := glutGet(GLUT_ELAPSED_TIME) / 1000;
612612end;
613613
614614function GetDeltaTime: Single;
615615var
616616 NewTime: Integer;
617617begin
618618 NewTime := glutGet(GLUT_ELAPSED_TIME);
619619 Result := (NewTime - OldTime) / 1000;
620620 OldTime := NewTime;
621621end;
622622
623623procedure FrameRendered(Count: Integer);
624624begin
625625 Inc(FPSCount, Count);
626626end;
627627
628628function GetFPS: Single;
629629var
630630 NewTime: Integer;
631631begin
632632 NewTime := glutGet(GLUT_ELAPSED_TIME);
633633
634634 Result := FPSCount / ((NewTime - FPSTime) / 1000);
635635
636636 FPSTime := NewTime;
637637 FPSCount := 0;
638638end;
639639
640640end.
641641```
642642
643643As 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.
644644
645645Now, let's have fun with lights.
646646
647647OpenGL have several types of light... ambient, diffuse, point, spot, specular and emissive light.
648648
649649Ambient 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.
650650
651651Diffuse light can be represented as parallel light rays comming from far away. They will lit only vertices that are oriented towards the light source.
652652
653653Point 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.
654654
655655Spot 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.
656656
657657Just 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.
658658
659659Emissive 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.
660660
661661For simplicity we'll use only diffuse light in this tutorial. Later on, some other lights may appear in tutorials :)
662662
663663Let's see how to enable light in scene:
664664
665665``` pascal
666666const
667667 DiffuseLight: array[0..3] of GLfloat = (0.8, 0.8, 0.8, 1);
668668
669669 glEnable(GL_LIGHTING);
670670 glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
671671 glEnable(GL_LIGHT0);
672672```
673673
674-As you see, we enable lighting in OpenGL so lights affect scene you are rendering. Light parameters are set with <b>glLightfv</b> 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.
674+As 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.
675675
676676More about glLightfv: <http://www.opengl.org//documentation/specs/man_pages/hardcopy/GL/html/gl/light.html>
677677
678678If 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:
679679
680680``` pascal
681681glEnable(GL_COLOR_MATERIAL);
682682glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
683683```
684684
685-![](/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. <b>glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE)</b> tells OpenGL that glColor changes ambient and diffuse material. We'll discus materials more in later tutorials.
685+![](/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.
686686
687687One 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.
688688
689689After all those setting ups, light will shine up your cube :)
690690
691691Part of the text is copied from [The OpenGL Light Bible](http://www.falloutsoftware.com/tutorials/gl/gl8.htm)
692692
693693Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
694694
695695### Bitmap fonts
696696
697697Games and programs usually need to write some text on screen. GLUT provides several functions for drawing chars that are platform independent.
698698
699699First, we'll show how to use default bitmap fonts. Almost all code additions will be made to utils.pas unit.
700700
701701Since text will be drawn in 2D, we'll need to know width and height of viewport... so, we'll write two functions for that:
702702
703703``` pascal
704704function glGetViewportWidth: Integer;
705705var
706706 Rect: array[0..3] of Integer;
707707begin
708708 glGetIntegerv(GL_VIEWPORT, @Rect);
709709 Result := Rect[2] - Rect[0];
710710end;
711711
712712function glGetViewportHeight: Integer;
713713var
714714 Rect: array[0..3] of Integer;
715715begin
716716 glGetIntegerv(GL_VIEWPORT, @Rect);
717717 Result := Rect[3] - Rect[1];
718718end;
719719```
720720
721721We just get left/right, top/bottom and calculate width/height by subtracting them.
722722
723723There must be functions for entering and leaving 2D mode:
724724
725725``` pascal
726726procedure glEnter2D;
727727begin
728728 glMatrixMode(GL_PROJECTION);
729729 glPushMatrix;
730730 glLoadIdentity;
731731 gluOrtho2D(0, glGetViewportWidth, 0, glGetViewportHeight);
732732
733733 glMatrixMode(GL_MODELVIEW);
734734 glPushMatrix;
735735 glLoadIdentity;
736736
737737 glDisable(GL_DEPTH_TEST);
738738end;
739739
740740procedure glLeave2D;
741741begin
742742 glMatrixMode(GL_PROJECTION);
743743 glPopMatrix;
744744 glMatrixMode(GL_MODELVIEW);
745745 glPopMatrix;
746746
747747 glEnable(GL_DEPTH_TEST);
748748end;
749749```
750750
751-When entering 2D mode, we save current matrices and set 2D matrix using <b>gluOrtho2D</b> 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.
751+When 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.
752752
753753Leaving 2D mode just returns old matrices and enable ZBuffer.
754754
755755Now, we can create function for text drawing:
756756
757757``` pascal
758758procedure glWrite(X, Y: GLfloat; Font: Pointer; Text: String);
759759var
760760 I: Integer;
761761begin
762762 glRasterPos2f(X, Y);
763763 for I := 1 to Length(Text) do
764764 glutBitmapCharacter(Font, Integer(Text[I]));
765765end;
766766```
767767
768768**glutBitmapCharacter** 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.
769769
770-Character will be drawn at current raster position. To set desired raster position we call <b>glRasterPos</b> 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.
770+Character 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.
771771
772772This new functions will make text drawing very easy:
773773
774774``` pascal
775775procedure DrawGLScene; cdecl;
776776begin
777777 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
778778
779779 glLoadIdentity;
780780 glTranslatef(0, 0, -5);
781781 glRotatef(GetTotalTime * 10, 0, 0.5, 0.5);
782782
783783 glColor3f(1, 0, 0);
784784 glutSolidCube(2);
785785
786786 glEnter2D;
787787
788788 glColor3f(0.2, 0.8 + 0.2 * Sin(GetTotalTime * 5), 0);
789789 glWrite(20, glGetViewportHeight - 20, GLUT_BITMAP_8_BY_13,
790790 Format('OpenGL Tutorial :: Bitmap Fonts :: FPS - %.2f FPS', [FPS]));
791791
792792 glColor3f(1, 1, 1);
793793 glWrite(50, glGetViewportHeight - 60, GLUT_BITMAP_9_BY_15, 'GLUT_BITMAP_9_BY_15');
794794 glWrite(50, glGetViewportHeight - 90, GLUT_BITMAP_8_BY_13, 'GLUT_BITMAP_8_BY_13');
795795 glWrite(50, glGetViewportHeight - 120, GLUT_BITMAP_TIMES_ROMAN_10, 'GLUT_BITMAP_TIMES_ROMAN_10');
796796 glWrite(50, glGetViewportHeight - 150, GLUT_BITMAP_TIMES_ROMAN_24, 'GLUT_BITMAP_TIMES_ROMAN_24');
797797 glWrite(50, glGetViewportHeight - 180, GLUT_BITMAP_HELVETICA_10, 'GLUT_BITMAP_HELVETICA_10');
798798 glWrite(50, glGetViewportHeight - 210, GLUT_BITMAP_HELVETICA_12, 'GLUT_BITMAP_HELVETICA_12');
799799 glWrite(50, glGetViewportHeight - 240, GLUT_BITMAP_HELVETICA_18, 'GLUT_BITMAP_HELVETICA_18');
800800
801801 glColor3f(0.5, 0.5, 1);
802802 glWrite(
803803 glGetViewportWidth - glutBitmapLength(GLUT_BITMAP_9_BY_15, LazText) - 5,
804804 10, GLUT_BITMAP_9_BY_15, LazText);
805805
806806 glLeave2D;
807807
808808 glutSwapBuffers;
809809
810810 FrameRendered;
811811end;
812812```
813813
814-![](/wiki-images/b/ba/BitmapFontsPic1.jpg) We draw red cube and rotate it, and some text to show how various bitmap fonts look like. <b>glutBitmapLength</b> function is used to find width of string so it could be aligned to right. Code can easily be altered to center text.
814+![](/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.
815815
816816Note: See how cube looks without light.
817817
818818Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
819819
820820### Textures
821821
822822It's time to use textures :)
823823
824824This 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.
825825
826826Let's get started... we'll create display list for drawing textured rectangle:
827827
828828``` pascal
829829procedure CreateList;
830830begin
831831 glNewList(LIST_OBJECT, GL_COMPILE);
832832 glBegin(GL_QUADS);
833833 glTexCoord2f(1, 0);
834834 glVertex3f( 2, 2, 0);
835835 glTexCoord2f(0, 0);
836836 glVertex3f(-2, 2, 0);
837837 glTexCoord2f(0, 1);
838838 glVertex3f(-2,-2, 0);
839839 glTexCoord2f(1, 1);
840840 glVertex3f( 2,-2, 0);
841841 glEnd;
842842 glEndList;
843843end;
844844```
845845
846-Notice <b>glTexCoord</b> 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.
846+Notice 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.
847847
848848Texture loading is extremely easy with Vampyre Imaging Library:
849849
850850``` pascal
851851var
852852 Tex1, Tex2: GLuint;
853853
854854procedure InitializeGL;
855855begin
856856 glClearColor(0, 0, 0, 0);
857857 Tex1 := LoadGLTextureFromFile('ashwood.bmp');
858858 Tex2 := LoadGLTextureFromFile('Flare.bmp');
859859 glEnable(GL_TEXTURE_2D);
860860end;
861861```
862862
863863**LoadGLTextureFromFile** 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.
864864
865865To draw textured polygon you have to bind texture and setup texture coordinations (texture coordinations are set in display list in this tutorial):
866866
867867``` pascal
868868 ...
869869 glLoadIdentity;
870870 glTranslatef(-5, 0, -15);
871871 glBindTexture(GL_TEXTURE_2D, Tex1);
872872 glCallList(LIST_OBJECT);
873873 ...
874874```
875875
876876**glBindTexture** function is used to select texture. When you draw polygins they will have selected texture on them. It's that easy :)
877877
878878So, 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:
879879
880880``` pascal
881881 ...
882882 glLoadIdentity;
883883 glTranslatef(5, 0, -15);
884884 glBindTexture(GL_TEXTURE_2D, Tex1);
885885 glCallList(LIST_OBJECT);
886886
887887 glEnable(GL_BLEND);
888888 glBlendFunc(GL_ZERO, GL_SRC_COLOR);
889889 glLoadIdentity;
890890 glTranslatef(5, 0, -15);
891891 glBindTexture(GL_TEXTURE_2D, Tex2);
892892 glCallList(LIST_OBJECT);
893893 glDisable(GL_BLEND);
894894 ...
895895```
896896
897-As you can see, polygon is drawn first time like we allready know. Before second drawing we enable blending by calling <b>glEnable(GL_BLEND)</b>. Blending means that finall pixel color is calculated like this:
897+As 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:
898898
899899`DrawingColor * SRCBLEND + BackgroundColor * DESTBLEND`
900900
901-SRCBLEND and DESTBLEND are defined using <b>glBlendFunc</b> function. In this tutorial we set SRCBLEND to GL_ZERO (zero) and DESTBLENT to GL_SRC_COLOR (DrawingColor) and finall color is then:
901+SRCBLEND 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:
902902
903903`DrawingColor * 0 + BackgroundColor * DrawingColor`
904904
905-<figure>
906-<img src="TexturesPic1.jpg" title="TexturesPic1.jpg" />
907-<figcaption>TexturesPic1.jpg</figcaption>
908-</figure>
905+![](/wiki-images/d/d4/TexturesPic1.jpg)
909906
910907This 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
911908
912909Next time, we'll use extensions to show how to use singlepass multitexturing.
913910
914911Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
915912
916913### Multitexturing (extensions)
917914
918915When 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.
919916
920917Let's see how code looks like:
921918
922919``` pascal
923920procedure InitializeGL;
924921begin
925922 Load_GL_ARB_multitexture;
926923 glClearColor(0, 0, 0, 0);
927924 Tex1 := LoadGLTextureFromFile('Lazarus.bmp');
928925 Tex2 := LoadGLTextureFromFile('Mask.bmp');
929926 glActiveTextureARB(GL_TEXTURE0_ARB);
930927 glEnable(GL_TEXTURE_2D);
931928 glBindTexture(GL_TEXTURE_2D, Tex1);
932929 glActiveTextureARB(GL_TEXTURE1_ARB);
933930 glEnable(GL_TEXTURE_2D);
934931 glBindTexture(GL_TEXTURE_2D, Tex2);
935932end;
936933```
937934
938-First we need load OpenGL extension that will allow us to use multitexture functions. <b>Load_GL_ARB_multitexture</b> will try to load those extensions and will return TRUE if operation was successful.
935+First 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.
939936
940-To select texture stage you want to work on, use <b>glActiveTextureARB</b> 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.
937+To 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.
941938
942939Since we setup every thing in initialization function, all we have to do is to draw object:
943940
944941``` pascal
945942procedure DrawGLScene; cdecl;
946943begin
947944 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
948945
949946 glLoadIdentity;
950947 glTranslatef(0, 0, -5);
951948
952949 glBegin(GL_QUADS);
953950 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
954951 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
955952 glVertex3f(2.516, 2, 0);
956953 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
957954 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
958955 glVertex3f(-2.516, 2, 0);
959956 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
960957 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
961958 glVertex3f(-2.516,-2, 0);
962959 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
963960 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
964961 glVertex3f(2.516,-2, 0);
965962 glEnd;
966963
967964 glutSwapBuffers;
968965end;
969966```
970967
971-![](/wiki-images/5/52/MultitexturePic1.jpg) As you can see, difference is only in defining texture coordinations. We now use <b>glMultiTexCoord2fARB</b> function that takes texture stage and texture coordinations. Every thing else is unchanged.
968+![](/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.
972969
973970Today 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.
974971
975972Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
976973
977974### Render to texture
978975
979976This 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.
980977
981978First, we must create empty texture which we'll use to capture scene:
982979
983980``` pascal
984981procedure SetupRenderTexture;
985982var
986983 Data: Pointer;
987984begin
988985 GetMem(Data, 256*256*3);
989986 glGenTextures(1, @RenderTexture);
990987 glBindTexture(GL_TEXTURE_2D, RenderTexture);
991988 glTexImage2D(GL_TEXTURE_2D, 0, 3, 256, 256, 0, GL_RGB, GL_UNSIGNED_BYTE, Data);
992989 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
993990 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
994991 FreeMem(Data);
995992end;
996993```
997994
998995Buffer for 256*256 RGB image is created and it is used to setup 2D texture.
999996
1000997Main part is in drawing function:
1001998
1002999``` pascal
10031000procedure DrawGLScene; cdecl;
10041001var
10051002 TotalTime: Single;
10061003begin
10071004 glClearColor(0, 0, 0, 0);
10081005 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
10091006 glEnable(GL_LIGHTING);
10101007 glDisable(GL_TEXTURE_2D);
10111008 glViewport(0, 0, 256, 256);
10121009
10131010 TotalTime := GetTotalTime;
10141011
10151012 glLoadIdentity;
10161013 glTranslatef(0, 0, -5);
10171014 glRotatef(50 * TotalTime, 1, 0, 0);
10181015 glRotatef(100 * TotalTime, 0, 1, 0);
10191016 glRotatef(50 * TotalTime, 0, 0, 1);
10201017
10211018 glColor3f(1, 1, 1);
10221019 glutSolidCube(2);
10231020
10241021 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, 256, 256, 0);
10251022
10261023 glClearColor(0.18, 0.20, 0.66, 0);
10271024 glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
10281025 glDisable(GL_LIGHTING);
10291026 glEnable(GL_TEXTURE_2D);
10301027 glViewport(0, 0, AppWidth, AppHeight);
10311028
10321029 glLoadIdentity;
10331030 glTranslatef(0, 0, -7);
10341031 glRotatef(20 * TotalTime, 1, 0, 0);
10351032 glRotatef(50 * TotalTime, 0, 1, 0);
10361033
10371034 glBegin(GL_QUADS);
10381035 glTexCoord2f(1, 0);
10391036 glVertex3f(2, 2, 0);
10401037 glTexCoord2f(0, 0);
10411038 glVertex3f(-2, 2, 0);
10421039 glTexCoord2f(0, 1);
10431040 glVertex3f(-2,-2, 0);
10441041 glTexCoord2f(1, 1);
10451042 glVertex3f(2,-2, 0);
10461043 glEnd;
10471044
10481045 glutSwapBuffers;
10491046end;
10501047```
10511048
1052-![](/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. <b>glCopyTexImage2D</b> is used to capture scene to currently selected texture.
1049+![](/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.
10531050
10541051When 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.
10551052
1056-P.S. Captured texture can be saved using <b>SaveGLTextureToFile</b> function from [Vampyre Imaging Library](http://imaginglib.sourceforge.net/).
1053+P.S. Captured texture can be saved using SaveGLTextureToFile function from [Vampyre Imaging Library](http://imaginglib.sourceforge.net/).
10571054
10581055Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
10591056
10601057### Vertex array
10611058
10621059OpenGL 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.
10631060
10641061In 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.
10651062
10661063First, let's define some types and constants:
10671064
10681065``` pascal
10691066type
10701067 TVertex3f = record
10711068 X, Y, Z: Single;
10721069 end;
10731070
10741071 TColor3f = record
10751072 R, G, B: Single;
10761073 end;
10771074
10781075 VertexBuffer: array [0..5] of TVertex3f = (
10791076 (X : 1; Y : 1; Z : 0),
10801077 (X : -1; Y : 1; Z : 0),
10811078 (X : -1; Y : -1; Z : 0),
10821079 (X : 1; Y : 1; Z : 0),
10831080 (X : -1; Y : -1; Z : 0),
10841081 (X : 1; Y : -1; Z : 0)
10851082 );
10861083 ColorBuffer: array [0..5] of TColor3f = (
10871084 (R : 1; G : 0; B : 1),
10881085 (R : 0; G : 0; B : 1),
10891086 (R : 0; G : 1; B : 0),
10901087 (R : 1; G : 0; B : 1),
10911088 (R : 0; G : 1; B : 0),
10921089 (R : 1; G : 1; B : 0)
10931090 );
10941091```
10951092
10961093We have two buffers. One for vertex coordinates and one for vertex colors. This 6 vertices defines 2 triangles that forms rectangle.
10971094
10981095Drawing primitives using buffers is easy:
10991096
11001097``` pascal
11011098 glEnableClientState(GL_VERTEX_ARRAY);
11021099 glEnableClientState(GL_COLOR_ARRAY);
11031100 glVertexPointer(3, GL_FLOAT, 0, @VertexBuffer[0]);
11041101 glColorPointer(3, GL_FLOAT, 0, @ColorBuffer[0]);
11051102
11061103 glDrawArrays(GL_TRIANGLES, 0, Length(VertexBuffer));
11071104
11081105 glDisableClientState(GL_VERTEX_ARRAY);
11091106 glDisableClientState(GL_COLOR_ARRAY);
11101107```
11111108
1112-First we enable buffers we want to use using <b>glEnableClientState</b> function. Than we can select buffers we want to use. Every buffer type has own function for selecting (<b>glColorPointer</b>, <b>glEdgeFlagPointer</b>, <b>glIndexPointer</b>, <b>glNormalPointer</b>, <b>glTexCoordPointer</b>, <b>glVertexPointer</b>). 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:
1109+First 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:
11131110
11141111``` pascal
11151112type
11161113 TBufferData = record
11171114 DataBefore: TDataBefore;
11181115 Vertex: TVertex;
11191116 DataAfter: TDataAfter;
11201117 end;
11211118```
11221119
11231120`Bytes between elements = SizeOf(TDataBefore) + SizeOf(TDataAfter)`
11241121
11251122Last parameter if pointer to the begginig of buffer.
11261123
1127-When buffers are selected we can draw them using <b>glDrawArrays</b> 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).
1124+When 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).
11281125
11291126When buffers are not needed you can disable them.
11301127
11311128To demonstrate indexed mode, I made some simple mesh class that can load vertex, color and index data from external files:
11321129
11331130``` pascal
11341131type
11351132 TMesh = class
11361133 private
11371134 FVertices: array of TVertex3f;
11381135 FColors: array of TColor3f;
11391136 FIndices: array of Integer;
11401137 procedure FreeBuffers;
11411138 public
11421139 constructor Create;
11431140 destructor Destroy; override;
11441141 procedure LoadMesh(FileName: String);
11451142 procedure DrawMesh;
11461143 end;
11471144```
11481145
11491146FVertices will contain data about vertices, FColors data about color and FIndices data about indices when external file is loaded.
11501147
11511148First we'll write some code that deals with creation and destruction of class:
11521149
11531150``` pascal
11541151procedure TMesh.FreeBuffers;
11551152begin
11561153 FVertices := nil;
11571154 FColors := nil;
11581155 FIndices := nil;
11591156end;
11601157
11611158constructor TMesh.Create;
11621159begin
11631160 FreeBuffers;
11641161end;
11651162
11661163destructor TMesh.Destroy;
11671164begin
11681165 FreeBuffers;
11691166 inherited Destroy;
11701167end;
11711168```
11721169
11731170File 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:
11741171
11751172`3 3`\
11761173`-1 -1 0 1 1 1`\
11771174`1 -1 0 1 1 1`\
11781175`0 1 0 1 1 1`\
11791176`0`\
11801177`1`\
11811178`2`
11821179
11831180This 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).
11841181
11851182Code for loading this data will loke like this:
11861183
11871184``` pascal
11881185procedure TMesh.LoadMesh(FileName: String);
11891186var
11901187 MeshFile: TextFile;
11911188 VertexCount, IndexCount: Integer;
11921189 iV, iI: Integer;
11931190begin
11941191 FreeBuffers;
11951192
11961193 AssignFile(MeshFile, FileName);
11971194 Reset(MeshFile);
11981195
11991196 ReadLn(MeshFile, VertexCount, IndexCount);
12001197
12011198 SetLength(FVertices, VertexCount);
12021199 SetLength(FColors, VertexCount);
12031200 SetLength(FIndices, IndexCount);
12041201
12051202 for iV := 0 to VertexCount - 1 do
12061203 ReadLn(MeshFile,
12071204 FVertices[iV].X, FVertices[iV].Y, FVertices[iV].Z,
12081205 FColors[iV].R, FColors[iV].G, FColors[iV].B);
12091206
12101207 for iI := 0 to IndexCount - 1 do
12111208 ReadLn(MeshFile, FIndices[iI]);
12121209
12131210 CloseFile(MeshFile);
12141211end;
12151212```
12161213
12171214After loading data, we have everything for drawing:
12181215
12191216``` pascal
12201217procedure TMesh.DrawMesh;
12211218begin
12221219 glEnableClientState(GL_VERTEX_ARRAY);
12231220 glEnableClientState(GL_COLOR_ARRAY);
12241221 glVertexPointer(3, GL_FLOAT, 0, @FVertices[0]);
12251222 glColorPointer(3, GL_FLOAT, 0, @FColors[0]);
12261223
12271224 glDrawElements(GL_TRIANGLES, Length(FIndices), GL_UNSIGNED_INT, @FIndices[0]);
12281225
12291226 glDisableClientState(GL_VERTEX_ARRAY);
12301227 glDisableClientState(GL_COLOR_ARRAY);
12311228end;
12321229```
12331230
1234-As you can see, allmost everything is the same as for non-indexed drawing, except function that actually draw polygons. In this case we use <b>glDrawElements</b> 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.
1231+As 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.
12351232
12361233![](/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:
12371234
12381235`4 6`\
12391236`1 1 0 1 0 1`\
12401237`-1 1 0 0 0 1`\
12411238`-1 -1 0 0 1 0`\
12421239`1 -1 0 1 1 0`\
12431240`0`\
12441241`1`\
12451242`2`\
12461243`0`\
12471244`2`\
12481245`3`
12491246
12501247As 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.
12511248
12521249Download source code, linux executable or windows executable from [Lazarus CCR SourceForge](http://sourceforge.net/projects/lazarus-ccr/files/OpenGL%20Tutorial/).
12531250
12541251## Modern OpenGL using macOS
12551252
12561253> **Note:** the macOS OpenGL implementation is stuck at version 4.1 from 2010 and was deprecated by Apple on the release of macOS 10.14 (Mojave) in September 2018.
12571254
12581255A great feature of Lazarus is "write once, compile anywhere", where code should just work on macOS, Linux or Windows. However, to support all three of these systems you need to decide whether to only support legacy OpenGL or to use the leander "OpenGL Core" profile. For Linux and Windows users, recent versions of OpenGL are a superset of older versions. Therefore, a Linux user can mix and match old OpenGL code with modern shaders. This is **not** the case for Macintosh macOS (nee OS X) users.
12591256
12601257macOS provides two forms of support: the legacy mode supports all OpenGL features up to 2.1 (and GLSL 1.2). Alternatively, the user can select a modern **core** version of OpenGL (whereas most Linux and Windows users have access to the **compatibility** versions of OpenGL). The **core** versions of OpenGL [remove many deprecated legacy functions](http://github.prideout.net/modern-opengl-prezo/). This means that all the tutorials above will only compile in macOS's legacy mode. The core mode has no fixed function pipeline and therefore the developer must write their own shaders. In addition, the Core mode removes some [basic primitives like GL_QUADS](https://www.opengl.org/wiki/Legacy_OpenGL), so a quad needs to be replaced by two triangles (this actually makes sense: all vertices of a triangle are coplanar, but this is not necessarily the case for a quad, just like each leg of a 3-legged stool will always touch the ground, while a 4-legged stool might wobble if one leg is shorter than the others).
12611258
12621259The benefit of the core model is that it is lean, and generally easy to adapt to mobile devices (which use a similar, embedded form of OpenGL). See [Apple's website](https://developer.apple.com/opengl/capabilities/) to see the OpenGL supported by your operating system. A further wrinkle is that the Core modes of OpenGL are only supported on the Cocoa widgetset, so you can not use the Carbon widgetset. Three demo projects are available on [Github](https://github.com/neurolabusc/OpenGLCoreTutorials), and will compile using Lazarus 1.6.2 or later on Linux, Windows or macOS.
12631260
12641261## See also
12651262
12661263- [[Creating bindings for C libraries]]
12671264- [[OpenGL]]
12681265
12691266## External links
12701267
12711268- [OpenGL Light Tutorial](http://www.falloutsoftware.com/tutorials/gl/gl8.htm) - An OpenGL tutorial outlining how to create and manipulate lights, as well as deal with polygon surface reflections using normals.
12721269- [Apple OpenGL Documentation](https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_pg_concepts/opengl_pg_concepts.html).
12731270
12741271---
12751272*From the Free Pascal wiki, page "OpenGL Tutorial", as it stood on 2021-04-16 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