Thursday, April 28, 2011

Creating an OpenGL Window with Freeglut in C++

It seems everybody exudes the virtues of OpenGL's 3D capabilities. That's all fine and good, but what about 2D?

While searching for information on creating my own 2D hardware-accelerated platform, I came across this excellent website.

http://nehe.gamedev.net/

- See the opengl tutorials on the left-hand side.

And as always, the official OpenGL website is a great resource.

http://www.opengl.org/resources/libraries/

Making a window using Freeglut to accelerate it through OpenGL isn't too difficult.

I can't remember where I found the code I am using as an example, but the gist of it can be found here:

http://www.codeproject.com/KB/openGL/OpenGLWindowWithGLUT.aspx?display=Mobile

(Credit goes to whomever wrote it)

Differences include:

  • This probably won't run on a Windows box
  • We have to compile it manually (see below)

Alright. Onto the code.

First we have the headers. They are C headers, but Freeglut is a C++ friendly library so there shouldn't be any problems.

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <GL/glew.h>

#include <GL/freeglut.h>

#include <GL/gl.h>

We are importing two main categories of libraries: standard C libraries that handle input/output, library functions and strings as well as the OpenGL libraries.

using namespace std;

Use the standard namespace so you don't have to write out the path of the library functions. It makes reading the code a lot easier.

Now to include a method that will display a static something in the window when we get to that point.

The reason we are writing this function right after the include and namespace statements is because both C and C++ (to name two of many programming languages) cannot call a method that it does not know about at compile time. If this function was at the bottom of our file and we called it at the top, it wouldn't know that function existed yet so it would fail.

void display(void)

{

/* clear all pixels */

glClear (GL_COLOR_BUFFER_BIT);

/* draw white polygon (rectangle) with corners at

* (0.25, 0.25, 0.0) and (0.75, 0.75, 0.0)

*/

glColor3f (1.0, 1.0, 1.0);

glBegin(GL_POLYGON);

glVertex3f (0.25, 0.25, 0.0);

glVertex3f (0.75, 0.25, 0.0);

glVertex3f (0.75, 0.75, 0.0);

glVertex3f (0.25, 0.75, 0.0);

glEnd();

/* don't wait!

* start processing buffered OpenGL routines

*/

glFlush ();

}

The statements are fairly self-explainatory here. glClear clears the window of previous drawings, sets a colour (white), creates a polygon (in this case, a square/rectangle), sets the points and then completes the shape, It then calls the glFlush so that all images that have been declared (have a Begin/End) get displayed.

The reason for having a flush statement is because a) it is faster to store a bunch of shapes in memory and display them after a period of time (aka a flush of the video buffer) than it is to display each and every object right after it gets created, plus b) it makes predicting and controlling frame rates a LOT easier. There are lots of reasons I'm probably not getting to, but it is important.

You could put the flush statement in the main loop after the display() method, but that would be if you want animations or are doing game design, and I recommend keeping the main loop as clean as possible. Plus it would make conditions a lot easier to write. Sometimes the flush() method is skipped because the hardware cannot keep up with the window which causes the dropping of frames to keep framerate acceptible. It will cause 'lag', but won't slow the entire program down to a crawl.

Another point to note: the glVertex3f function. Perhaps you are thinking it should be 'glVertex3d' because we are dealing with 3d graphics libraries here. This is not the case. The '3f' part of the function stands for '3 float parameter' (3f) function. As you can see, the method as 3 values that are floats and must stay as floats. (A float is a number with decimal places, smaller than a double value) If there is no number/letter suffix to the method, it probably has just one type of input parameter.

There could be a glVertex3d method, which would take doubles. Or a glVertex3i, which takes 3 integers. As you can see, the number/letter notation is a great way to know how the method gets used. Of course, you need to know the different C types as a pre-requesite to make good use of this.

void init (void)

{

/* select clearing (background) color */

glClearColor (0.0, 0.0, 0.0, 0.0);

/* initialize viewing values */

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);

}

The init method is next. It takes no parameters itself, like the display method above. All this does is set a few values for the window we'll be creating. The window we'll be drawing will need a background colour (otherwise the background of your computer will be seen through it) and some values set.

I'm not an expert in OpenGL by any means, so when I describe the glMatrixMode() method, I could be off.

There are a few different modes that OpenGL can operate in. GL_PROJECTION mode which I'm guessing is a more 2D-oriented mode, GL_MODELVIEW which does 3D, GL_TEXTURE which applies images and other texturey things to a surface (2D or 3D) therefore requires efficient I/O operations, and GL_COLOR which works on colour algorithms such as blending, transparencies and effects than actual objects.

glOrtho is part of the GL_PROJECTION Matrix Mode. It may be 2D only. The first 4 double values determine the four points of the buffer we display. Left, Right, Bottom, Top. The next two values are near and far values, glOrtho seems to deal with inversion and clipping. The OpenGL article seems to depict that it is a matrix transformation that replaces the current matrix (or what has been written to the video buffer).

Anyway, enough math for now. It may be better to experiment with changing values than to explain it with words.

The main() method, not to be mistaken for the main loop I mentioned earlier, sets up the window.

/*

* Declare initial window size, position, and display mode

* (single buffer and RGBA). Open window with "hello"

* in its title bar. Call initialization routines.

* Register callback function to display graphics.

* Enter main loop and process events.

*/

int main(int argc, char** argv)

{

glutInit(&argc, argv);

glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);

glutInitWindowSize (250, 250);

glutInitWindowPosition (100, 100);

glutCreateWindow ("hello");

init ();

glutDisplayFunc(display);

glutMainLoop();

return 0; /* ISO C requires main to return int. */

}

The header with the 'int argc, char** argv' is a C and C++ thing for command line parameters. It could probably be void, but would leave the program with little to no options if it wanted it to run a certain way.

Here's where we use glut commands. Init glut using the method parameters GLUT_SINGLE and GLUT_RGB. These are parameters: GLUT_SINGLE should set up a single buffer for the window while GLUT_RGB allows Red/Green/Blue colour values in it. GLUT_RGBA is also a possible option and I assume includes alpha transparency too.

glutWindowSize and glutWindowPosition are self explainatory. glutCreateWindow sets the window title bar text. Size and Position on your desktop. init() sets up the internals to freeglut while glutDisplayFunc() specifies the name of the method to call for displaying things using GL syntax. glutMainLoop() is that main loop I mentioned earlier and should loop through, refreshing the display so when you move the window, it doesn't get corrupted. (Don't quote me on that though).

Return statement exists as an ISO standard. Even if the program never gets to the return statement, it's required.

So that's a window example in freeglut, opengl, and c++. Copy all the code segments into a text file, save as "filename".cpp. (change filename to something else if you wish, change it in your compile statement too though.)

To compile it, you must first have installed the freeglut libraries and opengl libraries AND have a compatible video card.

But that's not all, you must also specify the libraries in the compile statment so it will include them.

g++ -Os filename.cpp -lGL -lglut -lX11 -lXext -o ~/filename

First off, "-Os" is an optimization flag. Good options are -O1, -O2, and -Os. -Os optimizes the program for small size. Can be omitted.

-lGL and -lglut are required, the -l means include library and we are including glut and opengl. (it's not -lfreeglut because freeglut is meant as a replacement for glut, an abandonded library, so requires the same import name.)

You can omit the -lX11 and -lXext, at least, it works on my machine. It still works when I include them, so maybe it's just if you write in C.

Lastly, -o means output and the next parameter is where to put it and what the program is called using a path. ~ means home directory for Unix or GNU/Linux users.

I'm fairly new to OpenGL windows, but I hope this helps other new users to start up their development-fu for hardware accelerated applications.

If learning this low-level stuff isn't for you, check out QT and KDE application libraries. They have this stuff built in already.

Thanks for stopping by and look forward to more.

=-=-=-=-=
Powered by Blogilo

No comments: