OpenGL Tutorial

From Free Pascal wiki
Revision as of 11:40, 28 July 2006 by Borut (talk | contribs) (Drawing a simple shape)

OpenGL 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.

You can find more information about OpenGL here.

GLUT (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.

You can find more information about GLUT here.

Many OS comes with preinstalled GLUT, but if yours don’t have one you can easily find it using Google.

Windows binaries can be downloaded from www.xmission.com.

Creating first GLUT program

In order to use GLUT, you must first initialize it. This is done using glutInit function. This function can parse command line and set parameters for main window, but it expect input in C/C++ like style. You'll have to write your own function to make conversation from ParamCount and ParamStr to C/C++ like command line parameters.

procedure glutInitPascal(ParseCmdLine: Boolean); 
var
  Cmd: array of PChar;
  CmdCount, I: Integer;
begin
  if ParseCmdLine then
    CmdCount := ParamCount + 1
  else
    CmdCount := 1;
  SetLength(Cmd, CmdCount);
  for I := 0 to CmdCount - 1 do
    Cmd[I] := PChar(ParamStr(I));
  glutInit(@CmdCount, @Cmd);
end;

Basically, you create an array and fill it with strings from ParamStr. This function also takes one parameter that can control what is passed to glutInit... whole command line or just executable file name.

More about glutInit: http://www.opengl.org/resources/libraries/glut/spec3/node10.html

Next, you need to create main window. You'll set display mode for main window using glutInitDisplayMode. It takes only one parameter which is combination of flags. Usually GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH combination is all you need.

More about glutInitDisplayMode: http://www.opengl.org/resources/libraries/glut/spec3/node12.html

Position and size is controled using glutInitWindowPosition and glutInitWindowSize. They take 2 parameters. X and Y coordination, and width and height. You can also use glutGet to find screen size and set window to center of screen.

More 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

Finnaly, window can be created using glutCreateWindow function. It will create window and set it caption to string you pass as parameter. As a result it will return window handle that can be used with other functions that require it.

More about glutCreateWindow: http://www.opengl.org/resources/libraries/glut/spec3/node16.html

Before program can enter main loop, you must set some callbacks. This time you need callback for drawing window, resizing and for getting keyboard input. This callbacks are set using glutDisplayFunc, glutReshapeFunc and glutKeyboardFunc.

More about setting callbacks: http://www.opengl.org/resources/libraries/glut/spec3/node45.html#SECTION00080000000000000000

Drawing function will look like this:

procedure DrawGLScene; cdecl;
begin
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
  glutSwapBuffers;
end;

This will only clear window to background color and reset zbuffer (don't worry about zbuffer... we'll tell more about it later).

Resize function will look like this:

procedure ReSizeGLScene(Width, Height: Integer); cdecl;
begin
  if Height = 0 then
    Height := 1;

  glViewport(0, 0, Width, Height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45, Width / Height, 0.1, 1000);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
end;

With this code, you tell OpenGL where it should draw and set matrices to desired value (matrice functions will be explained later).

You get keyboard input with this code:

procedure GLKeyboard(Key: Byte; X, Y: Longint); cdecl;
begin
  if Key = 27 then
    Halt(0);
end;

This function will instruct program to exit if you press ESC key. GLUT is made to be event driven and the only way to terminate program is to call Halt inside one of callback functions. If you close window on some other way, it will disapere but prugram will continue to loop througt main loop infinitly.

To start main loop you call glutMainLoop. It will enter loop that never ends and call all yours callback functions.

Main part of program looks like this:

const 
  AppWidth = 640; 
  AppHeight = 480; 

procedure InitializeGL; 
begin 
  glClearColor(0.18, 0.20, 0.66, 0); 
end; 

var 
  ScreenWidth, ScreenHeight: Integer; 
begin 
  glutInitPascal(True); 
  glutInitDisplayMode(GLUT_DOUBLE or GLUT_RGB or GLUT_DEPTH); 
  glutInitWindowSize(AppWidth, AppHeight); 
  ScreenWidth := glutGet(GLUT_SCREEN_WIDTH); 
  ScreenHeight := glutGet(GLUT_SCREEN_HEIGHT); 
  glutInitWindowPosition((ScreenWidth - AppWidth) div 2,
    (ScreenHeight - AppHeight) div 2); 
  glutCreateWindow('OpenGL Tutorial 1'); 

  InitializeGL; 

  glutDisplayFunc(@DrawGLScene); 
  glutReshapeFunc(@ReSizeGLScene); 
  glutKeyboardFunc(@GLKeyboard); 

  glutMainLoop; 
end.

Next tutorial will add some code that will draw simple shape.

Downloads: source code, linux executable, windows executable from Lazarus CCR SourceForge.

Drawing a simple shape

This time we shall add just a few lines of code and focus on explanation of some of the OpenGL functions.

Let us explain code you allready have.

  .
  .
  .
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity;
  gluPerspective(45, Width / Height, 0.1, 1000);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity;
end;

Using 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.

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.

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.

Now, you'll change model matrix... for this time, you just set it to identity.

OK... and now, the code for drawing the first shape:

procedure DrawGLScene; cdecl;
begin
  glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);

  glLoadIdentity;
  glTranslatef(0, 0, -5);

  glBegin(GL_TRIANGLES);
    glColor3f(1, 0, 0);
    glVertex3f(-1, -1, 0);

    glColor3f(0, 1, 0);
    glVertex3f(1, -1, 0);

    glColor3f(0, 0, 1);
    glVertex3f(0, 1, 0);
  glEnd;

  glutSwapBuffers;
end;

We have allready used glClear function. It will just reset buffers. We'll skip next two functions and head for drawing ones.

glBegin 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.

GL_LINES: Treats each pair of vertices as an independent line segment. Vertices 2n-1 and 2n define line n. n/2 lines are drawn.

GL_LINE_STRIP: Draws a connected group of line segments from the first vertex to the last. n-1 lines are drawn.

GL_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.

GL_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.

GL_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.

GL_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.

GL_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.

GL_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.

GL_POLYGON: Draws a single, convex polygon. Vertices 1 through n define this polygon.

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.

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.

Ogltut2pic1.jpg

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.

In the end you call glEnd functions that finishes drawing. You could now start another drawing block with new glBegin function if you wish.

Downloads: source code, linux executable, windows executable