http://www.linuxjournal.com/content/introduction-opengl-programming
OpenGL is a well-known standard for generating 3-D as well as 2-D graphics that is extremely powerful and has many capabilities. OpenGL is defined and released by the OpenGL Architecture Review Board (ARB).
This article is a gentle introduction to OpenGL that will help you understand drawing using OpenGL.
The latest version of OpenGL at the time of this writing is 4.4, which uses a different technique for drawing from the one presented here. Nevertheless, the purpose of this article is to help you understand OpenGL philosophy, not teach you how to code using the latest OpenGL version. Therefore, the presented source code can be run on Linux machines that have an older OpenGL version installed.
There are many free OpenGL implementations for Linux, but you need only one. I installed FreeGLUT, because it is the most up to date. FreeGLUT is an open-source alternative to the OpenGL Utility Toolkit (GLUT) library:
Finally, you may need to install the mesa-utils package in order to be able to use the glxinfo command:
Figure 2. OpenGL Architecture
OpenGL is a big state machine. Most calls to OpenGL functions modify a global state that you cannot access directly.
The OpenGL Shading Language code that is intended for execution on one of the OpenGL programmable processors is called a Shader. The OpenGL Shading Language has its roots in C (presenting the OpenGL Shading Language is beyond the scope of this article).
OpenGL does not define a windowing layer, because it tries to be platform-neutral and leaves this functionality to the operating system. The OS must provide a rendering context that accepts commands as well as a framebuffer that keeps the results of the drawing commands.
Matrix Algebra is extensively used in 3-D graphics, so it is good for you to know how to add, multiply, subtract and divide matrices, although you will not need to code such operations yourself. It also is useful to become familiar with 3-D coordinates and be able to sketch on paper the 3-D scene you are trying to draw.
On a Debian 7 system, the following command compiled the triangle.cc OpenGL program without any error messages:
The output from the executable will produce the triangle shown in Figure 3. The correct compilation of triangle.cc is proof that your Linux system can be used for developing OpenGL applications.
Figure 3. Drawing a Triangle Using OpenGL
There is more than one way of drawing a triangle using OpenGL, mostly depending on the OpenGL version you are using. Although the presented method is an older way of programming OpenGL applications, I find it straightforward and easy to understand. Remember, whichever method you use, the coordinates of the triangle will be the same.
Note: keep in mind that the most important part of this article is the code!
A cube also has eight vertices. Each triangle requires three different vertices to be defined.
Listing 2 shows the full source code of the cube.cc file.
Figure 4. The Graphical Output of cube.cc
As an exercise, the front face of the cube is made using four triangles. Figure 5 shows the coordinates of the front face of the cube when four triangles are used to define it. The point (0,0,-0.6) has been chosen arbitrarily. You just need a point that belongs to the front face of the cube.
Figure 5. The Coordinates of the Front Face of the Cube
Defining the Vertices:
Figure 6 shows the coordinates of the vertices of the cube for x=0.6, y=0.6 and z=0.6. Note that the vertices that define an edge have two out of their three coordinates exactly the same.
Figure 6. The Vertices of the Cube
As you can see in Figure 6, the coordinates of the four vertices that define the front face and the respective coordinates of the four vertices that define the back face differ only in the value of the z-axis coordinate.
Defining the Triangles:
The triangles are based on vertices. Each triangle needs three vertices to be defined. Two triangles are needed for defining each face of the cube, except for the front face where four triangles are used. The following commands create a color triangle based on the values of x, y and z:
You can change the color of a shape with the
Changing Perspective:
You can change the perspective of the scene with the following commands:
Drawing the Cube:
Drawing the cube is done face by face. Each face needs two triangles to be drawn except for the front face where four triangles are used.
Once you get the coordinates of the triangles correctly, drawing is pretty easy. The drawing of each triangle starts with the
If you are drawing using triangles (
Using the Arrow Keys:
Using the arrow keys is pretty simple. The following code checks for the arrow keys and acts accordingly:
This time, the cube is drawn using rectangles. As the cube has six faces, only six rectangles are needed. Drawing using rectangles is easier and requires less code, but complex OpenGL code runs faster when triangles are used.
Note: every object can be split into triangles, but a triangle cannot be split into anything other than triangles.
Figure 7. The Output of rotateCube.cc
Listing 3 shows the source code for rotateCube.cc.
The crucial thing here is the usage of the
Learning Modern 3D Graphics Programming: http://www.arcsynthesis.org/gltut
OpenGL Superbible, 6th edition, Graham Sellers, Richard S. Wright and Nicholas Haemel, Addison Wesley, ISBN: 0321902947
GLEW: http://glew.sourceforge.net
The Mesa 3D Graphics Library: http://www.mesa3d.org
OpenGL is a well-known standard for generating 3-D as well as 2-D graphics that is extremely powerful and has many capabilities. OpenGL is defined and released by the OpenGL Architecture Review Board (ARB).
This article is a gentle introduction to OpenGL that will help you understand drawing using OpenGL.
The latest version of OpenGL at the time of this writing is 4.4, which uses a different technique for drawing from the one presented here. Nevertheless, the purpose of this article is to help you understand OpenGL philosophy, not teach you how to code using the latest OpenGL version. Therefore, the presented source code can be run on Linux machines that have an older OpenGL version installed.
Installing OpenGL
If you run the following command on a Debian 7 system to find all packages that contain the word "opengl", you'll get plenty of output (Figure 1):
$ apt-cache search opengl
Figure 1. Running apt-cache search opengl
There are many free OpenGL implementations for Linux, but you need only one. I installed FreeGLUT, because it is the most up to date. FreeGLUT is an open-source alternative to the OpenGL Utility Toolkit (GLUT) library:
root@mail:~# apt-get install freeglut3 freeglut3-dev libglew-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following package was automatically installed
and is no longer required:
fonts-liberation
Use 'apt-get autoremove' to remove it.
The following extra packages will be installed:
libgl1-mesa-dev libglu1-mesa-dev libice-dev libpthread-stubs0
libpthread-stubs0-dev libsm-dev libx11-dev libx11-doc
libxau-dev libxcb1-dev libxdmcp-dev libxext-dev libxt-dev
mesa-common-dev x11proto-core-dev x11proto-input-dev
x11proto-kb-dev x11proto-xext-dev xorg-sgml-doctools xtrans-dev
Suggested packages:
libice-doc libsm-doc libxcb-doc libxext-doc libxt-doc
The following NEW packages will be installed:
freeglut3 freeglut3-dev libgl1-mesa-dev libglu1-mesa-dev
libice-dev libpthread-stubs0 libpthread-stubs0-dev libsm-dev
libx11-dev libx11-doc libxau-dev libxcb1-dev libxdmcp-dev
libxext-dev libxt-dev mesa-common-dev x11proto-core-dev
x11proto-input-dev x11proto-kb-dev x11proto-xext-dev
xorg-sgml-doctools xtrans-dev
0 upgraded, 22 newly installed, 0 to remove and 0 not upgraded.
Need to get 7,651 kB of archives.
After this operation, 24.9 MB of additional disk space
will be used.
Do you want to continue [Y/n]?
You also will need a C++ compiler to compile the code.
Finally, you may need to install the mesa-utils package in order to be able to use the glxinfo command:
# apt-get install mesa-utils
The glxinfo command displays useful information about your OpenGL
installation, as you can see in the following output:
...
GLX version: 1.4
GLX extensions:
GLX_ARB_get_proc_address, GLX_ARB_multisample,
GLX_EXT_import_context, GLX_EXT_texture_from_pixmap,
GLX_EXT_visual_info, GLX_EXT_visual_rating,
GLX_MESA_copy_sub_buffer, GLX_MESA_multithread_makecurrent,
GLX_OML_swap_method, GLX_SGIS_multisample, GLX_SGIX_fbconfig,
GLX_SGIX_pbuffer, GLX_SGI_make_current_read
OpenGL vendor string: VMware, Inc.
OpenGL renderer string: Gallium 0.4 on llvmpipe
(LLVM 3.4, 128 bits)
OpenGL version string: 2.1 Mesa 10.1.3
OpenGL shading language version string: 1.30
OpenGL extensions:
...
Mesa is a 3-D graphics library with an API that is so very similar to
OpenGL's, it is pretty much indistinguishable.
OpenGL Pipeline
Figure 2—taken from the OpenGL Shading Language book (aka "The Orange Book")—shows the programmable OpenGL pipeline with the vertex and fragment processors. As you can imagine, the OpenGL pipeline is complex, but you do not have to understand it fully in order to be able to use OpenGL. The Pipeline shows how OpenGL operates in the background. Newer versions of the OpenGL Pipeline are even more complex!Figure 2. OpenGL Architecture
OpenGL is a big state machine. Most calls to OpenGL functions modify a global state that you cannot access directly.
The OpenGL Shading Language code that is intended for execution on one of the OpenGL programmable processors is called a Shader. The OpenGL Shading Language has its roots in C (presenting the OpenGL Shading Language is beyond the scope of this article).
OpenGL does not define a windowing layer, because it tries to be platform-neutral and leaves this functionality to the operating system. The OS must provide a rendering context that accepts commands as well as a framebuffer that keeps the results of the drawing commands.
Matrix Algebra is extensively used in 3-D graphics, so it is good for you to know how to add, multiply, subtract and divide matrices, although you will not need to code such operations yourself. It also is useful to become familiar with 3-D coordinates and be able to sketch on paper the 3-D scene you are trying to draw.
Drawing a Triangle
Now it's time for some real OpenGL code. The code in Listing 1, when executed, draws a triangle on-screen using OpenGL.Listing 1. triangle.cc
// Programmer: Mihalis Tsoukalos
// Date: Wednesday 04 June 2014
//
// A simple OpenGL program that draws a triangle.
#include "GL/freeglut.h"
#include "GL/gl.h"
void drawTriangle()
{
glClearColor(0.4, 0.4, 0.4, 0.4);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 1.0, 1.0);
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
glBegin(GL_TRIANGLES);
glVertex3f(-0.7, 0.7, 0);
glVertex3f(0.7, 0.7, 0);
glVertex3f(0, -1, 0);
glEnd();
glFlush();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE);
glutInitWindowSize(500, 500);
glutInitWindowPosition(100, 100);
glutCreateWindow("OpenGL - Creating a triangle");
glutDisplayFunc(drawTriangle);
glutMainLoop();
return 0;
}
The code in Listing 1 for setting up OpenGL is large, but you will have to
learn it only once.
On a Debian 7 system, the following command compiled the triangle.cc OpenGL program without any error messages:
$ g++ triangle.cc -lglut -o triangle
On an Ubuntu Linux system, the same command produced the following error
messages:
/usr/bin/ld: /tmp/ccnBP5li.o: undefined reference to symbol
↪'glOrtho'
//usr/lib/x86_64-linux-gnu/mesa/libGL.so.1: error adding
//symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
The solution is to compile the triangle.cc program by linking the
executable to an additional library (-lGL):
mtsouk@mtsouk-VirtualBox:/media/sf_OpenGL.LJ/code$ g++
↪triangle.cc -lglut -lGL -o triangle
The libGL.so library accepts OpenGL commands and makes sure that they are
put on the screen in some way. If your graphics card does not have 3-D
acceleration, libGL contains a software renderer that gives
a 2-D image as output to the X Window System. This is the case with
Mesa. libGL also can pass the OpenGL information to the X Window System,
if the GLX extension is present. Then, the X Window System can do
software rendering with the help of Mesa or use hardware acceleration.
The output from the executable will produce the triangle shown in Figure 3. The correct compilation of triangle.cc is proof that your Linux system can be used for developing OpenGL applications.
Figure 3. Drawing a Triangle Using OpenGL
There is more than one way of drawing a triangle using OpenGL, mostly depending on the OpenGL version you are using. Although the presented method is an older way of programming OpenGL applications, I find it straightforward and easy to understand. Remember, whichever method you use, the coordinates of the triangle will be the same.
Note: keep in mind that the most important part of this article is the code!
Drawing a Cube Using OpenGL
Next, let's program an application that draws a cube using OpenGL. You need to construct the cube using triangles. A cube has six faces, and each face needs at least two triangles to be defined. The reason I say "at least" is that, generally speaking, you can use more triangles if you want in order to have a smoother and more accurate shape, but when drawing a cube, that's not necessary. As I'm sure you've realized, you are going to need 12 triangles in total.A cube also has eight vertices. Each triangle requires three different vertices to be defined.
Listing 2 shows the full source code of the cube.cc file.
Listing 2. cube.cc
// Programmer: Mihalis Tsoukalos
// Date: Wednesday 04 June 2014
//
// A simple OpenGL program that draws a colorful cube
// that rotates as you move the arrow keys!
//
// g++ cube.cc -lm -lglut -lGL -lGLU -o cube
#define GL_GLEXT_PROTOTYPES
#ifdef __APPLE__
#include
#else
#include
#endif
#include
Figure 4 shows two different screenshots from the cube.cc application. The
left screenshot of the program just shows a square, because you are
looking straight at the front face of the cube and can't
see any of the other five faces of the cube. Therefore, the shape you
see on screen looks like it is in 2-D. When you start rotating the cube
using the arrow keys, you can tell that it is a 3-D shape.
Figure 4. The Graphical Output of cube.cc
Explaining the Code
Each triangle is defined by three vertices. Each vertex is defined with the help of a single point (x,y,z). Each point comes with three numbers (coordinates), because OpenGL uses a 3-D space. A cube needs eight vertices to be defined.As an exercise, the front face of the cube is made using four triangles. Figure 5 shows the coordinates of the front face of the cube when four triangles are used to define it. The point (0,0,-0.6) has been chosen arbitrarily. You just need a point that belongs to the front face of the cube.
Figure 5. The Coordinates of the Front Face of the Cube
Defining the Vertices:
Figure 6 shows the coordinates of the vertices of the cube for x=0.6, y=0.6 and z=0.6. Note that the vertices that define an edge have two out of their three coordinates exactly the same.
Figure 6. The Vertices of the Cube
As you can see in Figure 6, the coordinates of the four vertices that define the front face and the respective coordinates of the four vertices that define the back face differ only in the value of the z-axis coordinate.
Defining the Triangles:
The triangles are based on vertices. Each triangle needs three vertices to be defined. Two triangles are needed for defining each face of the cube, except for the front face where four triangles are used. The following commands create a color triangle based on the values of x, y and z:
glBegin(GL_TRIANGLES);
glColor3f(0.4, 0.0, 0.4);
glVertex3f(-x, -y, -z);
glVertex3f(-x, -y, z);
glVertex3f(x, -y, z);
glEnd();
Changing Color:
You can change the color of a shape with the
glColor3f(...)
command. The
glColor3f(...)
command takes three parameters, which represent the RGB
values of the desired color.Changing Perspective:
You can change the perspective of the scene with the following commands:
glRotatef(rX, 1.0, 0.0, 0.0);
glRotatef(rY, 0.0, 1.0, 0.0);
As the user presses one of the four arrow keys, the perspective angle
changes accordingly.
Drawing the Cube:
Drawing the cube is done face by face. Each face needs two triangles to be drawn except for the front face where four triangles are used.
Once you get the coordinates of the triangles correctly, drawing is pretty easy. The drawing of each triangle starts with the
glBegin(GL_TRIANGLES)
command and finishes with the glEnd()
command. The
GL_TRIANGLES
is
an OpenGL primitive. Other primitive types are
GL_POINTS
, GL_LINES
,
GL_LINE_STRIP
, GL_LINE_LOOP
,
GL_TRIANGLE_STRIP
,
GL_TRIANGLE_FAN
,
GL_QUADS
, GL_QUAD_STRIP
and
GL_POLYGON
. Eventually, every primitive
shape in OpenGL is represented by one or more triangles.
If you are drawing using triangles (
GL_TRIANGLES
),
the order in which you
put the vertices is not important. If you are drawing using rectangles
(GL_POLYGON
), the four vertices of the rectangle must be drawn in
the right order, although it does not matter if you draw CW or CCW. If
the drawing order is wrong, the rectangle you are trying to draw
will have a big hole in it.
Using the Arrow Keys:
Using the arrow keys is pretty simple. The following code checks for the arrow keys and acts accordingly:
void keyboard(int key, int x, int y)
{
if (key == GLUT_KEY_RIGHT)
{
rY += 15;
}
else if (key == GLUT_KEY_LEFT)
{
rY -= 15;
}
else if (key == GLUT_KEY_DOWN)
{
rX -= 15;
}
else if (key == GLUT_KEY_UP)
{
rX += 15;
}
// Request display update
glutPostRedisplay();
}
The keyboard(...)
function is registered inside the
main(...)
function using the
following line of code:
glutSpecialFunc(keyboard);
Rotating a Cube Automatically
As a bonus, let's look at how to make a cube rotate automatically (Figure 7).This time, the cube is drawn using rectangles. As the cube has six faces, only six rectangles are needed. Drawing using rectangles is easier and requires less code, but complex OpenGL code runs faster when triangles are used.
Note: every object can be split into triangles, but a triangle cannot be split into anything other than triangles.
Figure 7. The Output of rotateCube.cc
Listing 3 shows the source code for rotateCube.cc.
Listing 3. rotateCube.cc
// Programmer: Mihalis Tsoukalos
// Date: Wednesday 04 June 2014
//
// A simple OpenGL program that draws a triangle
// and automatically rotates it.
//
// g++ rotateCube.cc -lm -lglut -lGL -lGLU -o rotateCube
#include
#include
// the GLUT and OpenGL libraries have to be linked correctly
#ifdef __APPLE__
#include
#include
#else
#include
#endif
using namespace std;
// The coordinates for the vertices of the cube
double x = 0.6;
double y = 0.6;
double z = 0.6;
float angle = 0.0;
void drawCube()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Reset transformations
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -5.0);
// Add an ambient light
GLfloat ambientColor[] = {0.2, 0.2, 0.2, 1.0};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);
// Add a positioned light
GLfloat lightColor0[] = {0.5, 0.5, 0.5, 1.0};
GLfloat lightPos0[] = {4.0, 0.0, 8.0, 1.0};
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0);
glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);
glTranslatef(0.5, 1.0, 0.0);
glRotatef(angle, 1.0, 1.0, 1.0);
glRotatef( angle, 1.0, 0.0, 1.0 );
glRotatef( angle, 0.0, 1.0, 1.0 );
glTranslatef(-0.5, -1.0, 0.0);
// Create the 3D cube
// BACK
glBegin(GL_POLYGON);
glColor3f(0.5, 0.3, 0.2);
glVertex3f(x, -y, z);
glVertex3f(x, y, z);
glVertex3f(-x, y, z);
glVertex3f(-x, -y, z);
glEnd();
// FRONT
glBegin(GL_POLYGON);
glColor3f(0.0, 0.5, 0.0);
glVertex3f(-x, y, -z);
glVertex3f(-x, -y, -z);
glVertex3f(x, -y, -z);
glVertex3f(x, y, -z);
glEnd();
// LEFT
glBegin(GL_POLYGON);
glColor3f(0.5, 0.5, 0.5);
glVertex3f(-x, -y, -z);
glVertex3f(-x, -y, z);
glVertex3f(-x, y, z);
glVertex3f(-x, y, -z);
glEnd();
// RIGHT
glBegin(GL_POLYGON);
glColor3f(0.0, 0.0, 0.0);
glVertex3f(x, -y, -z);
glVertex3f(x, -y, z);
glVertex3f(x, y, z);
glVertex3f(x, y, -z);
glEnd();
// TOP
glBegin(GL_POLYGON);
glColor3f(0.6, 0.0, 0.0);
glVertex3f(x, y, z);
glVertex3f(-x, y, z);
glVertex3f(-x, y, -z);
glVertex3f(x, y, -z);
glEnd();
// BOTTOM
glBegin(GL_POLYGON);
glColor3f(0.3, 0.0, 0.3);
glVertex3f(-x, -y, -z);
glVertex3f(-x, -y, z);
glVertex3f(x, -y, z);
glVertex3f(x, -y, -z);
glEnd();
glFlush();
glutSwapBuffers();
}
// Function for increasing the angle variable smoothly,
// keeps it <=360
// It can also be implemented using the modulo operator.
void update(int value)
{
angle += 1.0f;
if (angle > 360)
{
angle -= 360;
}
glutPostRedisplay();
glutTimerFunc(25, update, 0);
}
// Initializes 3D rendering
void initRendering()
{
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
// Set the color of the background
glClearColor(0.7f, 0.8f, 1.0f, 1.0f);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_NORMALIZE);
}
// Called when the window is resized
void handleResize(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (double)w / (double)h, 1.0, 200.0);
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(700, 700);
glutInitWindowPosition(100, 100);
glutCreateWindow("OpenGL - Rotating a Cube");
initRendering();
glutDisplayFunc(drawCube);
glutReshapeFunc(handleResize);
// Add a timer for the update(...) function
glutTimerFunc(25, update, 0);
glutMainLoop();
return 0;
}
Note that the main(...)
functions of triangle.cc, cube.cc and
rotateCube.cc are pretty similar, although the three programs perform
different tasks.
The crucial thing here is the usage of the
glutTimerFunc(...)
function. It
registers a timer callback for the update(...)
function that is
going to be triggered in a specified number of milliseconds. The
update(...)
function changes the angle of the scene
every time it's called.
Conclusion
OpenGL programming is not easy, but this article should help you start writing OpenGL applications quickly. If you want to become proficient in OpenGL, keep practicing writing more OpenGL programs. OpenGL is easy to start with but difficult to master.Acknowledgement
I would like to thank Dr Nikos Platis for sharing a small part of his OpenGL knowledge with me.Resources
OpenGL: http://www.opengl.orgLearning Modern 3D Graphics Programming: http://www.arcsynthesis.org/gltut
OpenGL Superbible, 6th edition, Graham Sellers, Richard S. Wright and Nicholas Haemel, Addison Wesley, ISBN: 0321902947
GLEW: http://glew.sourceforge.net
The Mesa 3D Graphics Library: http://www.mesa3d.org
No comments:
Post a Comment