OpenGL Game Development Tutorials

OpenGL Book: A OpenGL Tutorial Reference Book
Easily understand OpenGL and learn how to create 3D worlds and games from scratch.

OpenGL Gems is the authorative guide on OpenGL programming published by Learning Curve / Education for software developers! This book in 3D C++ programming series clearly, and in easy to follow language, explains fundamental principles used behind programming 3D games using the OpenGL cross-platform framework.

Preorder Now in PDF format for Kindle and get a discount.
OpenGL Gems is coming out on April 15th, 2017.
The paperback version will be available via this Amazon page, please bookmark it!

Drawing OpenGL Primitives
VBO, VAO, glBegin and glVertex (old Immediate Mode)

How To Draw Primitives in OpenGL
  • Immediate Mode (glBegin, glEnd)
  • Using Display List
  • Vertex Arrays
  • VBO & VAO
  • VBO Example
  • Drawing OpenGL Primitives using glVertex3f command is simple, but has become outdated more than recently.

    And it's time for a tutorial update as well.

    As OpenGL's pipeline evolved to adapt to new graphics hardware that keeps coming out every few years there emerged several ways of drawing primitives on the screen with OpenGL.

    These methods are not entirely different from one another. And an optimal way to reach a thorough understanding of drawing primitives in OpenGL is to go through each method from the very early beginning. This tutorial will take you back to 1990s, when everything was done using glBegin and glEnd commands, referred to as the Immediate Mode. Then we will discover Display Lists, Vertex Arrays and only finally VBO (Vertex Buffer Object).

    Drawing primitives using Immediate Mode is not entirely useless, mostly because the fundamental principles all have remained the same. There are still [x,y,z] vertices for each point in a polygon. There are still texture coordinates and colors.

    The only thing you will not be able to do in Immediate Mode is to use OpenGL Shading Language (GLSL). Note, however, although different methods of rendering, it is possible to mix drawing in Immediate Mode and with GLSL by switching between the two within the same OpenGL program. Having said this, GLSL is a much more powerful rendering technique. And anything you can do in Immediate Mode can be done in GLSL with salt and pepper added.

    This tutorial will explain drawing every possible type of primitive in OpenGL using multiple methods. It may be a little tough to grasp all this right away, but after a few hours of meditation things will settle down.

    When I started learning OpenGL, I was eager to draw my first 3D object, so I jumped into glBegin and glEnd functions. But advanced subjects such as VBO and VAO were something I just didn't want to spend time on. I just wanted to draw graphics.

    However, drawing primitives in OpenGL via VBO has become a requirement as over the years, the OpenGL pipeline changed from outdated methods to new ones. And knowing glBegin and glEnd commands is not enough, as they are quickly becoming outdated and obsolete.

    If you feel a bit lost, please see my OpenGL Tutorial 2.5: Using glBegin and glEnd which should serve as a bridge between the old way of drawing triangles in OpenGL and the new, discussed in this tutorial.

    After that, let's take a look at different ways of drawing primitives in OpenGL. One, called Immediate Mode using glBegin and glEnd, has become outdated. But it still works, and it's Ok for practicing. But probably not a good idea to use in modern software.

    The other is using Display Lists. Note, OpenGL ES does not support Display Lists. But they still work on desktops. First, a list is created, and the 3D data representing vertices is placed into it. Later on, in the rendering loop, the vertex list is called using glCallList command, to render the polygons.

    One way using glBegin and glEnd to draw OpenGL primitives in "Immediate Mode".

    When OpenGL came out, its original rendering pipeline presented a neat, simple way of drawing 3D primitives using commands glBegin and glEnd. In between these commands we would define each vertex of a polygon, one by one.

    // Draw a triangle:
    // Lower left vertex
    glVertex3f(-1.0f, -0.5f, -4.0f);
    // Lower right vertex
    glVertex3f( 1.0f, -0.5f, -4.0f);
    // Upper vertex
    glVertex3f( 0.0f,  0.5f, -4.0f);

    While this simple logic of constructing to create primitives is still in place, the glBegin/glEnd combo has unfortunately become obsolete, and it is now superseded by modern standards that use VBOs (discussed later in this article.) A VBO is a Vertex Buffer Object. This is what you really want to use to draw primitives. But this example is still nice-to know for historical purpose, if any.

    This is called Immediate Mode because soon as we draw a triangle it will be rendered on the screen. As we keep adding more objects to our program, one after another, the objects become more complex.

    Immediate Mode offers a simple way of drawing primitives while learning OpenGL. The primitives can be points, lines, triangles, triangle fans, cubes, rectangles, and polygons with an arbitrary number of vertices.

    Just replace GL_TRIANGLES with GL_POINTS, GL_LINES, GL_QUADS, GL_TRIANGLE_FAN, GL_TRIANGLE_STRIP )there are a few others as well) and list the vertex coordinates in a similar way as shown in this example.

       Another way using Display List commands glGenList, glNewList and glCallList or glCallLists.

    Display Lists can be used to draw OpenGL primitives. Display lists expand on glBegin and glEnd commands. The purpose of display lists was to limit the number of calls to glBegin and glEnd commands, with a hope to improve performance.

    // Generate 1 display list
    GLuint list = glGenLists( 1 );
    glNewList( list, GL_COMPILE );
    // Enter your vertices here
    // Lower left vertex
    glVertex3f(-1.0f, -0.5f, -4.0f);
    // Lower right vertex
    glVertex3f( 1.0f, -0.5f, -4.0f);
    // Upper vertex
    glVertex3f( 0.0f,  0.5f, -4.0f);
    // Then later, elsewhere in your code,
    // Call the display list to render it
    glCallList( list );

    This method is obsolete in favor of VBOs. However, you should still be able to get this to work in your OpenGL program, it's just most graphics developers have moved on to rendering using Vertex Buffer Objects, which seems to be the future.

    In this example, first we create the list using glGenLists function. Then we define the list in between two calls glNewList and glEndList. Whatever vertex commands go in between, will become part of the list. And later, whenever you're ready to render the polygons on the created list, call glCallList.

    Display Lists are not supported by OpenGL ES.

    As of 2014 this code will still work in most OpenGL programs. They have not been completely removed from the API, but it is likely that eventually they will become entirely removed. Nobody really uses them to render graphics anymore and most modern graphics cards support VBOs.

    I provided these examples for reference only. They are still incredibly useful for learning. Although these methods are no longer favored among OpenGL programmers, the principles remain the same even when using VBO. Constructing vertex data is still done using vertex coordinates (x,y,z per vertex), and VBOs can still be thought of as display lists, because we can have multiple VBOs and much like a display list is rendered using glCallList command, a VBO can be rendered using glDrawArrays. We will discover how this is so later in this tutorial.

    Now we understand the obsolete ways of rendering primitives with OpenGL. It's pretty trivial. What's next? Before we dive into VBO, let's first take a look at its distant brother Vertex Array, which shares basic principles. And a perfect subject to go through before VBO.

    Using Vertex Arrays To Draw OpenGL Primitives

    A lot of times in OpenGL, we need to enable certain functionality before we can start using it. In this case, let's enable vertex arrays by issuing the following command:

    // Enable vertex arrays

    Next, let's define a basic data structure to hold the X, Y and Z components of our vector. We will be passing a vertex array object to OpenGL, that expects a set of vertices specified as GLfloat:

    struct vertex
        GLfloat x, y, z;

    Let's create a test vertex array from this structure. The values can be anything whilst we're just figuring out how vertex arrays work. The following will store some kind of a random triangle defined by 3 points.

    // Create array of vertices, consisting of 3 points:
    vertex *vertices = new vertex[3];
    // Assign some values to all 3 points
    vertices[0].x = 10.0f;
    vertices[0].y = 5.0f;
    vertices[0].z = 7.0f;
    // Vertex 2
    vertices[1].x = -10.0f;
    vertices[1].y = 3.0f;
    vertices[1].z = 1.0f;
    // Vertex 3
    vertices[2].x = 5.0f;
    vertices[2].y = -5.0f;
    vertices[2].z = 2.0f;

    Now that we have some values in our vertices, we're ready to define an array of vertex data out of them. This is accomplished using glVertexPoint command:

    glVertexPointer( 3, // number of coordinates per vertex (x,y,z)
        GL_FLOAT,       // they are floats
        sizeof(vertex), // stride
        vertices);      // the array pointer

    We're now ready to draw the vertex array somewhere in our render frame loop:

    // Render primitives from array-based data
    int num_indices = 3;
    glDrawArrays(GL_TRIANGLES, 0, num_indices);

    We are using flag GL_TRIANGLES to let OpenGL know we're drawing triangles. However, other values could have been used such as GL_POINTS, if you needed to render dots. Other flags that can be used are GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_QUAD_STRIP, GL_QUADS, and GL_POLYGON.

    The variable num_indices specifies how many indices to be rendered from the vertex array list created earlier. Because the triangle we created is composed from 3 vertices, and that is the length of resulting array. We're going to use it here.

    Of course you could have said 2 vertices and specified GL_POINTS flag to draw points rather than triangles. This also demonstrates that we can put a cap on the maximum number of vertices that will be rendered, regardless of total number in the existing array list.

    VAO and VBO

    Inevitably on your journey to understand modern OpenGL rendering techniques, you will stumble across two principles. They are VAO or Vertex Array Object and VBO or Vertex Buffer Object. Let's get familiar with them.

    VBO and VAO can cause a little confusion at first. Because they seem like they are used together or for the same or similar purpose. However, they have little to do with one another. First question is what is the difference between the two? This is probably a wrong question to ask, as they perform two unique actions.

    VBO stores actual vertex data. The most important thing about a VBO is not that it stores data, though it is its primary function, but where it is stored. A VBO object resides on GPU, the graphics processing unit. This means it is very fast, it is stored in memory on the graphics card itself. How cool is that? Storing data on the computer processor or RAM is slow mostly because it needs to be transferred to the GPU, and this transfer can be costly.

    VAO represents properties, rather than actual data. But these properties do describe the objects actually stored in the VBO. VAO can be thought of as an advanced memory pointer to objects. Similar to C-language pointers, they do a whole lot more tracking than just the address. They are very sophisticated.

    VAOs are a lot like helpers, rather than actual data storage. That's what they're for. They also keep track of properties to be used in current rendering process. Finally, they describe properties of objects, rather than the raw data of those objects that is by all means already stored in a VBO.

    VAOs are not directly related to VBOs, although it may seem that way at first. VAOs simply save time to enable a certain application state needed to be set. Without VAO, you would have to call a bunch of gl* commands to do the same thing.

    VBO is used to render complex 3D geometry represented as a list stored in GPU memory. It's tough to abandon glBegin and glEnd methods in favor of VBO. But we are gaining so much more in terms of performance. And if we want to achieve a greater amount of realism we will use VBOs together with GLSL shaders. But that's a subject of another tutorial.

    In the following examples we will discover how VBOs are used in an OpenGL program to actually draw a display list consisting of a set of arbitrary vertices.

    An Example of Using VBO or Vertex Buffer Objects To Draw OpenGL Primitives

    OpenGL VBO rely on the extension GL_ARB_vertex_buffer_object and your graphics card must support it in order to use it. This extension enables us to increase performance of rendering and updating our display list.

    In some instances, one advantage of VBO gives us is drawing of static objects from data that was created only once. We don't have to create the data each frame, it will reside in GPU memory. In other words, if the data is static, you can create the VBO only once, then reuse it. This can dramatically speed up rendering performance. We will see how this works elsewhere.

    For now, before we write any code at all, let's take a look at functions used for creating, drawing and updating VBOs. First, we'll take a look at creating a VBO. This process takes three steps, using the following functions:

    • glGenBuffersARB(GLsizei m, GLuint *ids) is used to create the buffer objects. It returns ids of the created buffers as a list of GLuints. (A list of integers.) These ids (or a single id) can now be used in future VBO calls to refer to the list.
    • glBindBufferARB(GLenum target, GLuint id) is used to bind the buffer to the id. Once the biffer object is created, we simply need to associate it with the id. Here, target is a hint for choosing the data type between an "index element array" GL_ELEMENT_ARRAY_BUFFER_ARB or a "vertex array" GL_ARRAY_BUFFER_ARB. In this example we will use vertex array.
    • glBufferDataARB(GLenum target, GLsizei size, const void* data, GLenum usage) to finally copy the data into the object. The last parameter usage identifies one of the values shown in the table below:

    glBufferDataARB usage flags:


    I'm still working on more details on how to use this, but I am coming close. For now, just use the functions above to construct your own function and name it CreateVBO(GLfloat *vertices, int size), it will be used in the following example. Inside this function, call the VBO functions above to go through the process of creating and binding the list to the object. This function should return a numeric id.

    Drawing Primitives Stored in VBO Objects

    We are now ready to use VBO or Vertex Buffer Objects to draw some primitive shapes. First, let's create our vertex list and store it in an array as follows:

    GLfloat vertices[] = {
        0,0,0,   // v1
        0,0,0,   // v2
        0,0,0,   // v3
        0,0,0,   // v4
        0,0,0,   // v5
        0,0,0    // v6

    We have here 18 coordinates, just enough to draw 2 triangles. You can change the 0's to actual vertex positions. This is just an example. We've just prepared our first vertex list! This list can contain hundreds, thousands and sometimes millions of actual vertex coordinates, depending on the size of the object you are drawing.

    Now we are going to create a VBO object, and populate it with this vertex array we just created:

    int size = 18 * sizeof(GLfloat);
    int m_vboId = CreateVBO(&vertices[0], size);

    Now our VBO object is created. Let's draw the contents on the screen by passing it to OpenGL for processing. This is done first, by "binding" the VBO object to GL_ARRAY_BUFFER_ARB flag, which means it is being prepared for rendering as an array of vertices.

        // Bind VBO for drawing array data
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_vboId);
        // Activate array-based data
        // Stride of 3, floats
        glVertexPointer(3, GL_FLOAT, 0, 0);
        // Draw triangles
        glDrawArrays(GL_TRIANGLES, 0, 18);
        // Switch off vertex array data
        // Bind with 0 to switch back to default pointer operation
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, NO_VBO_ID);

    We then enable GL_VERTEX_ARRAY mode, just like in the previous example where we used a vertex array for the first time. Now, it's time to specify vertex properties. We will pass 3 coordinates per vertex, which is the stride between each set of vertices to draw triangles to glVertexPointer and we make sure our vertices are of GL_FLOAT data type.

    Finally, we are going to render the vertices using the function glDrawArrays and pass GL_TRIANGLES flag to it. Beside some de-initialization code, shown in the source code listing above toward the end, that's pretty much it. You've just rendered two triangles with OpenGL using a VBO object.

    Using VBO doesn't guarantee faster rendering compared to obsolete methods in some (but not all) cases. However, this is what is not used in modern OpenGL graphics, and there is no reason not to use VBOs. They are the new standard for rendering OpenGL primitives.

    Essentially, glDrawArrays performs the same set of logic to render GL_TRIANGLES, or some other type of primitive as obsolete methods. Not much really has changed in terms of what we are doing. What's changed is the how the data is passed to OpenGL. When using VBO to render primitives the drivers and the video card (GPU) will take care of the rest.

    For this reason, it is really still somewhat important to know how old, now obsolete, ways of drawing worked. It is not necessary to know them anymore, but taking a look at them gives us some perspective on the basic operations of OpenGL from a much simplified vantage point.

    VBOs are just a more advanced way of doing the same thing.

    Examples of Drawing OpenGL Primitives The Old Way

    Below I will provide a discussion on drawing primitives using the old glBegin and glEnd commands. It is here for reference only to provide a sufficient discussion of how it was used to be done in the old days. I just enjoy writing and anything that can give us a deeper understanding of OpenGL evolution is a good thing. But if you are curious about VBOs (and I don't blame you,) you can probably move on.

    Before we move on I need to mention one important point. If you're making a jump from 2D graphics to 3D like many people do, you need to completely forget about the 2D coordinate system. It's time to think in 3D, which can be a little frustrating in the beginning. To refresh your memory here is the coordinate system that OpenGL uses which is the 3D Cartesian Coordinate System.

    3D Cartesian Coordinate System

    Remember that when OpenGL is initialized and the projection is set up we are initially looking down the negative Z space as seen on the image above. The point x=0, y=0, z=0 is in the exact center of the screen. Keep this image in your head when you write primitive-rendering code. Before we continue, I'd like to point out that I have learned much of this from OpenGL books, available at other sites like Amazon and/or Barnes and Nobles.

    Open up my OpenGL base code and bring up glmain.cpp into the main view. That's where the function RenderFrame is located. This function executes once per frame or a single run through the main execution loop. Here you will usually want to draw everything and process the rest of your program. Since we are only practicing to draw primitives, you will only use this function to merely display some simple stuff.

    Initially, the base code has a single line in that function referring to clearing the contents of the video and depth buffers. This call should generally be there before you add any drawing code since it clears the back (video) buffer as well as the depth buffer; just think of it as clearing the screen to start drawing "from scratch". The reason you clear the depth buffer is that there might be some garbage from the previous frame and you wouldn't want it there.

    The video buffer clear color is specified by the function glClearColor which is called during initialization. Its parameters specify the R G B and A (alpha) values of the clearing color. How they are specified is the topic of the next tutorial entitled "Colors" and you shouldn't be bothered by it at this point. Just think about it as when you pass all parameters as 0.0f, the clear color is black.

    Also note that by default, all primitives' color is white even if you don't specify their color (which we won't for now, and which is also one of the topics of the next tutorial where I explain how to add some color to your primitives). In this tutorial we will be constrained to drawing plain white primitives.

    Well, it's time to draw something on the screen. You're about to draw your first polygon! Type the following lines inside the RenderFrame function after the call to glClear.

    void RenderFrame (void)
            glVertex3f(-1.0f, -0.5f, -4.0f);    // lower left vertex
            glVertex3f( 1.0f, -0.5f, -4.0f);    // lower right vertex
            glVertex3f( 0.0f,  0.5f, -4.0f);    // upper vertex

    Compile and run the program. You should see a white polygon centered on the screen with given coordinates. Note that the z value is always -4.0f, this means the triangle is drawn a little further into the screen, otherwise it would be too close to the camera. This glBegin-glEnd sequence can be used to create any other type of primitives. In this example, if you change the GL_ TRIANGLES flag to, say, GL_POINT you will see 3 points positioned at the ending points of the triangle. Try it. If you specify the GL_TRIANGLES flag and list 4 vertices instead of required 3, the last vertex will be ignored. However, if you specify 6 vertices, TWO triangles will be rendered. Here's an example of drawing two triangles. Add 3 more vertices to the list you already have as in the following example:

    glVertex3f(-1.0f, -0.5f, -4.0f);    // lower left vertex
    glVertex3f( 1.0f, -0.5f, -4.0f);    // lower right vertex
    glVertex3f( 0.0f,  0.5f, -4.0f);    // upper vertex
    glVertex3f(-0.5f, -1.5f, -4.0f);
    glVertex3f( 0.5f, -1.5f, -4.0f);
    glVertex3f( 0.0f, -1.0f, -4.0f);

    Recompile this code and run it. One more, smaller triangle appears at the bottom of the first one. As you can see this way you can save time on glBegin and glEnd calls and draw multiple triangles using only one glBegin and one glEnd calls. This is true for all primitives. Say you want to draw 3 lines, then you will specify GL_LINES as the parameter of glBegin and specify 6 vertices one by one. Each 2 vertices in a consequent order will specify the starting and ending points of the 3 lines. Same goes for the rest of primitives specified by following flags: GL_POINTS, GL_LINES, GL_TRIANGLES, GL_QUADS, GL_POLYGON. Just apply the same rules when using glBegin with these flags to draw points, lines, triangles, quads and n-sided polygons.

    Line, Triangle and Quad Strips

    As you can imagine a call to glVertex takes some time to process. It is possible to minimize the number of calls to glVertex while drawing the same amount of primitives be it a line or a triangle or a quad. For this purpose there are 3 additional flags which are


    These types of rendering can greatly increase performance of large models! I will demonstrate only one type of strips in theory - GL_TRIANGLE_STRIP. The same rules will apply to the other two types of strips, only with a different number of vertices. Let's take a look at this image generated by following code.

    Drawing with Triangle Strips in OpenGL.

    And here's the according code to generate this image:

    glVertex3f(-1.0f, -0.5f, -4.0f);    // A
    glVertex3f( 1.0f, -0.5f, -4.0f);    // B
    glVertex3f( 0.0f,  0.5f, -4.0f);    // C
    glVertex3f(1.5f,  0.0f, -4.0f);     // D
    glVertex3f(2.0f, -1.5f, -4.0f);     // E

    Compare coordinates of this code with the corresponding image above; I named vertices with letters A through E to make it easier to compare the code with the visual output and also connected them with red lines so you can see the triangles.

    You see, when you will get to the point of creating 3D objects out of triangles, you will realize that many triangles share the same vertices. These points would normally be listed as separate vertices, with the same coordinates. But why call glVertex twice? This is the whole point of primitive strips.

    To initialize a triangle strip you will need to at least specify one initial triangle. In the example above, this triangle is ABC. From then on, as you add new vertices (D and E), you add a new triangle to the "strip" with only ONE vertex. And this is the idea behind triangle strips.

    This technique can also be applied to both line strips and quad strips. Using this technique can improve the speed of your 3D model rendering loops. With only 5 calls to glVertex you can make 3 triangles.

    Line Loops

    Another unique type of rendering is called a line loop. You cannot create triangle loops or quad loops. This will only work with lines. The flag you will use to generate it is GL_LINE_LOOP. A line loop is the same thing as a line strip. The only difference is that the last and the first vertices specified in the list are also connected. <> FANS

    The next and the last type of rendering triangles are fans. Fans also can only be used with triangles and no other type of primitive and are identified by the GL_TRIANGLE_FAN flag. A triangle fan has one origin indicated by the first vertex, and an initial triangle, indicated by the first 3 vertices including the origin.

    As you add the 4th and 5th vertices to you fan, you continue it just like you did with a triangle strip. The difference is that all consequent triangles are tied to one origin point and do not extend into a line (or a strip) of triangles. Here is an example of a triangle fan and code to generate it.

    Drawing with Triangle Fan in OpenGL.

    glVertex3f(-1.0f, -0.5f, -4.0f);    // A
    glVertex3f( 1.0f, -0.5f, -4.0f);    // B
    glVertex3f( 0.0f,  0.5f, -4.0f);    // C
    glVertex3f(-1.5f,  0.0f, -4.0f);    // D
    glVertex3f(-1.8f, -1.0f, -4.0f);    // E
    glVertex3f( 0.2f, -1.5f, -4.0f);    // F
    glVertex3f( 1.0f, -0.5f, -4.0f);    // G

    A triangle fan doesn't have to be closed as in this example, just remember that all triangles in a triangle fan share the same vertex which is the origin.

    As you see here, with only 7 calls to glVertex it was possible to create FIVE triangles as opposed to 6 vertices per 2 triangles when using GL_TRIANGLES flag. The limitation of this is of course that you cannot have a whole model made out of ONE triangle fan because it only has one origin!

    Well it looks like our tutorial is just about over. Thanks for getting this far!

    To use this type of rendering you need to parse the model data in a special way. Well this seems to cover all types of rendering primitives. I didn't demonstrate drawing quads (GL_QUADS) but it is trivial to understand; as I said before, just follow the same rules. There is no source code for this tutorial, just remember all possible flags and practice, practice, practice.

    OpenGL Book: A OpenGL Tutorial Reference Book
    OpenGL Book - A OpenGL Tutorial Reference Book

    If tutorials on this site are not enough, or you simply like reading from a physical book or a digital device (Kindle, iPad, tablets, etc.) check out OpenGL Book. Written by the author of tutorials on this site.

    This book is a OpenGL tutorial and a reference that guides the reader through the process of setting up and initializing OpenGL, drawing 3D primitives and creating 3D computer games.

    Preorder Here
    © 2017 Copyright OpenGL Tutorials (

    All content and graphics on this website are the property of - please provide a back link when referencing on other sites.