What is OpenGL?
OpenGL is a set of standard API's for developing 2D and 3D graphics applications. The standard provides it in the form of a library which can be linked along wih your program (could be C/C++). OpenGL includes vast scope of API's which can be used to render figures, textures, shading, etc. The man pages are available along with openGL SDK documentation
Getting Started With OpenGL
As such, there is lot more to discuss when we talk about OpenGL, but to start with a simple program would be a good breather for a novice. Here we'll be writing a program to draw a rectangle to get a hold of basic know-how of OpenGL programming.
The article assumes :
- Platform as Linux
- Native programming language as C
Libraries to install:
Glut libraries i.e. the openGL Utility toolkit needs to be installed which provides a platform independent windowing along with the window event handling for interactive graphics programs.
Use following commands to install openGL and Glut
sudo apt-get install libglu1-mesa-dev freeglut3-dev mesa-common-dev
sudo apt-get install freeglut3
Regarding windowing for openGL rendering, there are other available libraries as well apart from Glut like GLOW Toolkit and SDL. However, we'll be using glut library in this article.
As unusual, OpenGL programming with C also has main() as the starting point. There would be glut calls to initialise/create windows for the openGL to draw onto followed by GL calls to do the actual drawing.
First of all, call
This initiates the glut library and takes the same parameters as main(), reason being it takes a few of main() commandline arguments whatever it understands to initialise the glut library. To mention a few like -display, -geometry, -direct. More information can be found in the man pages.
Next call is,
This requests the future window of the specified width and specified height and at a certain position specified by inputted (x-coord,y-coord) coordinates.
Then we need to call
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE)
There are modes of Glut windows and we set these modes through this call. Here, GLUT_RGB means red-blue-green framebuffer for rendering and GLUT_DOUBLE means to enable use of double buffers. Double buffering is a concept used in drawing graphics to reduce flickering. One buffer is used to display and other is used to compute the next frame, and the technique keeps swapping these buffers.
Please refer the man page for more modes accepted.
Creating a window,
glutCreateWindow("Basic OpenGL Example")
This creates a glut window of title "Basic OpenGL Example. This function accepts an ascii string as the title of the newly created window.
This method sets the global idle callback function. Here, drawRect() is our defined function which draws the rectangle.
Global idle callback function is called during no event-handling. All rendering is done during the idle callbacks as to not to affect the responsiveness of the interactive events.
The next call is an openGL call,
glViewport( 0, 0, window-width, window-height )
This sets the view port i.e. the viewing region specified by left-bottom and top-right coordinates.
We have to set the current matrix mode by
glMatrixMode( GL_PROJECTION )
Most of the graphics computations are done in matrices. And so is the underlying implementation of openGL. Hence, we need to set the matrix stack prior to computive openGL calls. Standard OpenGL supports three major matrix modes:
- GL_PROJECTION - for camera positioning
- GL_MODELVIEW - for various transformations
- GL_TEXTURE - as the name suggests it is used for texturing
Here, because we want to set the camera view perspective (forthcoming call), we are setting the matrix mode to GL_PROJECTION
We can enable the depth testing, which is a part of graphics pipeline, by following call
glEnable( GL_DEPTH_TEST )
Following is the call for setting the projection perspective matrix
gluPerspective( 45, (float)window-width/window-height, .1, 100 )
This call sets the camera view frustum, which specifies a 3D space region in world coordinates which would be visible in the rendered scene.
The frustum consists of a clipping plane nearest to camera eye (called the near clipping plane) and a farthest plane from the camera eye (called far clipping plane).
Please note, any object before to the near clipping plane and farther to the far clipping plane would be clipped.
Here in our call,
- The First parameter is the angle of the camera view frustum = 45 degrees
- The second parameter is the aspect ratio = our window aspect ratio in our case
- The third parameter is the distance of the camera eye from the near clipping plane = 0.1 units
- Fourth parameter is the distance of the camera eye from the far clipping plane = 100 units
Now, its time to start the drawing which would involve transformations. Hence, lets change the matrix mode to GL_MODELVIEW.
glMatrixMode( GL_MODELVIEW )
Remember, we had mentioned to do all drawing in a separate method - drawRect()? We had mentioned this function as aparameter to glutIdleFunc(). So, its time to call a glut method to start the loop where it will do its drawing part whenever its idle from the event handling.
Here it enters the glut event processing loop with no returning. It will render as per the function drawRect(), with high priority to interactive events.
Coming to the drawing part, lets enter the drawRect() method.
The first call would be,
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
This call clears the bits of the buffer screen along with the color buffer and the depth buffer.
It is a good programming practice to clear all enabled buffers before starting to draw or in between frames of animation.
Alongwith, we need to load an identity matrix before transformations which are relative to the current loaded matrix. Hence, if we want the translation/rotation to happen with respect to origin, then we need to load the identity matrix first.
To translate the to-be-drawn figure, we call
Translation is a matrix multiplication internally. This gl translate call translates the to be drawn figure by specified input (x,y,z) parameters. The input parameters are specified in float for this call. It can be inputted as integers as well. Please refer the man page for the various other versions of gltranslate().
The actual drawing is done as:
glColor3ub(255, 0, 0);
glColor3ub(255, 255, 0);
Here openGL needs the programmer to specify what figue to draw and then specify the vertices (x, y coordinates) as per the figure. We specify one vertex through one glVertex() call. We can also set the color for each vertex where openGL would render the color gradient figure as per each vertex set color.
Here we are specifying 2D vertices in float values to draw a two colored rectangle.
Color is set through glColor(), and every vertex is drawn with the previous set glColor() call. The color is mentioned in RGB format i.e. Red, Green, Blue.
We need to explicitly call,
in case of double buffering. Generally its a good practice to call glutSwapBuffers() after drawing each frame in an animation.
Thats it, we are done with openGL programming to draw a colored rectangle :)
The complete Program
Here is how the complete programs looks like:
FileName : myRect.c
int main (int argc, char**argv)
int windowWidth = 640;
int windowHeight = 480;
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutCreateWindow("Basic OpenGL Example");
glViewport( 0, 0, windowWidth, windowHeight );
glMatrixMode( GL_PROJECTION );
glEnable( GL_DEPTH_TEST );
gluPerspective( 45, (float)windowWidth/windowHeight, .1, 100 );
glMatrixMode( GL_MODELVIEW );
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3ub(255, 0, 0);
glColor3ub(255, 255, 0);
Compiling and Running
While compiling, you will need to link to the installed GL and glut libraries.
gcc -Wall myRect.c -o myRect -lglut -lGLU
Now we know how to write a basic drawing program using openGL and most importantly we understand what each line of code means. Since, openGL programming needs the knowldge of its API's, hence the more you know and understand and with the ability to imagine, the more you enhance on your graphics programming skills.
For drawing animations, we can add a varying static parameter , whose value changes after every run. Hence, that would lead to a different frame each time, creating an animation. As an example, in the above program, provide the z-value of gltranslate() call as a static variable in our method drawRect(), whose value is incremented in each run. Wondering, what the animation will look like? Try and enjoy playing :)