An Introduction to OpenGL 3D Programming Using Basic4GL
14-Nov-2003
Tom Mulgrew
Hello! And welcome to this set of tutorials, which are designed to take some of the mysticism out of 3D programming and OpenGL. If you're new to OpenGL or 3D in general, then these tutorials are intended for you, they assume no prior knowledge or experience with either. You will however need a little bit of programming knowledge to follow the examples (but you don't have to be a guru).
This tutorial uses Basic4GL. This is a BASIC programming language with OpenGL support, designed to make OpenGL programming easy. You can download Basic4GL for free from: http://www.basic4gl.net.
We'll be learning by example, so I encourage you
to keep Basic4GL open and be ready to cut and paste or type in a
program to see what it does.
The best way to learn is to experiment, so feel free to change
the code and see what happens. Basic4GL is designed to be a safe
programming environment, so don't worry, you won't break
anything. The worst that should ever happen is that the program
will stop and say that there's an error.
DISCLAIMER: While every effort has been made to make Basic4GL a safe programming language, computers are complex mechanisms, and it is almost impossible to claim anything with 100% certainty. Therefore I (Tom Mulgrew, the author of Basic4GL) cannot and do not take any responsibility for anything that Basic4GL may or may not do to you, your computer or anything else. The software is strictly to be used at your own risk.
So what exactly is OpenGL? And how does it relate to 3D computer graphics?
OpenGL was created by Silicon Graphics (SGI) as
an Application Programming Interface (API) specification. It
tells you what lines of code need to go into a program in order
to draw 3D graphics images.
For example, it says that the following program:
|
Will draw a coloured triangle.
This is the first example program by the way. You can type it into the main program window of Basic4GL, or just cut and paste it. The run it by pressing the green "Go" button on the toolbar.
Try it.
If all goes to plan, you should now be looking at something like the triangle pictured.
If you've typed in
the example and clicked the "Go" button, and
you don't see a triangle like the picture, then there's a
problem somewhere, and we'll have to figure out what it
is. Try the following:
|
It's important to note that OpenGL is a specification. It says what the computer and graphics system should do when OpenGL commands run, but it doesn't necessarily say how, nor does it have to supply any program code to drive it. Instead this task generally falls to the 3D graphics card makers, who release OpenGL drivers which implement the OpenGL specification for their particular graphics card (or range of graphics cards).
Without an accepted graphics library specification like OpenGL, you would have to write different code for every type of 3D graphics card that you want your program to run on, an impractical amount of work.
OpenGL does realistic modern 3D computer graphics,
with texturemapping, lighting and all the bells and whistles.
It also uses modern 3D graphics hardware, which means it does 3D
graphics fast!
OpenGL has been used for 3D modelling programs like CAD, 3D
visualisation / virtual reality, 3D demos, and perhaps most
importantly, 3D games.
The triangle example was certainly big and colourful, but we didn't really say anything about how it works.
To explain we would have to talk about coordinate
systems, vectors and projection. But we
can't do that until we've explained what each of these are, and
to do that, we need to start with some basic 3D graphics
theory.
With 3D graphics, the idea is to make it look
like there is something behind the computer screen.
That something can be as simple as a small shape, or as complex
as a moving realistic 3D object, (say a car, a dragon or a
samurai warrior).
We try to draw an image on the computer screen that makes it look
as if the screen was a window into that world.
So as a starting point we need to store our imaginary world in the computer in a format that it can work with.
Enter the "vertex".
Here's a program to draw a 3D wireframe cube. It's a little bit long winded (we will learn some better ways to do this later), so you might want to cut and paste it into Basic4GL.
|
All 3D graphics starts with vertices. These
are points in 3D space that are used to specify the corner
"dots" of the 3D object, that we can join together to
make the 3D image. We store each vertex as a 3D vector.
Note: Vector and vertex are often used interchangeably.
A vector is a point in 3D space. It has a very general meaning.
A vertex is a specialised type of vector. It is a point in 3D
space that corresponds to the corner of a 3D object.
A vector is stored as 3 numbers.
The first number we call the "X coordinate", the second
we call the "Y coordinate" and the third the "Z
coordinate".
Each coordinate corresponds to a direction.
In OpenGL the directions are as follows:
Each number tells us how many units to go in that
corresponding direction starting from the start position. (If the
number is negative, it means we go that many units in the
opposite direction).
So if we take the first vector (-1, 1, -5) we can see that it is:
relative to the starting position. The starting position is called the origin, and it corresponds to the viewpoint, (in otherwords; your eyeball).
In our example, the vertices are the corners of our cube, and we tell OpenGL to join them up with lines.
Note: "Vertex" and "Vector" have very
similar meanings. A vertex denotes a point on a 3D model,
presumably intended for drawing. A vector is a more general
purpose.
A vertex is also a vector. A vector can be a vertex, but
it doesn't have to.
Now that we've touched on what vertices are we can start to
understand the OpenGL code.
Let's have a look at it from the start:
dim vertex#(8)(2) vertex#(1) = vec3 (-1, 1, -5) vertex#(2) = vec3 (-1,-1, -5) vertex#(3) = vec3 ( 1,-1, -5) vertex#(4) = vec3 ( 1, 1, -5) vertex#(5) = vec3 (-1, 1, -7) vertex#(6) = vec3 (-1,-1, -7) vertex#(7) = vec3 ( 1,-1, -7) vertex#(8) = vec3 ( 1, 1, -7)
We are creating 8 3D vertices (remember the "(2)" part actually allocates 3 values, index 0, 1, and 2), and setting them up as the corners of a cube. The cube is centred 6 units into the screen.
glBegin (GL_LINE_LOOP)
This is the first OpenGL command. Note
that all OpenGL commands start with "gl", so they're
easy to spot.
glBegin() tells OpenGL that we are about to send it some
vertices, and it tells OpenGL what we want to do with them.
In this case we've used GL_LINE_LOOP which means we want it to
join all the vertices together with lines, then join the last
vertex to the first vertex to create a loop.
glVertex3fv (vertex#(1)) glVertex3fv (vertex#(2)) glVertex3fv (vertex#(3)) glVertex3fv (vertex#(4))
As you've probably guessed, this specifies the vertices.
glVertex3fv is one of a family of commands all starting with
glVertex.
The "3fv" at the end tells us that this version:
3 | Expects 3 coordinates |
f | These coordinates are in "float" format. Basic4GL uses "float" format internally for floating point numbers, so it makes the most sense to pass them in this way. |
v | Vertex will be passed in as an array. Otherwise we would have to pass in the X, Y and Z coordinates separately. (Like we did for the coloured triangle example.) |
The next line:
glEnd ()
tells OpenGL that we have finished sending through this set of vertices, and it can now go off and draw them. This part draws a square, i.e the front part of the cube.
The next glBegin() - glEnd() block is much the same. It draws another square, representing the back of the cube.
The last block starts with glBegin
(GL_LINES). GL_LINES tells OpenGL to join up each pair of
vertices with a line. So vertex 1 gets joined to vertex 2, and
vertex 3 to vertex 4 e.t.c. We use this to draw the lines that
join the front square to the back square, completing the cube.
The last line in the program is:
SwapBuffers ()
Up until this point, OpenGL has been doing all it's drawing in the "Back buffer". This is an off-screen region of memory - you can't see it - where OpenGL does all it's drawing until the image is completed and ready to be displayed. SwapBuffers() transfers the image in the back buffer into the "Front buffer", which is OpenGL's name for the part of the screen where the image is displayed.
This system is called "double buffering", and is useful for creating flicker free animations, because it prevents you from seeing half drawn images.
Okay, that cube was pretty boring, and looked like something from the 1980's. But the principles involved are the same as those that drive modern graphics engines today. It just has a few extra drawing technologies built on top.
To illustrate:
If we start with vertices and lines.
To add more detail, just add more vertices and
lines.
Replace the lines with textured polygons, to make it
look solid.
Then add lighting.
We have ourselves a faily convincing 3D scene.
We'll be covering texturemapping and lighting later on, but for now the important thing to realise is that vertices are the backbone of 3D graphics.