Dev 132 – Using OpenGL – part 2 of 4

Devoting some space to the GLUT library before we start to see how to use OpenGL will save us a lot of time later by not addressing issues related to a specific operating system.


Introduction

As already said, by its nature OpenGL is a sophisticated API for creating 3D graphics and has more than 300 commands that allow us to generate very complex and realistic scenes.

It is normal to marvel agains the complete lack of functions for display management. There is not even a system to receive commands from the keyboard, to detect mouse activity and use the audio capabilities. The motivation for such a choice stems from the desire of the creators of the API to make it as platform independent as possible. In fact a single function for opening windows will lead to an implementation based on direct calls to the host operating system’s API of, which is in conflict with the original idea.

Some preliminary specifications

The first rule that OpenGL developers have followed to maintain a high level of portability has been to create a standard nomenclature for functions and for the declaration of data types. For the first is used a system which allows to identify at a glance the library to which a function belongs, the command, the number of arguments it requires and the type of data used. In Figure 1 there is shown an example of a function where is visible the suffix ‘gl’ which indicates that the function belongs to the OpenGL library, the name of Vertex command, the number of arguments equal to 3, and the final ‘f’ that indicates that float is the type to use for parameters.

Figure 1 – An example of naming conventions of functions in OpenGL

It ‘s important to keep in mind another factor when using the OpenGL functions, most C++ compilers consider the numerical values being of type double by default​​, so you need to add the symbol ‘f’ to each argument passed to a function that accepts float data type, as shown:

glColor4f (0.0f, 1.0f, 0.0f);
glVertex3f (0.0f, 10.0f, 0.0f);

When we write a program normally we use data types that are defined by the compiler, but that somehow are related to the underlying machine architecture. We can not, in fact, be sure that a given type of float or int C is treated the same on different platforms. This leads, of course, to problems about portability; so OpenGL has been provided with its own set of data types that can be used with confidence that they will be interpreted in the same manner everywhere.

Following you can find a list of all defined types, their corresponding C and the internal representation all with the standard suffixes identical to those used for the functions naming:

Data typeInternal representationC typeC literal suffix
GLbyte8bit integersigned charb
GLshort16bit integershorts
GLint,
GLsizei
32bit integerlongl
GLfloat,
GLclampf
32bit floating pointfloatf
GLdouble,
GLclampd
64bit floating pointdoubled
GLubyte,
GLboolean
8bit unsigned integerunsigned charub
GLushort16bit unsigned integerunsigned shortus
GLuint,
GLenum,
GLbitfield
32bit unsigned integerunsigned longui

As you can see also the data types uses a standard naming structure with the ‘GL’ prefix to distinguish them from the types defined by C. The significance of most of them is clear and known, but they have added a lot more and useful ones. First, the type GLbitfield is useful for those variables that must contain bit fields. GLenum is used for the enumerated variables, while GLclamp, with suffix ‘f’ or ‘d’ represents a single or double-precision floating-point type which values can vary only between 0.0 and 1.0. Such type is useful because many functions take values ​​which vary within this range. For example, the function glColor3f wants as parameters the RGB values of a color but in this case the intensity of the single component is defined in the range 0.0 to 1.0 and not between 0 and 255 as we could expect.

The GLUT library

To facilitate the learning process of OpenGL and accelerate development was created an auxiliary library called AUX, but due to some problems of implementation and compatibility was soon replaced by GLUT, written by Silicon Graphics.[(br)][(br)]

The GLUT acronym stands for ‘OpenGL Utility Toolkit’, not to be confused with GLU the ‘OpenGL utility library’. GLUT is a cross-platform library that contains many functions for creating windows, pop-up menu and allows the management of the keyboard, joystick and video in full screen mode. Even developers without programming experience of GUIs, in a particular operating system, are thus able to achieve complete functional and fully portable programs.

As the old AUX, however, GLUT is unable to meet the development needs of commercial applications, but is excellent in teaching for testing ideas or creating presentation programs. For these reasons GLUT will be used throughout the course of this special. References to download GLUT can be found directly from the site www.opengl.org

Our first program with GLUT

