Tutorial 1 (Creating an OpenGL Context)

Outline

Many OpenGL tutorials start with something like “First install GLFW”, or “To start you will need a windowing library such as SDL”, or if you’re really unlucky, “Start by downloading Freeglut”. None of these are necessary (although if you insist on using one I recommend SFML which provides lots of nice features such as image/audio loading and has a very shallow learning curve). The one advantage these libraries offer you is that they tend to be extremely portable and will run across most platforms.
I suspect that if you are reading a tutorial on getting started with OpenGL then you’re probably some way from worrying about cross-platform compatibility, and the benefits of doing it all yourself can arguably outweigh the costs.

Benefits

No external dependencies

This makes it easier to share your code with others and involves less faffing about setting up the project.

You have a better understanding of how everything in your project works

This is generally a ‘Good Thing’.

Most off these libraries do more than just provide an OpenGL context, they provide all kinds of things like image loading, input handling, font loading, vector/matrix classes and much more.

This is all useful stuff, but the more of it you choose to use the more it will influence the style and architecture of your own applications. This in itself isn’t a bad thing, however being aware of how this stuff works helps you make better decisions if you do decide to go for a third party solution.

These libraries themselves have API’s that you will need to learn. For most of them it is possible to create a window with an OpenGL context with just a couple of lines of code, and if that’s all you want then that would be OK, but the other features (particularly input handling which is generally closed tied to the window), are always tempting to use and it can be quicker to just role your own and only implement what you actually need.

Costs

It takes time to write your own windowing code and attach an OpenGL context.

This is especially true if you want it to be robust.

Win32 is awful.

Really, it’s just awful.

If you want to target another problem, you will have to do this all again.

In order to create cross-platform libraries, you do at some point need to write platform specific code. Some people will say you don’t need to know Win32 as you should only write cross platform code. If you want to do anything graphical then at some point platform specific code will be required. It may be hidden from you inside a 3rd party library such as SDL or SFML, but they do have various sections of code that are just pre-processor conditionals checking for platform type. It is nice to have at least a basic understanding of how this stuff works.

Method

To start, create a new Visual Studio project (if you are using a different tool chain then do the equivalent), call it whatever you like, but for this tutorial I’m going to call mine Tutorial1. (Personally I go for a console application with pre-compiled header, but you can go without the pre-compiled header if you like, I mainly just use them out of habit as most projects I work on actually benefit from them).
In your new project you will see a number of files have been generated:

stdafx.h

targetver.h

YourProjectName.cpp (For my project that will be Tutorial1.cpp)

stdafx.cpp

stdafx.h and stdafx.cpp make up your pre-compiled header, targetver.h can be safely ignored as well, the block comment it contains provides all the explanation that’s really needed.

You may see both Win32 and x64 configurations have been created,
these tutorials assume that they run under the x64 configuration,
I generally just delete the Win32 one as soon as I remember.

Tutorial1.cpp defines a main function in the Microsoft way:

int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}

Delete this and replace it with the more traditional:

int main( int argc, const char* argv[] )
{
return 0;
}

NOTE: This tutorial was originally created using VS 2013. I now use VS 2015 and the generated main looks like:

int main()
{
return 0;
}
>```
as we don’t actually use the input parameters for the main method this is fine as it is.
We will also need a couple of windows header files as well as maing sure we link against the opengl library, add the following after #include "stdafx.h":
```c++
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
#include <windowsx.h>
#pragma comment(lib,"opengl32.lib")

I like to have my main function in a file called main.cpp so I’m going to rename that as well.

As much as possible we will try and lessen the awfulness of Win32 by avoiding its endless macros and defines (macros are always a ‘Bad Thing’).
You don’t even really need the parameters unless you plan on passing in command line parameters to your application (and this tutorial won’t cover any off that) but it is nice to at least see the “Standard” entry point definition.

The first thing that has to be done is we need to create a window. I’m not going to go into too much detail about this, there’s loads of stuff about windows and how to create them on the internet already,
however there are a couple of pitfalls to be careful off.

To start with we will just do everything inside main().

The first thing you need is a Window Handle (HWND):

HWND window_handle;

An HWND is initialised by calling:
CreateWindow(...);
Unfortunately, as with all things windows, it’s not that straightforward. In order to create the window you will need a HINSTANCE and a WNDCLASSEX.
The HINSTANCE can be created by calling:

HINSTANCE instance = GetModuleHandle(nullptr);

The HINSTANCE is a handle to an instance. That is to say the base address of the module in memory. For more info on HINSTANCE click here:
The WNDCLASSEX is slightly more complex. It’s a struct contains information about your window and contains the following fields:

