2014-02-13



OpenGL

In this article I will introduce the reader to the OpenGL rendering API (application programming interface). I will also introduce GLSL (OpenGL Shading Language). We will create a simple vertex shader and fragment shader that can be used to render very basic 3D primitives. By the end of this article you will know how to create a simple OpenGL application and render 3D objects using shaders.

Table of Contents

Introduction

OpenGL 1.x Fixed-Function Pipeline

Vertex Data

Primitive Processing

Points

Lines

Triangles

Quads (Deprecated)

Polygon (Deprecated)

Transform and Lighting

Primitive Assembly

Rasterizer

Texture Environment

Color Sum

Fog

Alpha Test

Depth & Stencil

Color Buffer Blend

Dither

OpenGL 2.0 Programmable Pipeline

Vertex Shader

Fragment Shader

OpenGL 3.2 Programmable Pipeline

Geometry Shader

OpenGL 4.0 Programmable Pipeline

Tessellation Control Shader

Tesselator

Tessellation Evaluation Shader

Dependencies

Setting Up the Project

The OpenGL Project

Project Setup

Adding Dependencies

FreeGLUT

GLEW

GLM

Setup Dependencies

Setup FreeGLUT Dependencies

Setup GLEW Dependencies

Setup GLM Dependencies

A Simple OpenGL Shader Program

Global Header File

The Main Source

Shaders

Vertex Shader

Fragment Shader

Initialize OpenGL

Initialize GLEW

Load a Shader

Create a Shader Program

Main

Reshape

Display

Idle

Key Down

Key Up

Special Keys

Mouse Handling

Conclusion and Final Remarks

Download the Source

References

 

Introduction

OpenGL is a rendering API (application programming interface) that provides hardware accelerated (GPU) rendering functions. Unlike other popular graphics API’s (like DirectX), OpenGL is platform agnostic meaning that you can write an OpenGL application on one platform and the same OpenGL program can be compiled and run on another platform.

OpenGL was originally created by a company called Silicon Graphics in 1991 and released in 1992 [2]. OpenGL was released as an open standard and a committee called the OpenGL Architecture Review Board (ARB) was setup to develop and maintain the OpenGL specification. In July 2006, the OpenGL Architecture Review Board transferred control of the OpenGL specification to the Khronos group where it currently resides [2]. Despite the transfer from the OpenGL Architecture Review Board, the ARB acronym is still used today to prefix the name of OpenGL core extensions.

OpenGL 1.x Fixed-Function Pipeline

When OpenGL was first introduced, it featured a fixed-function rendering pipeline. The term “fixed-function” refers to the fact that all of the functions performed by OpenGL were “fixed” and could not be modified except through the manipulation of various rendering states. Using a fixed-function pipeline, the graphics programmer did not have full control over the various aspects of the rendering pipeline.

The following diagram illustrates the various stages of the fixed-function pipeline.



OpenGL Fixed-Function Pipeline

In this diagram, the blue nodes represent the stages of the rendering pipeline that are still used in the current OpenGL rendering pipeline. The orange nodes represent stages of the fixed-function pipeline that have been replaced by different stages in the programmable shader pipeline.

Vertex Data

The first step in the graphics application is sending the vertex data to the GPU for rendering. The vertex data represents the 2D or 3D objects that are to be rendered. In addition to the position of the vertex in 2D or 3D space several other attributes that describe the vertex can also be sent to the GPU. Using the fixed-function pipeline these attributes included vertex color, vertex normals, texture coordinates, and fog coordinate. The vertex data processed by the GPU is often referred to as the vertex stream.

Primitive Processing

In the Primitive Processing stage, the vertex stream is processed per primitive. OpenGL has support for several types of primitives. These include points, lines, triangles, quads, and polygons (but quads and polygons are deprecated as of OpenGL 3.1). In addition to the classic primitive types, the patch primitive is introduced in OpenGL 4.0 to provide support for tessellation shaders.



OpenGL 2.1 Primitive Types

Points

You can specify that OpenGL should treat the vertex data as points using the GL_POINTS OpenGL primitive enumeration. Points are drawn as either squares or circles of various sizes. Points can be used to render particle effects but it is more common to render particle sprites as quads instead of points.

Lines

OpenGL provides several line primitive types [3]:

GL_LINES: Treat 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.

Triangles

OpenGL provides several triangle primitive types [3]:

GL_TRIANGLES: Treat each triple set of vertices as a single triangle. Individual triangle primitives will not be connected.

GL_TRIANGLE_STRIP: Will draw a connected group of triangles. For each additional vertex after the 3rd, the triangle is closed by adding an edge from the to the vertex.

GL_TRIANGLE_FAN: This primitive type 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.

Quads (Deprecated)

Prior to OpenGL 3.1, the quad primitive was provided to draw quadrilaterals. This primitive type was useful for drawing sprites (particle sprites or game sprites). The quad primitive type was depreciated in the OpenGL 3.1 specification and removed in the OpenGL 3.2 specification. The alternative is to specify a quad as a pair of connected triangles.

OpenGL 2.1 provided the following quad types [3]:

GL_QUADS: Used to draw a set of separated quadrilaterals (4-vertex polygons).

GL_QUAD_STRIP: Used to draw a set of connected quadrilaterals.

Polygon (Deprecated)

Similar to the quads primitives types, the polygon primitives types have also been removed from the OpenGL specification as of OpenGL 3.2. Again, polygons should be defined as triangles instead.

To draw a connected (convex) polygon in OpenGL 2.1, you can use the GL_POLYGON primitive type.

Transform and Lighting

The next stage of the OpenGL fixed-function pipeline is the Transformation and Lighting stage (often simply referred to as T&L). In this stage the vertex is transformed into view space by transforming the vertex position and vertex normal by the current model-view matrix (GL_MODELVIEW). After transforming the vertex position and vertex normal, lighting information is computed for each individual vertex. Since this stage happens for each vertex it is not capable of performing per-pixel lighting using the fixed-function pipeline. To achieve per-pixel lighting, you must use a fragment shader (using GLSL for example. This will be described later).

Primitive Assembly

During the Primitive Assembly stage, OpenGL will convert the primitives from the basic primitive types into triangles. For example, if a triangle strip of consisting of N vertices, N-2 triangles will be drawn [3]. Primitives must also be transformed into viewport space and clipped to fit within the viewport boundaries. It is also possible that back-face culling will occur at this stage (if back-face culling is enabled).

Rasterizer

The Rasterizer is responsible for converting the primitives from the primitive assembly into fragments (which eventually become the individual screen pixels). Vertex attributes are interpolated across the face of the vertex for attributes such as color, texture coordinates, and fog coordinates.

Texture Environment

The texture environment will apply any bound textures to the fragments. OpenGL has support for several texture stages that can be applied to a single object. For an example of using multi-texture blending in the fixed-function pipeline, you can refer to my article titled [Multi-textured Terrain in OpenGL].

Color Sum

The Color Sum stage is used to add-in a secondary color to the geometry after the textures have been applied. If the specular highlights are computed in the lighting stage (before textures are applied) the resulting effect simply makes the texture brighter instead of adding-in the specular highlights. This stage allows for the application of a secondary color in the final image [4].

Fog

The fog stage was used to simulate the effect of geometry fading out as-if dimmed by fog. This is useful when rendering outdoor scenes when far away geometry will be clipped away by the far clipping plane (clipping planes are defined by the projection matrix). Instead of seeing the hard-edge of the far clipping plane, the geometry will gradually become less visible in the distance approaching the fog color.

Alpha Test

During the Alpha Test stage, fragments can be discarded if the alpha value is below a certain threshold. This stage only functions if the GL_ALPHA_TEST is enabled and the framebuffer uses a color mode that stores the alpha value (such as RGBA).

Depth & Stencil

During the Depth test stage, a fragment can be discarded if the current framebuffer has a depth buffer, and the depth test stage (GL_DEPTH_TEST) is enabled and it fails the depth comparison function [6]. By default, a fragment will be discarded if its depth value is greater than the depth value of the current fragment (that is, it is behind the current fragment).

The stencil test can be used to discard fragments that fail a stencil comparison operation based on the content of the stencil buffer [7].

Color Buffer Blend

If color blending is enabled (glEnable(GL_BLEND)) then blending will be performed based on the bend function and the blend equations. Fragments that are currently being rendered are referred to as the “source” and fragments that have already been written to the framebuffer are referred to as the “destination”. Blending is performed by combining the colors and alpha values from the source fragment with the destination fragment.

Dither

