Learn to use glBegin and glVertex commands
For drawing basic OpenGL primitives
OpenGL Primitives. You might be wondering why this tutorial was named 2.5. That's rather a long story. Anyway, this tutorial fits nicely between 2 and 3. It was added before I had written tutorial 3, when I felt that there were details I had not yet mentioned that needed to be made clear.
June 18th, 2014 update: Using glVertex, glBegin and glEnd, the method described in this tutorial, is no longer acceptable way of creating graphics with OpenGL and as of recent years is considered obsolete. However, these methods still work as of this writing and some like to use this mode to get familiar with OpenGL programming. This is called Immediate Mode.
Therefore, these examples here are provided for reference only. They are still sometimes used for teaching the basics of OpenGL. Things you would have to know if you're later planning to understand the "new" way of creating graphics with OpenGL using programmable shaders (In GLSL the GL Shader Language) at this point not discussed in this tutorial. I am planning on adding OpenGL shader tutorials to this site as the next step, just browse around).
You might already know what OpenGL (and 3D graphics in general) primitives are but for those who don't: a primitive "object" can be anything from a 3D point to a line to a triangle to an n-sided polygon. By the way, many of these things I learned from numerous OpenGL books. And I have to say that helped me a lot as far as the understanding of new 3D concepts goes.
Anyway, each primitive has at least one vertex. What is a vertex exactly? Is it one of the points in a polygon or is it a stand-alone point in space? Well it can be both, depending on how you think about it. With points, vertex is just that - the point. A line has only 2 vertices - its starting point and its ending point.
With polygons, there should be more than 2 vertices since polygons are surfaces defined by more or equal to 3 vertices residing on the same plane. A triangle is, for instance, a polygon with 3 vertices. This all should be obvious to us by now! Note that a 3D cube cannot be considered a primitive. Generally, primitives restrict themselves to triangles. A four-sided polygon can generate a quad but that quad will still be made out of 2 polygons. Points and lines can also be considered primitives.
Now that we briefly went over primitives, the next question is how does OpenGL define primitives?. From now on I won't mention the word primitive anymore when I talk about a single primitive. I will name these objects by their respective names they are given (e.g. points, polygons, lines...). The main function (and probably the most used OpenGL function) is function named glVertex. This function defines a point (or a vertex) in your 3D world and it can vary from receiving 2 up to 4 coordinates. Let's take a look at these variations:
glVertex2f(100.0f, 150.0f); defines a point at x = 100, y = 150, z = 0; this function takes only 2 parameters, z is always 0. glVertex2f can be used in special cases and won't be used a lot unless you're working with pseudo-2D sprites or triangles and points that always have to be constrained by the depth coordinate.
glVertex3f(100.0f, 150.0f, -25.0f); defines a point at x = 100, y = 150, z = -25.0f; this function takes 3 parameters, defining a fully 3D point in your world. This function will be used a lot to define any kind of shapes you will possibly want.
glVertex4f(100.0f, 150.0f, -25.0f, 1.0f); this is the same as glVertex3f, the only difference is in the last coordinate that specifies a scaling factor. The scaling factor is set to 1.0f by default.
This won't make a lot of use and I'm not going to explain this function in details. It can be used to make your 3D points look thicker than one pixel. I don't want to sound pathetic but why would you want to use that functionality? (A few boring ideas creep into my mind but you'll just have to trust me with this, we're not going to use this function. If you're really desperate, I recommend searching the net for it in hope that someone out there explained it).
Anyway, glVertex alone won't draw anything on the screen, it merely defines a vertex, usually of a more complex object. To really start displaying something on the screen you will have to use two additional functions. These functions are
glBegin(int mode); and glEnd( void );
glBegin and glEnd delimit the vertices of a primitive or a group of like primitives. What this means is that everytime you want to draw a primitive on the screen you will first have to call glBegin, specifying what kind of primitive it is that you want to draw in the mode parameter of glBegin, and then list all vertices one by one (by sequentially calling glVertex) and finally call glEnd to let OpenGL know that you're done drawing a primitive.
The parameter mode of the function glBegin can be one of the following:
GL_POINTS GL_LINES GL_LINE_STRIP GL_LINE_LOOP GL_TRIANGLES GL_TRIANGLE_STRIP GL_TRIANGLE_FAN GL_QUADS GL_QUAD_STRIP GL_POLYGON
These flags are self-explanatory. As an example I want to show how to draw some primitives and then you will be able to understand how to draw the rest of them.
// this code will draw a point located at [100, 100, -25] glBegin(GL_POINTS); glVertex3f(100.0f, 100.0f, -25.0f); glEnd( ); // next code will draw a line at starting and ending coordinates specified by glVertex3f glBegin(GL_LINES); glVertex3f(100.0f, 100.0f, 0.0f); // origin of the line glVertex3f(200.0f, 140.0f, 5.0f); // ending point of the line glEnd( ); // the following code draws a triangle glBegin(GL_TRIANGLES); glVertex3f(100.0f, 100.0f, 0.0f); glVertex3f(150.0f, 100.0f, 0.0f); glVertex3f(125.0f, 50.0f, 0.0f); glEnd( ); // this code will draw two lines "at a time" to save // the time it takes to call glBegin and glEnd. glBegin(GL_LINES); glVertex3f(100.0f, 100.0f, 0.0f); // origin of the FIRST line glVertex3f(200.0f, 140.0f, 5.0f); // ending point of the FIRST line glVertex3f(120.0f, 170.0f, 10.0f); // origin of the SECOND line glVertex3f(240.0f, 120.0f, 5.0f); // ending point of the SECOND line glEnd( );
As you can see OpenGL makes drawing primitives a very simple task indeed. Take a look at the last code example where I demonstrate drawing 2 lines at a time. This functionality can be used with any primitive as long as you specify the right number of vertices.
For example, to draw two lines in one glBegin-glEnd sequence you need to specify FOUR vertices. Likewise, to draw 2 triangles in one shot, you would call glBegin(GL_TRIANGLES) and specify SIX vertices afterwards. glBegin and glEnd can be expensive calls in tight loops.
If you have an object containing a polygon and, say a line (to indicate its normal?), it would be better to have two loops: first one for drawing all the polygons in the object and second one to draw all the lines in the objects, rather than calling glBegin and glEnd 4 times in one loop each iteration. This is an oversimplified example, don't take my word for it.
I honestly haven't tested whether that would actually be faster (please e-mail me if you know for sure), but I am almost sure that it will be. As you all know, in programming sometimes it can be faster, sometimes it can't be. It all depends on the situation and I don't have the time right now to investigate the effects of glBegin and glEnd versus the for-loops in different cases. Anyway, back to the topic.
glVertex is not constrained to be the only function you can use inside glBegin and glEnd. Here is the full listing of all functions you can use inside glBegin and glEnd (for reference only, these functions will be explained later in following tutorials as we explore OpenGL in more detail):
glVertex glColor glIndex glNormal glTexCoord glEvalCoord glEvalPoint glMaterial glEdgeFlag
If any other OpenGL function is called between glBegin and glEnd, the error flag is set and the function is ignored.
The next tutorial will put to action what I just described here for each glBegin mode. Drawing points and lines is fairly obvious so I'm not going to cover it. I will start with drawing triangles right away since triangles are most important primitives in 3D graphics.
In addition to this tutorial I, again, included a printable version of all functions that can be used inside glBegin and glEnd with brief coverage of each; don't try to memorize what each function does on the list because it's a hard task, since those functions have to be used in action in order to be completely understood. And they will be used in action in future tutorials.