After many introductory words we finall can begin to actually use these libraries so now we will study how to build a full executable that opens a blank window. This code can be used as a template for all programs you will made. Following the complete source code

void SetupRC()
{
    glClearColor(0.0f,0.0f,0.0f,1.0f);
}

void ChangeSize(GLsizei w, GLsizei h)
{
    glViewport(0, 0, w, h);
}

void RenderScene()
{
    glClear(GL_COLOR_BUFFER_BIT);
    glutSwapBuffers();
}

void main()
{
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize(640, 480);
    glutCreateWindow("My first GLUT program");

    SetupRC();

    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutMainLoop();
}

Looking at the following code we analyze, then each function. We start from the main() where we find the following call

glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);

containing a parameter consists of several flags. The first, GLUT_RGBA, tells GLUT to initialize a window that supports a 32Bit video mode – the suffix RGBA stands for Red, Green, Blue and Alpha, where alpha is the transparency channel. GLUT_DOUBLE tells to the library info about format of the two buffers used for the final images storage.

Theese buffers are used to achieve the mechanism known as ‘double-buffering’ that allows you to use a memory location at which all drawing operations remains hidden behind the scenes. After the drawing process the image is swapped quickly with that still visible on the screen. In this manner is possible to avoid the annoying and well-known effect of flickering. This setting is the opposite of GLUT_SINGLE in which case each drawing command will cause an immediate update on the displayed window. To swap the buffers just mentioned we must launch the ‘glutSwapBuffers()’ that we will soon explain.

Then we find the call

glutInitWindowSize(640, 480)

which does nothing but make GLUT set a size of 640×480 pixels for the window to be created. Inside the official GLUT documentation are also mentioned functions to position and move the window on the screen or even switch to fullscreen mode.

Finally arrived the time to create our display window and that can be obtained by calling

glutCreateWindow("My first GLUT program")

it also sets the text displayed as windows caption. The last line of code

glutMainLoop()

causes GLUT to launch a cycle that “keeps alive” the program until the close button of the window will be clicked.

So far these instructions, by themselves, are sufficient to run a minimal program which does not perform any operation: it just displays a blank window. In our applications, presentations or games we essentially need that:

  • At application start must there is an environment initialization procedure
  • At regular intervals the program will draw a frame on the window
  • It will reacts to the window resize signals

The first and second conditions are quite obvious and will be implemented by the functions SetupRC() and RenderScene(). The third, however, means that every time a user resizes the window or minimize the program it must, somehow, change the size of the displayed image. This operation will be performed by ChangeSize().

We observe that we have no way to know in advance how to handle system events. GLUT once again comes to help us solve this problem using a few commands that take as pointer to our function as a parameter and link it to a particular system event. So, whenever there a downsizing is reached, GLUT will launch the function passed as an argument to:

glutReshapeFunc((void (* func GLUTCALLBACK) (int width, int height))

In our case we have

glutReshapeFunc(ChangeSize)

where the function ChangeSize() should accept two integer values ​​as the width and height of the window after resizing. In practice, as shown in the listing, the function is declared in this manner

void ChangeSize(int w, int h);

For the less experienced we want to clarify that the function names and parameters are not significant and can change them at will.

The drawing function, which has the task of producing the scene, is passed to the

glutDisplayFunc(void (* func GLUTCALLBACK) (void))

In our listing we find

glutDisplayFunc(RenderScene)

with RenderScene() declared with no parameters.

The function glClearColor() within SetupRC() code indicates to OpenGL what color should be used for to clear the drawing buffer. We chose the black color by setting the values ​​of the RGB components to 0.0f.

In the function RenderScene() we can find two instructions, the first of which,

glClear(GL_COLOR_BUFFER_BIT)

erases the contents of the color buffer, ie, that contains the rendered image. In this way we prepared the buffer to be filled by another frame of the scene.

After you have completed every drawing operations becomes necessary to display the image produced by copying the contents of the ‘color buffer’ into ​​the visible area of the window. That operation can be obtained by calling the function glutSwapBuffers(). This function internally calls the glFlush() that determines the processing of all instructions previously stored in the Command Buffer, triggering the process of rendering.

Managing the viewport

In almost every applications users has the ability to resize the window where the program is running. It’s our intention to write programs that display the correct output in all conditions; both when the user enlarges the window to full screen or when makes it smaller the output must be properly scaled.

Next listing presents the new version of the ChangeSize().

void ChangeSize(GLsizei w, GLsizei h)
 {
     GLfloat aspectRatio;
     if (h == 0) h = 1;

     aspectRatio = (GLfloat) w / (GLfloat) h;

     glViewport(0, 0, w, h);

     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();

     if (w <= h)
         glOrtho(CV_SINISTRA, CV_DESTRA, CV_ALTO / aspectRatio, CV_BASSO / aspectRatio, CV_VICINO, CV_LONTANO);
     else
         glOrtho(CV_SINISTRA * aspectRatio, CV_DESTRA * aspectRatio, CV_ALTO, CV_BASSO, CV_VICINO, CV_LONTANO);

     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
}

The first operation performed calling the function

void glViewport(GLint x, GLint y, GLsizei width, GLsizei height)

The parameters x and y are related to the position from the bottom-right in which makes OpenGL produces output inside the window. Usually to make this area coincide with the entire window the parameters x and y are set to 0 while width and height assume the value of the parameters w and h passed from GLUT as follows:

glViewport(0, 0, w, h)

Then you have to set the clipping volume. In the previous article we talked about clipping area for images in two dimensions. Adding a further dimension no longer makes sense to talk about areas; so, we can set the volume within which the objects of the scene are represented by calling the function

glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top,GLdouble near, GLdouble far)

The left and right parameters define the range of coordinates along the x-axis within which images are displayed. Bottom and top define the same type of interval but on the y axis, while near and far on the axis of z.

The function glOrtho also indicates to OpenGL to use an orthographic system of projection where there is not the typical effect of perspective. All the lines that make up an object appear having the same size even if they lie close to the observer or at a great distance.

Other instructions will be discussed in future articles, but for now is sufficient to know that they inform OpenGL to which matrix apply the shown settings.

Finally it may happen that, by resizing the application window, the ratio between width and height changes determining a non-desired deformation to the final image. However, by calculating the ratio between the size of the window, based on the values ​​passed to the function ChangeSize(), we can make the output immune to any distortion

if (w >= h)
    glOrtho (-100.0, 100.0, -100.0 / aspect ratio, 100.0 / aspect ratio, 1.0, -1.0);
else
    glOrtho (-100.0 * Aspect ratio, aspect ratio * 100, -100, 100, 1.0, -1.0);

aspectRatio = (GLfloat) w / (GLfloat h)

Creating a simple animation

Unlikely we would like to write programs that generate static images, and that is why we will learn to use another feature of GLUT who worthily fulfills this purpose. Indeed, there is a command that allows us to specify what function to run each time interval

Unlikely we would like to write programs that generate static images, and that is why we will learn to use another feature of GLUT who worthily fulfills this purpose. Indeed, there is a command that allows us to specify what function to run each time interval

glutTimerFunc void (unsigned int msecs, void (* func) (int value), int value)

The first parameter specifies the number of milliseconds GLUT must wait before running the function specified in the parameter (* func). Following listing carries an example:

void Timer(int v)
{
    glutPostRedisplay();
    glutTimerFunc(1, Timer, 1);
}

void main()
{
    ...
    ...    
    glutTimerFunc(1, Timer, 1);
    ...
    ...
}

Here the function is called after 1 millisecond of delay, causing the redraw of the entire scene through the function RenderScene() aside from further activation of the timer.

Conclusions

This article has covered all the basics needed to use GLUT to implement our test applications. Anyone wishing to know more about and/or wishing to learn the use of joystick, mouse, 3D texts and more into programs, can refer to the documentation inside the attached package.

Bibliography

  1. Richard S.Wright jr. and Benjamin Lipchak, “OpenGL SuperBible – Third Edition”, SAMS, 2005, ISBN 0-672-32601-9

Original article

Downloads

Leave a Reply

Your email address will not be published. Required fields are marked *