Welcome to the new Friends-of-FPC!

Here you can find all kinds of information about the FreePascal Compiler. We have many tutorials and howtos as well as a selection of tools to help you with your programming. We also have some example codes for you. And if you want to contribute some information/ sources/ tools yourself you can do so.
Also we have finally relaunched the FoFPC forum. It's your chance for some Q&A about everything FreePascal.

Friends-of-FPC

Tutorials: Learn how to code with FreePascal.

Source Codes: A collection of examples, miscellaneous source codes and open source stuff.

Tools and Help Files: Intro- duction of some tools that might help you with FPC.

Community

Forum: Ask or answer questions about the FreePascal Compiler, programming or just babble about coding.

Contribute! Contribute your own Tutorial, Source Codes or Tools and send them to us!

Website

About: Information about Friends-of-FPC.org.

OpenGL Tutorial Part 02 - by Ulf Ochsenfahrt

Hi, this is the second of the OpenGL with FreePascal tutorials. For a short introduction and setup instructions, see tutorial 01!

Idea

This time we will start differently than in the first tutorial. The goal is to understand the general OpenGL application outline. So, we will produce a general OpenGL application that will be the basis of the following OpenGL tutorials.

So, how does GLUT work? Remember, GLUT does all the windowing stuff for us. All we have to do is draw, if something has to be drawn and process events, if there are events. GLUT works on the basis of registering callback functions. They are called when the appropriate events have occurred. In this tutorial you will see how callback functions work and how to register them with GLUT.

General Outline

We'll have a standard application frame of course:

Program Tutor02;

Uses GL, GLUT;

Begin
End.

Our application is divided into several parts that are common to every GLUT OpenGL application. Sometimes parts may be omitted or occur in a completely different order. But these are the standard first-level subdivisions of it:

  • declaration of constants and variables
  • program initialization functions
  • miscellaneus functions
  • OpenGL list creation functions
  • drawing functions
  • callback functions
  • OpenGL and GLUT initialization
  • callback registration
  • glutMainLoop

Our general outline will look like this now:

Program Tutor02;

Uses GL, GLUT;

//declaration of constants and variables

//program initialization

//misc

//list creation

//drawing

//callback

Begin
  //OpenGL and GLUT initialization
  
  //callback registration
  
  //glutMainLoop
  glutMainLoop;
End.

WARNING

Of course you may, but you should NOT place code after the call to the glutMainLoop-function. Although it only looks like a procedure call, it is not. This procedure will NEVER return and thus code behind this procedure will never be executed.

Callback Functions

What are callback functions and how do they work?

Callback functions are functions that can be called by GLUT. GLUT has an internal event queue (event list) that keeps all the possible events. If there is an event in the event queue, it is removed and the appropriate callback function is called if it exists. If the event queue is empty, an special callback function - the idle function - is called if it exists.

Callback functions are the main parts of an GLUT OpenGL app, as they are the only functions that are known to GLUT. If you want to utilize a certain callback function, you have to write it and thereafter register it with GLUT.

How are callback functions declared?

Since GLUT (and windows) do not use the same subfunction call model, we have to tell FreePascal that we want to write functions that can be called by GLUT. In general, callback functions are declared like this:

//general callback function declaration
Procedure callbackfunction(parameterlist); stdcall;
Begin
End;

IMPORTANT: Notice the "stdcall;" at the end of the function declaration.

How are callback functions registered?

You have to register callback functions like this:

//general callback function registration
glutCallbackFunc(@callbackfunction);

IMPORTANT: Notice the "@" before the callback function name.

Which callback functions exist and how are they registered?

There are several callback functions with (of course) different behavior and tasks:

event GLUT register function to do
draw/paint glutDisplayFunc draw the entire scene
resize glutReshapeFunc setup the viewport and view matrix
idle glutIdleFunc do as you like, e.g. issue a glutPostRedisplay
key pressed glutKeyboardFunc process the character
window becomes (in)visible glutVisibilityFunc you shouldn't draw into an invisible window

See the GLUT specification for a complete list (available at opengl.org).

Description of the two most important callback functions

display callback

This is certainly the most important function for your program as it should draw the entire scene every time it is called. For sake of speed, it should make extensive use of lists. What lists are and how they work will be explained in a later tutorial. It takes no parameters.

Procedure draw; stdcall;
Begin
  //do some drawing
  ...
  glFlush;         //tell OpenGL to flush internal buffers
  glutSwapBuffers; //tell GLUT to actually show the buffer (only if double buffered)
End;

  //draw callback function registration
  glutDisplayFunc(@draw);

It is important to remember, that OpenGL can (and does) delay the actual painting until the next glFlush is issued. If you work with double-buffered graphics, you also have to issue a glutSwapBuffers to actually show what you have drawn into the buffer before.

reshape callback

This function is important (lets say it's second place) as well, as it gives you the actual screen size which may differ from what you have requested (see glutInitWindowSize). You should use it to setup the projection matrix (see math tutorial).

Procedure reshape(width, height : LongInt); stdcall;
Begin
  //setup new projection matrix which depends on window size
  ...
End;

  //reshape callback function registration
  glutReshapeFunc(@reshape);

For more detail on how to set up this, see the math tutorial. For now, it may suffice to know, that the standard code from the first tutorial should fare you well.

General Outline Version 2.0

Our general outline will look like this now:

Program Tutor02;

Uses GL, GLUT;

//declaration of constants and variables

//program initialization

//misc

//list creation

//drawing

//callback
Procedure reshape(width, height : LongInt); stdcall;
Begin
  //setup new projection matrix which depends on window size
  ...
End;

Procedure draw; stdcall;
Begin
  //do some drawing
  ...
  glFlush;         //tell OpenGL to flush internal buffers
  glutSwapBuffers; //tell GLUT to actually show the buffer (only if double buffered)
End;

Begin
  //OpenGL and GLUT initialization
  
  //callback registration
  glutDisplayFunc(@draw);
  glutReshapeFunc(@reshape);
  
  //glutMainLoop
  glutMainLoop;
End.

You still cannot actually compile this application - there are still some important parts missing.

OpenGL and GLUT initialization

GLUT initialization

Let's start with the easier of the two parts. We have already covered a little bit of GLUT initialization and there is not much left. We had callback functions and the glutMainLoop. We still need the start initialization. There's really not much about this, the standard parameters will fare you well or you can easily see what you may change.

At the program start, we have to tell GLUT that we want to have a window and set some parameters.

  • let GLUT initialize itself
  • set initial window position (optional)
  • set initial window size (optional)
  • set initial display mode
  • create a window
  • register callback functions
  • glutMainLoop

GLUT initializes itself

//let GLUT initialize itself
glutInit(@argc, argv);

There's not much to say about this. Just a

WARNING

GLUT uses some of the comand-line parameters, so you have to be careful if you want to make use of them.

set initial window position and size

//set initial window position
glutInitWindowPosition(x, y);

//set initial window size
glutInitWindowSize(xsize, ysize);

If these are not set, the standard values of the underlying windowing system (e.g. Microsoft Windows) are used.

window creation

//create a window
glutCreateWindow('Tutorial 02');

You might be able to create more than one GLUT-(OpenGL)-Window. Try if you like, but be careful: The callback functions are global and you might have to change the actual window and things...

That's all initialization needed at this stage of development. We'll come back to this some time later.

OpenGL drawing

clearing the screen

As we start simple (!), we'll have to clear the screen before starting to draw. Sometimes there is no need to clear the screen, e.g. if there is an object or polygon at every point of the screen.

As there may be many buffers, we will have to tell OpenGL, which buffer to clear. For example, we might use a COLOR buffer, a DEPTH buffer and a STENCIL buffer in some application.

//clear the color buffer
glClear(GL_COLOR_BUFFER_BIT);

//clear the color and the depth buffer
// glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);  

(In some future tutorial, this will be extended.)

the 3. dimension

Although OpenGL offers 2-dimensional drawing as well, we'll start with the 3. dimension (as it's more interesting...).

general outline

In general, OpenGL drawing works like this:

You tell OpenGL what you want to draw, e.g. a line or a triangle. There is actually only little difference between those two modes of drawing. Yeah, let's call it like that. You select a mode of drawing.

Then you give OpenGL the parameters needed, which will mainly consist of vector data (at least at first).

Last but..., you tell OpenGL that you have finished giving it parameters and that this shall be drawn to the screen.

Yes, it's actually as easy as this. Of course, there are lots of parameters you can change, that influence how the objects are being drawn, but in general it's always like this. So let's have a look at

some code

//draw something ;-)
glBegin(GL_QUADS);       //draw something with 4 vertexes
  glVertex3f(-0.5, -0.5, -2.0);  //first vertex
  glVertex3f(-0.5,  0.5, -2.0);  //second vertex
  glVertex3f( 0.5,  0.5, -2.0);  //...
  glVertex3f( 0.5, -0.5, -2.0);
glEnd;                   //thats four vertexes, draw(!!)  

Don't forget to end every begin.

Our mode of drawing is GL_QUADS. Every corner of our rectangle has 3 coordinates (x, y, z). The negative part of the z-axis is the direction of view. Normally our window has x and y coordinates in the range of -1.0 to 1.0. That's easy, huh?

setting up the projection matrix

There's not much left, as you can see in the "General Outline Version 2.0" and we'll finish this tutorial soon. Unfortunatly this is the part that involves enough math to make it difficult to explain.

That's why I don't explain it too deeply. Think of this: We have 3-dimensional objects - thus we have 3-dimensional vectors to the vertexes of our objects - BUT, we only have a 2-dimensional screen. So we have to think of some mathematical formula to calculate 2-dimensional vectors out of the 3-dimensional ones.

Imagine a piece of wood with a square hole in it. The square is exactly 2 by 2 something. Your head is at some point before this hole looking at it directly. Now, imagine you stand in front of a wall, so you cannot look infinetly wide. Then the space you can see through that hole is a frustum, that is, a pyramid without its top.

Now the hole in the wood is our screen. All objects behind the screen can be seen on it, while the space we see has the form of a frustum. The correct mathematical formula can be expressed with a 4-dimensional matrix (not the movie !!). However, OpenGL offers a possibility to easily setup a matrix with those values.

glMatrixMode(GL_PROJECTION); //switch to projection-matrix
glLoadIdentity;                                            //setup standard matrix
glFrustum(-1.0*aspect, 1.0*aspect, 1.0, -1.0, 1.0, 20.0);  //setup viewing matrix

glMatrixMode(GL_MODELVIEW);  //switch back to the modelview-matrix
glLoadIdentity;              //setup standard matrix

OpenGL has more than one matrix. By the way, it has three different matrixes, the PROJECTION, the MODELVIEW and the TEXTURE matrix. However, the projection matrix keeps the formula we have only just spoken of. We'll switch back to the modelview matrix, as we will need it for other calculations.

General Outline Version 3.0

Our general outline will look like this now:

Program Tutor02;

Uses GL, GLUT;

//declaration of constants and variables

//program initialization

//misc

//list creation

//drawing

//callback
Procedure reshape(width, height : LongInt); stdcall;
Var aspect : glFloat;
Begin
  aspect := width / height;
  //setup new projection matrix which depends on window size
  glMatrixMode(GL_PROJECTION); //switch to projection-matrix
  glLoadIdentity;                                            //setup standard matrix
  glFrustum(-1.0*aspect, 1.0*aspect, 1.0, -1.0, 1.0, 20.0);  //setup viewing matrix

  glMatrixMode(GL_MODELVIEW);  //switch back to the modelview-matrix
  glLoadIdentity;              //setup standard matrix
End;

Procedure draw; stdcall;
Begin
  //clear the color buffer
  glClear(GL_COLOR_BUFFER_BIT);
  
  //do some drawing
  glBegin(GL_QUADS);       //draw something with 4 vertexes
    glVertex3f(-0.5, -0.5, -2.0);  //first vertex
    glVertex3f(-0.5,  0.5, -2.0);  //second vertex
    glVertex3f( 0.5,  0.5, -2.0);  //...
    glVertex3f( 0.5, -0.5, -2.0);
  glEnd;                   //thats four vertexes, draw(!!)
  
  glFlush;         //tell OpenGL to flush internal buffers
//  glSwapBuffers;   //we actually don't use the double-buffer
End;

Begin
  //OpenGL and GLUT initialization
  glutInit(@argc, argv);
  glutCreateWindow('Tutorial 02');
  
  //callback registration
  glutDisplayFunc(@draw);
  glutReshapeFunc(@reshape);
  
  //glutMainLoop
  glutMainLoop;
End.

When the program is started, it initializes GLUT and registers two callback functions. GLUT creates a window and calls the reshape callback, which setups the projection matrix. GLUT then issues a DRAW event, because the window shall be shown on the screen. The draw callback then draws a white rectangle on the screen. And that's it.

I have deeply dropped into OpenGL and GLUT and I hope you got an idea of what it is all about. The next few tutorials will explaine some details that have been left out now and then we'll start making special effects (imagine diabolic grin). Nevertheless we will keep to this general outline so you should have an idea of what I was taking about.

Back to previous page

Useful Links









Link to us