struct WNDCLASSEX {
UINT cbSize; // This is always this sizeof(WNDCLASSEX)
UINT style; // For an OpenGL window this must be CS_OWNDC
WNDPROC lpfnWndProc; // Function pointer to a function that deals with windows messages
int cbClsExtra; // We don’t need this.
int cbWndExtra; // We don’t need this.
HINSTANCE hInstance; // The HINSTANCE of this module
HICON hIcon; // The icon that identifies the app. Set it to NULL for default
HCURSOR hCursor; // The default cursor to be used when your window has focus
HBRUSH hbrBackground; // The default background of the window
LPCTSTR lpszMenuName; // The main application menu for the window
LPCTSTR lpszClassName; // An identifier for this WNDCLASSEX (This has nothing to do with C++ classes)
HICON hIconSm; // The small icon that identifies the app. Set it to NULL for default
}

For more details on what these all mean see MSDN:
We create a WNDCLASSEX as follows:

window_handle = CreateWindow(
L"tutorial_1_window", // The class we created above
L"Tutorial", // The title of the window
WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, // The style of the window
200, // The x position of the top left
200, // The y position of the window
600, // The width of the window
400, // The height of the window
NULL, // The parent of the window
NULL, // The menu for the window
instance, // The module instance
NULL // Not sure don't worry about it
);

Now the window has been created we just need to tell it to show and to update so:

ShowWindow(window_handle, SW_SHOW);
UpdateWindow(window_handle);

And finally we need to deal with windows messages, otherwise it becomes unresponsive. This is pretty much boilerplate. We need to check for the message_return value otherwise the application won’t exit properly.

So now we have a window, but we still don’t have an OpenGL context.
The most basic OpenGL context is simple enough to do. We just need a PIXELFORMATDESCRIPTOR , an HDC (Device Context) and an HGLRC (OpenGLContext).
(Note: All the following code should be added before the call to ShowWindow)
We first need to create a PIXELFORMATDESCRIPTOR object, the PIXELFORMATDESCRIPTOR is a struct that has the following form.

Once we have an OpenGL context we have to set it as the current context:

wglMakeCurrent(device_context, gl_context);

Also in the spirit of encouraging good coding practice we should delete the context before we exit (just before we destroy the window).

wglDeleteContext(gl_context);

And that’s it we have an OpenGL context.
But Wait…
What we actually have is a pretty crappy OpenGL context.
You may have noticed that all the functions dealing with OpenGL creation started with wgl, that’s because WGL is the windows API for interacting with OpenGL, on other OS’s you may use GLX, which provides similar functions.
The base API is pretty basic and you can’t really do much, (I’ve never even tried to use it), so what we want to do is enable extensions, that way we can create more useful contexts. The first problem is that the call to enable extensions requires a context to be created. So what we do is create a simple context, enable the extensions, destroy the simple context, and then create a new and improved one.
The best thing is we have just created a simple context so what do we need next? We need to enable extensions.
There are loads of extensions, and it is possible to load them manually but generally it is better to use an extensions library. The most common one is GLEW, which is available as source or binary, linking to it can be a bit of a pain, so I find it easiest to just get the source and it directly to my own project.
Just download the source, extract it and copy the glew.h and wglew.h files from the include\GL folder and glew.c from the src folder into a folder called GL beside your own source files.

You then need to add the files to your project:

And then add the path to the GLEW headers to your project properties.

I recommend using the built in Macros for Visual Studio paths wherever possible. It really does simplify things.
Finally we need to turn pre-compiled headers off for glew.c.