If you are using a color palette with few colors, you can enable the GL_DITHER state to inform OpenGL to try to simulate a larger color palette by mixing colors in close proximity. This is similar to the effect you get when you are using an indexed image compression algorithm such as Graphics Interchange Format [8] (GIF) which uses a color palette with few colors.

OpenGL 2.0 Programmable Pipeline

The stages of the fixed-function rendering pipeline were described in the previous section. With the release of OpenGL 2.0 in 2004 the ability to programmatically define the vertex transformation and lighting (T&L) and the fragment operations were introduced. The stages colored orange in the fixed-function pipeline are replaced by the vertex program and fragment program. The resulting pipeline now has the following stages.

OpenGL 2.0 Programmable Shader Pipeline

Vertex Shader

The previously defined fixed-function transformation and lighting stage are now replaced by the programmable vertex shader program. This gives the graphics programmer more flexibility regarding how the vertices are transformed (we can even decide not to transform the vertices at all) and we can even perform the lighting computations in the fragment shader to achieve per-pixel lighting (as opposed to per-vertex which was a limitation of the fixed-function pipeline).

However with great power, comes great responsibility. The fixed-function pipeline performs a lot of the operations that beginning graphics programmers will have difficulty with (for example, the vertex transformations are always a difficult concept to master). Throughout this article, we will learn how to use the programmable shader pipeline to define a vertex program that can be used to transform the vertex data into the correct space for rasterization.

The primary responsibility of the vertex shader program is to transform the vertex position into “clip space”. Commonly, this is done by multiplying the vertex position by the model-view-projection matrix (commonly referred to as the MVP matrix). I will discuss this process in detail later in the article.

Fragment Shader

The fragment shader program replaces all of the complicated texture blending, color sum, and fog operations from the fixed-function pipeline. The graphics programmer must create a fragment program that performs these operations (or not if you don’t care about the distance fog for example).

The primary responsibility of the fragment program is to determine the final color (or colors in the case of multiple color targets) of the fragment. The fragment program can be used to compute the per-pixel lighting as well as blend together multiple textures to determine the final fragment color.

OpenGL 3.2 Programmable Pipeline

OpenGL 3.2 (released in 2009) introduced an additional stage to the programmable shader pipeline called the geometry shader [9]. The OpenGL 3.2 pipeline looks like this:

OpenGL 3.2 Programmable Shader Pipeline

Geometry Shader

The geometry shader comes after the vertex shader in the programmable shader pipeline and therefore the output of the vertex shader becomes the input to the geometry shader. The geometry shader can be used to process primitives of one type and generate primitives of another type. For example, you could pass a stream of points (a single vertex) that represents the 3D positions of particles in a particle system and the geometry shader can produce a set of triangles that can be used to render the texture mapped, orientated particles.

OpenGL 4.0 Programmable Pipeline

OpenGL 4.0 (released in 2010) introduced a set of three additional stages to the programmable shader pipeline. These stages come after the Vertex Shader but before the Geometry Shader. These three new stages form the Tesselation stages.

OpenGL 4.0 Programmable Shader Pipeline

The three parts of the tessellation stages are the Tesselation Control Shader (TCS), the Tessellator (tessellation primitive generator), and the Tessellation Evaluation Shader (TES) [10]. From these three stages, the TCS and the TES are programmable. The Tessellator is a fixed-function stage and cannot be programmed.

The tessellation stages do not function on the classic OpenGL primitive types (GL_POINTS, GL_LINES, GL_TRIANGLES, etc.) but instead function on patches (GL_PATCH) primitive type. Patches are simply a stream of vertices that are to be considered a single patch primitive. A patch can consist of 3 vertices in which case the patch is simply a triangle but a patch can also consist of 4 or more vertices (polygon tesselation). The number of vertices that define a single patch in OpenGL is defined by the glPatchParameteri function. This function takes 2 parameters. The first parameter must be GL_PATCH_VERTICES and the second parameter is an integer value that determines the number of vertices in the vertex stream that are to be considered a patch. By default, this value is 3.

Tessellation Control Shader

The Tesselation Control Shader (TCS) is an optional shader stage (it is not required to specify a shader program for this stage). The primary responsibility of the TCS is to determine how much to tessellate the output patch [1].

Tesselator

The Tessellator (or Tessellation primitive generator) is a fixed stage in the rendering pipeline. It consumes the output from the TCS and creates new tessellated primitives. It passes the tessellated coordinates to the TES stage (described next) for further processing [1] [10].

The purpose of the tessellator is to determine how many vertices to generate, in which order to generate them, and what kind of primitives to build out of them [10].

Tessellation Evaluation Shader

The Tessellation Evaluation Shader (TES) is invoked for each tessellation coordinate generated by the tessellation primitive generator (the previous stage). The primary purpose of the TES is to determine the final attributes of each of the tessellated vertices. This is the only required shader stage that must be present for tessellation to occur.

Dependencies

Before we start setting up the project, we need to obtain a few dependencies that we will be using to facilitate the creation of the OpenGL application.

Free OpenGL Utility Toolkit (FreeGLUT 2.8.1): FreeGLUT is an open-source alternative to the OpenGL Utility Toolkit (GLUT) library. The purpose of the GLUT library is to provide window, keyboard, and mouse functionality in a platform-independent way.

OpenGL Extension Wrangler (GLEW 1.10.0): The OpenGL Extension Wrangler library provides accessibility to the OpenGL core and extensions that may not be available in the OpenGL header files and libraries provided by your compiler.

OpenGL Math Library (GLM 0.9.5): The OpenGL math library is written to be consistent with the math primitives provided by the GLSL shading language. This makes writing math functions in C++ very similar to the math functions you will see in GLSL shader code.

These dependencies are all platform agnostic. They should work on Mac, Windows, and Unix/Linux based operating systems.

Setting Up the Project

In the next sections, I will step through the process of creating a project in Visual Studio 2012 that is suitable for creating OpenGL applications. If you are already very familiar with setting up projects and configuring dependencies, feel free to skip to the next section titled A Simple OpenGL Shader Program

In this article, I will only show how to create a minimal shader program that consits of a simple vertex shader and a fragment shader. I will step through the different stages of the programmable shader pipeline to see how we can use OpenGL to push vertices through the rendering pipeline to generate the final rendered image. The steps of the rendering pipeline that we are concerned with are:

Vertex Data: Specify the vertex data that represents the model that we want to render.

Primitive Processing: Specify the primitive type that is used by our vertex data.

Vertex Shader: Create a simple vertex shader that will transform the vertices from object-space to clip-space.

Fragment Shader: Create a simple fragment shader that will determine the final color of the fragment that will appear on screen.

The OpenGL Project

For this tutorial, I will be using Visual Studio 2012 to create a simple OpenGL demo. All of the source code should be platform agnostic so you should be able to follow the tutorial if you are programming for Linux, Mac OS, or any other desktop platform.

Let’s start Visual Studio 2012 and create a new project.

Visual Studio 2012

In the New Project dialog box, select the Win32 Console Application. Specify a name, location, and solution name for your new project.

Visual Studio 2012 – New Project Dialog

In the Win32 Application Wizard dialog box, click Next.

Visual Studio 2012 – Win32 Application Wizard

In the Win32 Application Wizard – Application Settings dialog box, select the Empty project check box and click the Finish button.

Visual Studio 2012 – Win32 Application Wizard (2)

You should now have an empty project that we can start creating our first OpenGL application in.

Visual Studio 2012 – OpenGL Project

Project Setup

Let’s start by adding a new source file to our new project. Right-click the project in the Solution Explorer and select Add > New Item… from the pop-up menu that appears.

From the dialog box that appears, select the C++ File (.cpp) template from the Visual C++ > Code template category.

Specify the name main and put the file in the src folder relative to your project.

Visual Studio 2012 – Add New Item

Before we start coding, let’s setup the project correctly to facilitate good development practices.

In the folder where the project file is located create the following folders:

src: This folder will be used to store the “private” source files for our project.

inc: This folder will be used to store the “public” header files for our project.

bin: This folder will be used to store the compiled executable files.

Open the project properties dialog box and change the Output Directory to bin\ for both the Debug and Release build configurations.

Change the Target Name to $(ProjectName)d for the Debug build configuration and leave it as $(ProjectName) for the Release build configuration. The “d” at the end of the Target Name property will indicate that it is a debug build.

We also need to tell Visual Studio to use the bin folder as the Working Directory when debugging the application.

In the Debugging properties, change the Working Directory to $(OutDir) for both the Debug and Release build configurations.

Visual Studio 2012 – Project Properties (2)

Adding Dependencies

We also want to place the dependencies in a location that can be accessed by several projects in our solution. Create a folder called extern in the same folder as the solution file. We will place all the external dependencies in this folder. Placing external dependencies in a folder with your solution file makes it easy to share and distribute your solution files with others later on.

As stated earlier, we have three dependencies: FreeGLUT, GLEW, and GLM.

FreeGLUT

Download the latest FreeGLUT libraries from http://freeglut.sourceforge.net/. At the time of this writing, the latest version of FreeGLUT was 2.8.1.

Extract the contents of the zip file to the extern folder.

GLEW

Download the latest source zip GLEW libraries from http://glew.sourceforge.net/. At the time of this writing, the latest version of the GLEW library was 1.10.0.

Extract the contents of the zip file to the extern folder.

GLM

Download the latest version of the GLM library from http://glm.g-truc.net/. A the time of this writing, the latest version of the GLM library was 0.9.5.1.

Extract the contents of the zip file to the extern folder and change the name of the glm folder to glm-0.9.5.1 to reflect the version of the library that you are using.

You should now have the following folders in the extern folder:

freeglut-2.8.1

glew-1.10.0

glm-0.9.5.1

Of course the version numbers may vary depending on the versions you downloaded.

Setup Dependencies

We need to update the project settings so that Visual Studio knows where to find the include files and compiled library files.

In the project’s property dialog, add the following paths to the Additional Include Directories property for all build configurations:

inc

..\extern\freeglut-2.8.1\include

..\extern\glew-1.10.0\include

..\extern\glm-0.9.5.1

Visual Studio 2012 – Project Properties (3)

Folder paths should always be specified relative to the project file. You should never use absolute paths (paths that start with a drive letter) and you should not use the $(SolutionDir) property to specify paths. The reason for this is portability. If you want to distribute your source code and project files to someone else then the relative paths will still be correct but absolute paths will not be the same on another computer. It should also be possible to use your projects in multiple solution files. If the solution files are located in different folders then the project-relative paths will still be correct but the $(SolutionDir) variable will be different.

The FreeGLUT and the GLEW library need to be compiled but the GLM library does not require any compilation (GLM is a header-only library).

Setup FreeGLUT Dependencies

FreeGLUT provides Visual Studio 2012 project files so we can add this project to our solution directly.

Add an existing project to your solution and select the FreeGLUT Visual Studio 2012 project file located in the “extern\freeglut-2.8.1\VisualStudio\2012” folder.

Visual Studio 2012 – Solution Explorer

FreeGLUT provides both static and dynamic linking configurations. I always prefer to use static linking for all my projects. If you prefer to use dynamic linking then you must also copy the DLL files to the bin folder where your executable resides. If you use static linking, you do not need to distribute any additional DLL files with your executable.

To make sure FreeGLUT builds the static libraries, open the build Configuration Manager (Build > Configuration Manager… from the main menu).

Set the Active solution configuration to Debug and the Active solution platform to Win32. Make sure the freeglut project is configured to build the Debug_Static configuration.

Visual Studio 2012 – Configuration Manager

Do the same for the Release solution configuration but make sure the configuration for the freeglut project is set to Release_Static.

Visual Studio 2012 – Configuration Manager (2)

Optionally, you can also edit the build configurations and remove configurations that you do not want to see in the Build and Platform drop-down menus in Visual Studio.

Build the FreeGLUT libraries for both the Release and Debug build configurations by right-clicking on the freeglut project in the solution explorer and selecting Build from the pop-up menu that appears.

Visual Studio 2012 – Build Freeglut

When we build the solution, want to make sure that the FreeGLUT binaries are built before our own application is built. To do that we can create a dependency between our project and the FreeGLUT project.

Open the Project Dependencies dialog by selecting Project > Project Dependencies… from the main menu.

In the Project Dependencies dialog box select your project from the Projects drop-down. Check the box next to the freeglut project in the Depends on list.

Visual Studio 2012 – Project Dependencies

One last step is to configure our project to link against the FreeGLUT static libraries.

Open the Project Properties dialog for your project and select the Linker > General properties.

Add the following paths to the Additional Library Directories property:

The library paths may not be present until you have built the freeglut library files for both the Debug and Release configurations.

For the Debug configuration:

..\extern\freeglut-2.8.1\lib\x86\Debug

Visual Studio 2012 – Linker Properties (1)

For the Release configuration:

..\extern\freeglut-2.8.1\lib\x86

Visual Studio 2012 – Linker Properties (2)

For both the Debug and Release configurations, in the Linker > Input property sheet, add the library files to to the Additional Dependencies property:

freeglut_static.lib

Visual Studio 2012 – Linker Properties (3)

Setup GLEW Dependencies

Now we are going to add the dependencies for the GLEW project.

Unlike Freeglut, GLEW does not provide project files directly for Visual Studio 2012. But we can use the supplied project files for Visual Studio 2010 and just change the toolchain to compile with Visual Studio 2012.

Add the glew_static.vcxproj project file from the extern\glew-1.10.0\build\vc10 directory into your solution.

Visual Studio 2012 – Add Glew Project

You may notice that the project gets added with the name glew_static (Visual Studio 2010). This name indicates that this project was intended to be used with the Visual Studio 2010 IDE but we are including it in a Visual Studio 2012 IDE. To fix this, we can simply update the project to use the Visual Studio 2012 tool chain.

Right-click on the glew_static project and select Properties from the pop-up menu that appears.

Make sure you select All Configurations from the Configurations drop-down menu.

In the Configuration Properties > General property tree change the Platform Toolset property to Visual Studio 2012 (v110).

Visual Studio 2012 – GLEW Project Properties

Build the GLEW library binaries by right-clicking on the project and select Build from the pop-up menu that appears.

You may get the following error when you try to build the GLEW project:

To fix this error, open the glew.rc file by double-clicking on the error in the Output window.

You will notice that the string on line 59 of this file is very long. To fix this, simply scroll to somewhere near the middle of the string and add a break by adding a pair of double quotes separated by a space to break the single string value into 2 separate string values (as shown in the highlighted text below).

Glew rc – Fix String Literal Too Long

Save the file and try to build the glew_static project again. It should build now without errors. Make sure you can build both the Debug and Release build configurations.

To ensure that the glew_static project is built before our own project, we can create a dependency between the glew_static project and ours.

Select Project > Project Dependencies… from the main menu.

From the Projects pull-down menu, make sure you select the Tutorial1 project is selected (or whatever name you chose for your own project).

Make sure the check box for both freeglut and glew_static projects are selected.

Visual Studio 2012 – Project Dependencies (2)

Doing this guarantees that Visual Studio will not try to compile our project unless all of it’s dependencies are built first.

In a previous step, we have already added the references to the include files. At this point we only need to tell Visual Studio to link in the library dependencies for the glew_static library.

Open the project properties dialog for the application project (Tutorial1 in my case).

In the Linker > General property sheet, add the paths to the Additional Library Directories property:

Debug:

..\extern\glew-1.10.0\lib\Debug\Win32

Visual Studio 2012 – Linker Properties (4)

Release:

..\extern\glew-1.10.0\lib\Release\Win32

Visual Studio 2012 – Linker Properties (5)

In the Linker > Input property sheet, add the library files to to the Additional Dependencies property:

Debug:

glew32sd.lib

Release:

glew32s.lib

Visual Studio 2012 – Linker Properties (6)

Setup GLM Dependencies

There is very little setup to perform for the GLM math library because it is a header-only library. The only thing you must make sure you do is configure the include paths correctly (which we have already earlier).

One nice feature that GLM provides is visualizers that tell Visual Studio how to display the vector and matrix types in the debugger. To use the visualizers provided by GLM, copy the extern\glm-0.9.5.1\util\glm.natvis file to the Documents\Visual Studio 2012\Visualizers folder in your personal user folder.

Optionally, you can also copy the extern\glm-0.9.5.1\util\usertype.dat file to the C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE folder (if the file already exists, you may want to merge the contents of this file with the one already there). This file tells visual studio which types should be highlighted in the editor.

Enough with setting up dependencies, let’s start writing some code!

A Simple OpenGL Shader Program

For this introduction demo, we’re simply going to make a rotating cube. This will be a very simple example and in the end you should know how to load geometry into OpenGL and render that geometry using a simple vertex and fragment program.

Global Header File

First, we’ll start off by making a global header file that can be included in all of our source files. For this project, we only have 1 or 2 source files but it is generally a good idea to put all of the header files for external projects (like Freeglut and GLUT) and system includes (like STL containers) in a “global” header file. Doing this has several advantages:

You usually only have to include a single header file to get consistent functionality throughout all of your CPP files.

You should not include 3rd party headers and system header files in your local project’s header files. Doing so may cause inconsistent behavior (for example if you need to declare specific macros before the inclusion of 3rd party header files, keeping everything in a single “global” header file will ensure consistency).

Having a single global header file facilitates the use of precompiled headers. See Creating Precompiled Header Files for more information.

You should not include any “local” header files in this include file. Local header files for your project tend to change frequently which defeats the purpose of using precompiled headers (because it will cause the precompiled header to be rebuilt frequently). The main advantage of precompiled headers is that they only have to be prebuilt once and can be quickly included in subsequent builds.

To create the “global” header file, create a new header file in your project called inc\Tutorial1PCH.h for example. The PCH acronym appended to the end of the name implies that this is a precompiled header. Visual Studio will not automatically treat this header file like a precompiled header until you configure your project to use precompiled headers. Configuring the project to use precompiled headers is beyond the scope of this article.

Open the Tutorial1PCH.h header file in the editor and add the following lines:

The first five includes are a few headers from the STL. We’ll be using file streams to read the shader files so we need the fstream header. The ctime header adds functions for reading the system clock. This will be useful for computing the amount of time that has elapsed between frame updates.

The GLEW_STATIC macro definition is required when we are linking against the GLEW static library. The wglew.h is required for access to the wglSwapInterval function. This function is useful for disabling vsync and allows our program to run as fast as it can (instead of waiting for the vertical refresh which is usually 60 Hz. Without disabling vsync, your program would never run faster than the refresh rate of your monitor).

It is useful to note that the wglew.h header file includes OpenGL extensions that are specific to Windows only. If you intend to port this code to another platform it would be necessary to guard the inclusion of this file with the existence of the _WIN32 macro:

Using this technique will ensure this header file is only included when compiling on Windows.

It is also important to note that the GLEW header files must be included before the freeglut headers. GLEW requires that it is included before the OpenGL header files. The FreeGLUT headers will include the OpenGL headers so it is important to always put the GLEW headers before the FreeGLUT headers.

The FREEGLUT_STATIC macro definition is required when linking with the FreeGLUT static library. If you look into the FreeGLUT header file, you may notice that the freeglut_static.lib library file will be automatically linked into our application if we declare the FREEGLUT_STATIC macro before the header file:

This implies that we don’t need to explicitly specify this file in the linker settings but I prefer to be explicit about which files my project is linking against in the project settings. However this statement is a bit contradictory because I don’t explicitly specify the OpenGL libraries which are auto linked:

By the way, you may have noticed that we don’t need to install any dependencies for OpenGL itself. This is because the OpenGL 1.1 core header and libraries are included with your compiler. These headers and libraries are part of the Windows SDK and are automatically included in projects created with Visual Studio. But since the OpenGL header files that come with your compiler only define the OpenGL 1.1 core specification, we need a library like GLEW to access the new functions and features of the OpenGL SDK.

The GLM_FORCE_RADIANS macro definition is required to ensure that all of the GLM functions consistently use radians instead of degrees for all of the math functions. In the past, GLM had a very inconsistent usage of angle units. This inconsistency is addressed by this macro and in later versions of GLM this macro will no longer be required because radians will be the default behavior for the GLM math library. So if you are using GLM 0.9.6.0 or higher (which was not yet available at the time of this writing) you may not need to speicfy the GLM_FORCE_RADIANS macro.

The Main Source

Now that we have our global header file defined, lets get to work on the main application code.

Clearly, the first thing we need is the global header file:

I am also using a simple camera class for this demo. This class simplifies some of the functionality to move and rotate the view and compute the view matrix and projection matrix that are required to render the 3D scene. You can download the files for the camera class here:

Camera.zip

Download and extract the Camera.zip file to your project folder and include the header and source files in your project in Visual Studio. You may need to change the name of the global header file in the Camera.cpp file to match the name of the global include file used in your project.

Include the camera’s header file in your main source:

Next, let’s define some macros that can be used later when setting up our vertex buffers.

I’ll explain the purpose of these macros later when they are used in the code.

Next, we’ll define a few global variables that are used by our application.

int g_iWindowWidth = 800;
int g_iWindowHeight = 600;
int g_iWindowHandle = 0;

int g_W, g_A, g_S, g_D, g_Q, g_E;
bool g_bShift = false;

glm::ivec2 g_MousePos;

glm::quat g_Rotation;

std::clock_t g_PreviousTicks;
std::clock_t g_CurrentTicks;

Camera g_Came

Show more