Rendering Primitives with OpenGL

Rendering Primitives

Rendering Primitives

In this article, I will examine multiple methods for rendering primitives in OpenGL. The first method I will look at is using immediate-mode rendering to render simple primitives in 3D. Another method of rending primitives in OpenGL uses vertex arrays. And finally I will also examine the use of display lists to generate a set of render calls that can be executed at another point in time. The reader is expected to have a basic understanding of programming techniques in C++. If you want to know how you can get started with OpenGL, you can refer to my previous article titled [Introduction to OpenGL].


The first OpenGL specification (1.0) was introduced in 1998 under control of the Khronos Group. At this time, OpenGL utilized a fixed-function pipeline that allowed the graphics programmer to communicate with the low-level graphics hardware by invoking OpenGL functions that were defined in the specification. This did not provide a lot of flexibility to the graphics programmer and it was difficult to achieve exactly the effect that was desired.

In 2004, the OpenGL 2.0 specification was released which added the programmable shader pipeline, giving the graphics programmer the ability to write custom vertex processing programs as well as pixel processing programs. These custom programs could implement different methods to transform vertices and shade pixels that was not previously possible using only the fixed-function pipeline.

In 2009, the OpenGL 3.2 specification introduced an additional programmable shader type called the geometry shader. This shader allows vertices to be introduced into the rendering pipeline on-the-fly to perform operations like adding complexity to spline curves for better approximation of an otherwise inaccurate curve.

In 2010, the OpenGL 4.0 specification added two additional programmable shader stages. The tessellation control shaders operate on input patches from the application and produce per-vertex and per-patch output attributes for a new output patch. The tessellation evaluation shaders are used to compute the position and attributes of each vertex produced by the tessellator. Refer to the ARB_tessellation_shader specification for more information.

In this article, I will only introduce methods that use the fixed-function pipeline to generate 3D images. The programmable shader pipeline will be introduced in later articles.

It is important to keep in mind that some of the techniques used here are deprecated in the latest OpenGL specification but I feel it is important to understand where we have come from in order to know how we got to where we are today and where we will go in the future.

OpenGL States

OpenGL is implemented internally as a state machine. State can be anything from the current vertex color, the current texture that will be applied to a primitive, light and material states, fog, currently bound buffers, or render targets, etcetera. Each state value can be modified, and the current state can also be queried.

Boolean states can be enabled using the glEnable method and disabled using the glDisable method. By default all OpenGL boolean states have the value GL_FALSE (except for GL_DITHER and GL_MULTISAMPLE which have a default value of GL_TRUE). A few of the important states to know about are:

  • GL_DEPTH_TEST: If enabled, do depth comparisons and update the depth buffer. Note that even if the depth buffer exists and the depth mask is non-zero, the depth buffer is not updated if the depth test is disabled.
  • GL_CULL_FACE: If enabled, cull polygons based on their winding in window coordinates. The default winding order of front-facing polygons is counter-clockwise.

There are more important states but they will be discussed in the article about texturing and lighting.

OpenGL state values can be queried using glIsEnabled or the glGet family of functions. For example, to query the currently specified color you would use the following function:

There are also functions to query the state of boolean parameters using glGetBooleanv and in this case the returned parameter will be either GL_FALSE for any value that is 0 and GL_TRUE for any value that is anything other than 0.

To query integer, or double state parameters, you would use the glGetIntegerv, or glGetDoublev methods.


Before we begin with the rendering code, I want to make a formal definition of what a “primitive” is in OpenGL. OpenGL has support for 10 different primitive types.

When we use immediate mode to draw primitives, we always begin our primitive list with a call to glBegin and our primitive list is always terminated with a call to glEnd. The parameter that is passed to glBegin determines the type of primitive we want to draw and it can be one of the following values:

  • GL_POINTS treats each vertex as a point that will be rendered to the screen. Points will always be drawn the same size irrelevant of their distance to the camera determined by the value specified by the glPointSize(GLfloat size) method. The standard size for points is 1.
  • GL_LINES treats each pair of vertices as separate line segments. You must specify at least 2 vertices for a line to be drawn.
  • GL_LINE_STRIP will draw an open connected series of lines where each vertex is connected by a line segment. The last vertex specified will not be automatically connected to the first vertex.
  • GL_LINE_LOOP will draw a closed connected series of lines where each vertex is connected by a line segment. The last vertex will be automatically connected to the first vertex in the list.
  • GL_TRIANGLES treats each triple set of vertices as a single triangle. Individual triangle primitives will not be connected.
  • will draw a connected group of triangles. For each additional vertex after the 3rd, the triangle is closed by adding an edge from the [math]n^{th}[/math] to the [math](n-2)^{th}[/math] vertex.

  • GL_TRIANGLE_FAN is useful for drawing circles with any number of vertices where the first vertex is the central vertex that is used to connect all additional vertices in the list.
  • GL_QUADS is used to draw a set of separated quadrilaterals (4-vertex polygons).
  • GL_QUAD_STRIP is used to draw a set of connected quadrilaterals.
  • GL_POLYGON is used to draw a closed (convex) polygon from any number of vertices.

An example of using each primitive type is shown in the image below.

OpenGL Primitive Types

OpenGL Primitive Types

Vertex Attributes

At a minimum, a primitive must consists of at least one vertex. A vertex defines a point in 2D or 3D space relative to the current reference frame. A vertex can also define a number of attributes that will effect the way the vertex is rendered to the screen. In the fixed-function pipeline a number of functions are defined that affect the different vertex attributes.

A few of the more commonly used attributes are:

  • Color: A 3- or 4-component color that represents the red, green, blue, and in the case of 4-component colors, the alpha channels of the vertex that is to be drawn.
  • Normal: A 3-component vector that represents the surface normal of the vertex that is to be drawn. The normal is necessary to compute correct lighting information for a particular vertex.
  • Texture Coordinate: A 1-, 2-, 3-, or 4-component value that represents the texel that is used to apply the currently bound texture to the vertex that is to be drawn.

There are a few more attributes that can be applied to vertices in the fixed-function pipeline, but these are the most common. In this article I will only apply the color attribute while the normal and texture coordinates will be shown in a later article.

It is important to understand the order in which the attributes must be applied to a vertex.
You must first specify the attributes using the glNormal, glColor, and glTexCoord and then finally define the current vertex using the glVertex family of functions. I will describe and demonstrate these functions in more detail later in the article.

It is also important to understand that once an attribute is set using the glColor, glNormal, or glTexCoord method, that attribute will remain active for all subsequent vertices that are rendered until it is explicitly changed.

Immediate Mode

When rendering in immediate mode you are required to enclose all of the render calls between matching glBegin and glEnd function calls. You can only use a subset of the methods that are available in OpenGL after calling glBegin and before calling glEnd. This subset of functions include setting vertex attributes (glColor, glNormal, glTexCoord, etcetera), setting material properties using glMaterial, rendering display lists with glCallList family of functions, and vertex plotting with glVertex.

An example of rendering a cube using immediate rendering mode is shown below:

We begin the drawing by calling glBegin with GL_QUADS as the primitive type.

We fist specify the color that will be applied to the vertex using glColor3f and specify a 3-component floating-point color value. The first face is rendered with the color green.

Since we are using quads, we need need to specify 4 vertices to define one quad (one face of the cube). We use glVertex3f to define a 3-component vertex with floating-point values.

We repeat this process for each face of the cube, changing the color of each face using glColor3f.

Every call to glBegin must be matched by a single call to glEnd. This tells OpenGL that we are finished drawing the shape and the image is sent to the GPU for rasterization.

We should get something similar to what is shown below.

Immediate Mode Rendering

It should be noted that as of OpenGL 3.0, the immediate rendering mode has been deprecated but will still work to support backwards compatibility with older software. I do not recommend using immediate mode for rendering complex scenes because of it’s extremely poor performance.

Vertex Arrays

Another method of rendering objects in OpenGL is using vertex arrays. Using vertex arrays is more efficient than using immediate mode because you don’t need to call a method to plot every vertex. Vertex arrays allow you to specify a pointer to an array that defines the vertex attributes (color, normal, texture coordinate, etc.) and also a pointer to an array that defines the vertices. You can either specify a different array for every vertex attribute and the vertex positions, or you can interleave the data in the array so that both the vertex attributes and vertex positions are defined in the same array.

Vertex Arrays

Vertex Arrays

The image above shows two ways the vertex arrays can be used. The packed arrays only define a single attribute and the vertex positions in separate arrays. Using interleaved arrays, both the attributes and the vertex positions can be defined in the same array. I will show how to use vertex arrays using interleaved arrays.

Using vertex arrays, OpenGL requires that we give OpenGL a pointer to the array that defines the attributes and vertex positions. Then we enable the client states that are associated with each attribute and vertex positions. Then we tell OpenGL to draw a primitives using these arrays. And finally, we disable the client states which returns OpenGL back to the normal rendering mode.

Client States

OpenGL is originally designed with a client-server model in mind. This was useful because dedicated rendering hardware was very expensive and it was much more economical to purchase just a single rendering machine that several “clients” could access to perform rendering jobs.

For this reason, we have this concept of “client-state” which refers to the data that resides in the client’s environment (system memory). In order to render the client’s data, it would need to be transferred over the network to the rendering server.

In today’s modern age, almost every commercial computer device (PC, laptop, gaming console) has a hardware accelerated graphics processing unit (GPU) available on the same machine that is running the client application, so this terminology is somewhat out-of-date.

Before OpenGL will use a client state it must be enabled using the glEnableClientState method. This method accepts a single parameter that defines the client state that is to be enabled. In this example, I will enable two client-side arrays: GL_COLOR_ARRAY and GL_VERTEX_ARRAY.

With the client state enabled, we must also specify the location of the client-side data as well as it’s type. To do this, we will use the glColorPointer method to assign a pointer to the vertex’s color attribute and the glVertexPointer method to assign the vertex positions.

The glColorPointer and glVertexPointer methods also need to know the type of data that is contained in these arrays.

The glVertexPointer method has the following signature:

And the glColorPointer method has the following signature:


  • GLint size: Specifies the number of components in a single vertex position. This value must be 2, 3, or 4.
  • GLenum type: Specifies the data type of each component in the array. This value can be one of the symbolic constants GL_SHORT, GL_INT, GL_FLOAT, or GL_DOUBLE.
  • GLsizei stride: Specifies the offset in bytes between consecutive vertices. This value can be 0 in which case the array is considered to be packed.
  • const GLvoid *array: Specifies a pointer to the first component of the first vertex in an array.

Example Using Vertex Arrays

To use client-side vertex arrays, we first need to specify the array data:

The vertex defines both the position and color using an interleaved array. We only define the unique vertices of our unit cube but in order to be rendered correctly, we must specify a total of 24 vertices (4 vertices for each face of the cube when rendered using GL_QUADS primitives). To reduce the number of duplicate vertices we will use an index array that defines the indices of each vertex in the vertex array that are needed to render the cube. An index array is simply an array of unsigned integers that define the index of each vertex in the vertex array.

The order of the vertices is important. As an optimization, it is possible to instruct OpenGL not to render polygon faces that are pointing away from the viewer. By default, the winding order of front-facing polygons is counter-clockwise (this can be specified using the glFrontFace method). So in this example, I specify the order of vertices so that each face has a counter-clockwise winding order so only back facing polygons will be culled. I explain the winding order in more detail on the article about particle systems [here]

To render this object we must specify the pointer to the first element in the arrays and render the object using an index buffer.

First, we reset the current model-view matrix by loading an identity matrix with glLoadIdentity(). On line 633, we use the glTranslatef method to move the origin of the world back 6 units in the Z-axis. The glRotatef will rotate origin of the world a certain number of degrees along the specified axis.

On line 636 and 637, we enable the color and vertex position client states. This indicates to OpenGL that will will specify client data to these properties.

The pointers to the vertex arrays are defined on line 639, and 640 using the glColorPointer and glVertexPointer methods described earlier.

Since we also have an index buffer that defines the order in which the vertices defined in the vertex buffer should be drawn, we use the glDrawElements method to draw indexed vertices. If our vertex array defines all 24 vertices in order (including the duplicated ones) then we could use the glDrawArrays instead.

If we are finished with those arrays, we should not forget to disable the client states again so that OpenGL is rendering normally again.

When we run the demo, we should see something similiar to what is shown below:

Vertex Array Rendering

As of OpenGL 3.0, client state vertex arrays are also deprecated in favor of vertex buffer objects. Vertex buffer objects (VBO’s) will be described in the article about OpenGL extensions.

Display Lists

The final method to render objects to the screen I want to discuss is display lists. A display list can be thought of as a list of OpenGL render instructions that can be compiled at run-time and then invoked over and over again very quickly. Display lists come in handy when you want to define a static shape (you cannot modify the vertices in a display list directly) and render that shape many times without having to recompute the vertices.

Before you can use a display list, you must generate a unique display list object ID using the glGenLists method. This method takes as it’s only argument a GLsizei parameter which defines the number of contiguous display lists to generate.

Display lists are deleted using the glDeleteLists method.

To tell OpenGL that we want to define the instructions that are compiled into the display list, we use the glNewList and end the display list definition with the glEndList command.

The glNewList has the following signature:


  • GLuint list: The display list object ID that was previously generated with glGenLists.
  • GLenum mode: Specifies the compilation mode and can be either:
    • GL_COMPILE: Commands are only compiled into the display list.
    • GL_COMPILE_AND_EXECUTE: Commands are executed and compiled into the display list.

Only a subset of OpenGL render commands are compiled into a display list. These commands include setting vertex attributes using glColor, or glNormal family of functions, setting material properties with glMaterial functions, state settings like lighting and texture states, matrix stack manipulation, and the like. However you should be aware that state changes made in a display list are not automatically restored after a display list has finished executing. You should make sure that any changes made in the display list are restored before you end the display list.

Functions that will NOT be compiled into a display list include enabling and disabling client states, querying state values using methods like the glGet family of functions and the glIsEnabled, glIsList, glIsTexture, etcetera.

A previously compiled display list can be executed using the glCallList method. This method takes as it’s only argument the object ID for a display list that was created with glGenLists and compiled with glNewList.

Executing a display list with glCallList is equivalent to invoking all of the compiled commands that were called when the display list was created.

Let’s see an example of creating the cube shape again but this time using display lists.

Generating Display Lists

In this example, I will show how to generate a simple cube display list and render it to the screen.

The CubeDisplayList method will generate a new display list, compile the list, then return the object ID that represents the display list. Most of the details of this function was explained in the section of immediate mode rendering, so there isn’t much new information to tell.

Notice that the display list is generated on line 120 using the glGenLists method specify that only a single display list should be generated.

The display list is compiled on line 121 with the glNewList method. Notice we only specify that the list should be compiled, so none of the methods called while compiling the list will actually be executed.

Then the commands of the display list are invoked.

On line 167, the display list is ended with the glEndList method. This must be called before we can actually use the display list later in the application.

Rendering the Display List

Display lists are rendered using the glCallList method. The only argument to this method is the ID of the display list object to render.

You will notice that this is the simplest form of all the rendering methods we have seen so far. The only thing we have to do is render the list with glCallList on line 746.

Delete the Display List

A display list is an OpenGL context resource. This means that it requires storage space somewhere to define it. Every time you generate a display list, it is generally a good idea to delete it when you are finished with it. The display list is deleted with the glDeleteList method.


In this article I described three different ways to render shapes to the screen using OpenGL. The first method demonstrated is immediate mode rendering. Using immediate mode rendering, each vertex attribute and vertex position requires a function to be called to define it. This is a very inefficient way of rendering but it may be useful for quickly drawing debug information on the screen.

The next rendering method is called vertex arrays. Vertex arrays are more efficient than immediate mode rendering because you define all of the attributes and vertex positions with a single draw call. Using this method, the client data is sent to the GPU every time it is used. This is not ideal if the vertex data never changes. It would be much more efficient to just define the data on the GPU and then render it directly whenever necessary.

The final method investigated in this article is display lists. Display lists allow us to define a pre-compiled set of instructions on the GPU and execute that set of instructions very quickly later on.

Although all of these methods shown in this article have been deprecated since OpenGL 3, it is useful to understand the roots of OpenGL programming because then it is easier to understand where the current OpenGL implementation came from and may also provide an insight into the direction that the OpenGL API is going.

In a later article I will explain how to use vertex buffer objects (VBO’s) to render the geometry in a much more efficient way that doesn’t require copying large vertex arrays the the GPU every time you want to render it.

Video Lecture

The video lecture for this article. This video is best viewed at 1080p can should be easily visible in 720p.


Beginning OpenGL Game Programming - Second Edition (2009)

Benstead, Luke with Astle, D. and Hawkins, K. (2009). Beginning OpenGL Game Programming. 2nd. ed. Boston, MA: Course Technology.
OpenGL 2.1 Reference Pages [online]. (1991-2006) [Accessed 27 January 2012]. Available from:

6 thoughts on “Rendering Primitives with OpenGL

  1. Thanks for the very good tutorial. I’m sort of a newbie when it comes to opengl, and to be honest the different functionality between the different versions are rather confusing. As I understand the fixed function pipeline is deprecated, and it seems to me everything is now shader based. This makes it it difficult to learn opengl as most tutorials is still based on immediate mode.
    So what is the easiest and most effective way of rendering objects today, is it VBOs or VAOs or what? All I understand is that immediate mode (begin..end) and display lists are old and on their way out if not already.

    • Cobus: By the time you posted this, I’ve already uploaded a new article on using VBOs. Using VBO’s is pretty much the standard way of getting things on your screen in OpenGL. Since all other methods of rendering primitives have been depricated, it’s the only way to get geometry to be rendered if you choose to strictly follow the deprication model introduced in OpenGL 3.0.

      Take a look at my new article on VBOs here:

  2. On the “Delete the Display List” part you spell ‘glDeletList’ which should be glDeleteLists, of course.
    Great explanation, haven’t seen the high quality video tutorial yet.

  3. A cool way to generate the positions for the cube yourself is by using this small loop. The index buffer that goes with this loop is included below, the index buffer is counter clock wise.

    float position[3];
    for (int i = 0; i < 8; ++i)
    position[0] = ( ( i & 1 ) ? half : -half );
    position[1] = ( ( i & 2 ) ? half : -half );
    position[2] = ( ( i & 4 ) ? half : -half );

    // do stuff with the position (like add it to the vertex buffer

    unsigned long indices[36] = {1,5,3,3,5,7,

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.