It might be best just to build the project and run just now to make sure it still works.
As long as everything still builds and we still get an empty window we can start to proceed with our improved context.
To do this first need to add three includes at the top of the file (after the existing #includes):

We need to enable glewExperimental due to an ongoing bug in GLEW, then we call glewInit() , it seems fairly obvious that initializes GLEW. Then we destroy the simple context that we created. Before we destroy it though we have to make it not the current context. To do this just call wglMakeCurrent(nullptr, nullptr); (This isn’t strictly necessary as wglDeleteContext(gl_context) should make the context non-current before deleting it. But this makes it more obvious to the developer that this is happening).
If we run the app now we should see something like (The version numbers will depend on your system):

Hopefully your supported OpenGL version is above 4.x.x or else some of the other tutorials I’m planning on writing might not work. In fact if your supported version is less than 3.1 then even this tutorial probably won’t work.
We’ve enabled GLEW so now we can create our fancy context.
The first thing we have to do is get a Pixel Format. This is similar to when we got a pixel format for the simple context, but this time we use the wglChoosePixelFormatARB extension.
First we check that this has been enabled properly:

Then we define our list of requested attributes. Unlike the simple context we don’t populate a struct, instead we define a set of paired values. The first value is the attribute we want to define, the second is the value that we want to use. This can have an arbitrary number of values

Now we get the pixel format using wglChoosePixelFormat which has the following signature:

!#c++
BOOL wglChoosePixelFormatARB(
HDC hdc, // The HDC of the window
const int *piAttribIList, // A list of integer attributes
const FLOAT *pfAttribFList, // A list of floating point
UINT nMaxFormats, // Number of pixel formats that will be returned
int *piFormats, // array of pixel formats from best match down
UINT *nNumFormats // The number of pixel formats returned
);

We then call SetPixelFormat, unlike in the simple context case above we don’t need a PIXELFORMATDESCRIPTOR object and can just pass in a nullptr.

SetPixelFormat(device_context, improved_pixel_format, nullptr);

The last thing we need to do is create the actual context. For this we need some context attributes. These are created in a similar way to the pixel format attributes we created a little while ago.
First we check for the extension:

The first two are fairly obvious, these are the major/minor version of OpenGl that you want your context to create.
The last attribute (WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB) tells the context to completely disable all deprecated features up to the requested context.
We then call:

The call to wglCreateContextAttribsARB replaces the call to wglCreateContext that the simple context used.
Then we call wglMakeCurrent in the same way as we did before, and the message being sent to the console should tell you which version of OpenGL the created context actually supports.
If that’s not enough to convince you its working then add:

Into the message loop (after the if()…else) and your window will show up yellow.

Tidying Up

So we have a working context.
But nobody wants to write all that code every time they start a new project so we should tidy this up a bit.
Normally I would just go straight to turning this into a class, but for the sake of clarity I’m going to just break it into local methods in the main.cpp file for now.
But even before that there are some things that can be done.
The first thing is that we can zero out the WNDCLASSEX and PIXELFORMATDESCRIPTOR structs before setting just the values we care about. This basically means that the entire struct is set to 0.
So for WNDCLASSEX:

So that’s a couple of basic things tidied up, I only included the more verbose versions initially so that the structs could be described in slightly more detail.
Now to split it into methods.
To me at least it seems that 3 things are happening:

A window is created

GLEW is initialized

An OpenGL context is created

These seem like candidates for methods so let’s start with the first:
Define a method with the signature

HWND createApplicationWindow();

Move the first section of code into the body of that method. Basically everything from:

HINSTANCE instance = GetModuleHandle(nullptr);

to, with th eonly change being that we return the call to CreateWindow(...) rather than assigning from it.

window_handle = CreateWindow(…)

So the method looks like:

HWND createApplicationWindow(
) {
HINSTANCE instance = GetModuleHandle(nullptr);
WNDCLASSEX wcex;
SecureZeroMemory(&wcex, sizeof(WNDCLASSEX));
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_OWNDC;
wcex.lpfnWndProc = DefWindowProc;
wcex.hInstance = instance; // The instance we got above
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); // Just use the arrow for now
wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); // Set it to black
wcex.lpszClassName = L"tutorial_1_window"; // The class name
RegisterClassEx(&wcex);
return CreateWindow(
L"tutorial_1_window", // The class we created above
L"Tutorial 1", // The title of the window
WS_BORDER | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, // The style of the window
200, // The x position of the top left
200, // The y position of the window
600, // The width of the window
400, // The height of the window
NULL, // The parent of the window
NULL, // The menu for the window
instance, // The module instance
NULL // Not sure don't worry about it
);
}

To me that main function still looks a bit busy. Ideally I’d like it to have no Win32 calls in it at all. To do that we can create a new class that will take care of creating a window and attaching an OpenGL context.
How you approach the design of a class, and what you define in its public API is a big topic, so I’m not going to cover it in detail here. The one thing I think is important is that you should define the API first and then create a class around that. Rather than just jump in writing code.
So what would an OpenGL windowing class need?
Obviously a name, let’s call it GLWindow.
Create two new files GLWindow.h and GLWindow.cpp in your project

Notice they don’t have parameters now, they will just be acting on the class members, also they all return void. You could have them return bool to check for success as we did earlier, however I don’t particularly like that pattern. I’m not going to cover the pros and cons of things like exceptions or returning failure codes, that’s a topic for a different time so we’re going to make the rather naïve assumption that all our stuff works.
Define them in the .cpp file as:

The next thing is we need to move the message loop into the class. At the moment we will keep this very simple. We’re not interacting with anything or detecting user input yet so this doesn’t have to be very clever.
Just add a public method:

bool run();

and move the whole message loop into it as below, this is slightly different than we had before, and this is due to the fact that the message loop will run separately from the main update loop: