Tutorial 1 – The OpenGL Window

In this tutorial we’ll learn how to set up OpenGL to use a window just like those we created in the Win32 tutorials. Most of the code for the main file remains the same. We’ll come to the required changes in a bit but first we’ll create a class to manage all of the OpenGL rendering.

Now bear in mind that splitting off the OpenGL functionality into it’s own class is just a preference of mine. You can lump it all in with the Win32 code or arrange it any other way you like. I like to implement the code in this way because I feel it is much easier to read and manage applications when they’re divided into self contained modules.

The OpenGL Class

So step 1; Add a new class to your project. I’ve called mine GLRenderer as it handles all of the OpenGL rendering. The header file will define a couple of simple functions that are needed and one private member variable:

#include <Windows.h>
#include <GL/gl.h>

class GLRenderer
{
  public:
    GLRenderer(void);
    ~GLRenderer(void);
    int Initialise(HDC hdc, unsigned int width, unsigned int height);
    void Resize(unsigned int width, unsigned int height);
    void Render(void);
  private:
    HDC m_hDC;
};

As you can see we include the Windows header file to allow us to use some Windows specific types in this class. We also include the OpenGL header which gives us access to OpenGL types and functions. You’ll need to get hold of that header before you can compile this project. Once you’ve got it you’ll also have to put it somewhere that Visual Studio can find. In my case this was in a folder called “GL” within Visual Studios “C” include directory: C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include.

Following the header includes is the definition of the GLRenderer class. It has a public constructor and destructor with no parameters, an initialise function which takes an HDC (more on that in a second!) and some window dimensions, a resize function which also takes window dimensions and a render function. There is also one private member variable which is an HDC.

HDC is short for Handle of a Device Context. Basically this gives us the ability to do certain things with a graphics device, in this case, the monitor of your PC. Well now you know what the GLRenderer class looks like, lets see what it does.

GLRenderer::GLRenderer(void) : m_hDC(NULL)
{
}

The constructor is fairly dull. For this one I’ve used a slightly different syntax than you’re probably used to called an initialisation list. It’s nothing scary, it basically initialises some of the values used by the class. In the GLRenderer class it sets our m_hDC variable to NULL.

Now let’s see the initialise function’s implementation:

int GLRenderer::Initialise(HDC hdc, unsigned int width, unsigned int height)
{
  m_hDC = hdc;
  Resize(width, height);
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

  return 0;
}

The initialisation function does a little more stuff. Firstly it takes in a device context handle and stores it in our member variable. Then it calls our resize function using the dimensions that it’s given. It then sets the default “clear” colour of the window’s colour-buffer. In terms of OpenGL, a buffer is just a section of memory. The colour buffer is the section of memory that contains all of our pixel’s colour values. So with the glClearColor function we’re setting this memory’s default “clear” value to be an opaque black pixel. Assuming the program didn’t crash during this time the function will quit with a “successful” return code.

void GLRenderer::Resize(unsigned int width, unsigned int height)
{
  if(0 == width)
  {
    width = 1;
  }

  if(0 == height)
  {
    height = 1;
  }

  glViewport(0, 0, (GLsizei) width, (GLsizei) height);
}

This resize function takes in the width and height dimensions of the window, ensures that they are greater than 0, and then uses them to create an OpenGL viewport. Think of viewports as your window into the OpenGL world. In this case we’re saying, create a viewport starting at position (0, 0) in our Win32 window that is “width” pixels wide and “height” pixels high.

void GLRenderer::Render()
{
  glClear(GL_COLOR_BUFFER_BIT);

  SwapBuffers(m_hDC);
}

To render all we do is clear the colour buffer to the default value that is set in our initialise function and then swap the back and front buffers (we’ll be using double buffering to render our scene because it gives a much smoother feel to the graphics).

What Is Wiggle?

In a little bit we’ll be looking at a couple of functions that start with the acronym ‘wgl’. These functions belong to Wiggle. This is simply the collection of interfaces that allow Windows to use OpenGL.

Using The OpenGL Class

To use the OpenGL class we need to include it’s header in the main file and then do some additional coding.

#include "GLRenderer.h"

GLRenderer *g_pGLRenderer(NULL);

So now we have access to the GLRenderer class and we’ve got a pointer to a GLRenderer object that is yet to be constructed. In the main file we add the following after we’ve created our Window:

HDC hDC(GetDC(hwnd));
SetupPixelFormat(hDC);
HGLRC hRC(wglCreateContext(hDC));
wglMakeCurrent(hDC, hRC)
g_pGLRenderer = new GLRenderer();

First of all we get the handle of the device context used by the window. Next we need to set up the pixel format used by OpenGL (don’t worry you’ve not gone mad, we’ll look at this function in a little bit). Although we have a handle to the device context, we need to get an OpenGL rendering context. We get that by passing the device context to wglCreateContext. Once we’ve got the rendering context we need to inform Windows that this is the one we’re going to use so we pass the device and the rendering context to wglMakeCurrent. After that we just construct a new GLRenderer object.

Pixel Format

To use OpenGL we need to tell it what pixel format it will use.

void SetupPixelFormat(HDC hDC)
{
  PIXELFORMATDESCRIPTOR pfd;
  pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);

  pfd.nVersion = 1;
  pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
  pfd.iPixelType = PFD_TYPE_RGBA;
  pfd.cColorBits = 24;
  pfd.cDepthBits = 16;

  int pixelFormat(ChoosePixelFormat(hDC, &pfd));

  SetPixelFormat(hDC, pixelFormat, &pfd);
}

The first thing we’re doing in this function is creating a structure to describe what pixel format OpenGL will be using. We inform the graphics device that it’s drawing surface will be a window and that we will be using double buffered OpenGL to do all the drawing. Our colour buffer will use 24 bits to store it’s RGB values (we ignore the alpha channel for now) and our depth buffer will use 16 bits.

Next we call ChoosePixelFormat. What this function does is attempt to create and return the closest match to our pixel format description. If you create a really weird or unsupported format then this function will return 0. Once we’ve got the nearest available match to our required pixel format, we inform the graphics device that this is the pixel format that we’ll be using by calling SetPixelFormat.

Initialisation

Although our OpenGL rendering class has been constructed, it still needs to be initialised.

RECT windowRect;
GetClientRect(hwnd, &windowRect);

g_pGLRenderer->Initialise(hDC, windowRect.right, windowRect.bottom);

In this bit of code we get the dimensions of the inside of our Window and pass it to the GLRenderer object’s initialise function. If that function succeeds then we can carry on with our standard message loop. Of course we still need to add a call to our render function to the code. We’ll put it in the message loop so that the window is continually redrawn:

g_pGLRenderer->Render();

If you follow these instructions then you should get something that looks like this:
It does look rather like the window from the Win32 tutorials but trust me, these are black pixels created by OpenGL rather than Win32! Honestly! Try changing the glClearColor function to use a different colour and see what happens.

Cleanliness Is A Virtue

We’ve created a few things in our main code file now and it’s important to always clear up after yourself. That’s why we’ll be adding this line to the end of our main code:

wglDeleteContext(hRC);

This will clean up the OpenGL rendering context that we created earlier. For a more in depth look, comments, additional error checking etc. can be found here:

OpenGL Tutorial 1

There’s quite a lot of new topics covered in this tutorial (Graphics APIs usually take a bit of setting up!) so take your time to digest it all. The following tutorials should be in slightly more bitesize chunks!

Advertisements
  1. formwach
    July 23, 2011 at 4:51 pm

    Hello, im the same guy, who`d requesting for texture tuto / without complied the first one,
    im work on win 7 64bit OS and build your code in VC++2010 IDE – Express-Edition, in debug and the release mode switch, i got the same error, the afxres.h is not installed in the express edition of VC++, this is the result of internet research, i not really sure, if this the main problem, or in context of VC++ Version$ – this sort out/replace by… anyway i solve it by open OpenGL Tutorial1.rc whit the std. notepad (win+r typing notepad, drag, drop file ) and change the missing header file into “windows.h”. At this point all works fine. Continue reading on taskcode : )

    • July 28, 2011 at 9:14 am

      Hi formwach, I too am working on Windows 7 x64 so this should be fine for the code you’re working on. I believe “afxres.h” is used in MFC projects, but for some reason VC++ sometimes still adds it to other projects. It sounds like changing the “.rc” file’s include to “windows.h” is an acceptable workaround for this (after a bit of browsing I found that some people say to change it to “winres.h”). As long as you’ve fixed this problem hopefully the rest of the code works fine!

  2. formwach
    July 26, 2011 at 7:53 pm

    I know this is a blog, and no forum, edit/del my comments/questions at will.
    im from germany, sorry for bad english.

    What is the most important characteristic, when ur use WINAPI – for tutorials, and either general programing yr apps. modern OpenGL- Programing, or whatever it’s called, old links from Khronos Group to pages whit tutorials,examples are gone, taking by sort books,
    whit the newest GL extensions freeglut,glew,glm to initialize the window, event handling.

    The reason why i will get deeper understanding of what your write in WINAPI, Windows is still Windows. But looking for other ways to make black windows, my experience HOW-TO
    in lightweight code, like glut. And make a game-engine possible to run on different Os.

  1. July 26, 2011 at 7:59 pm

Tell me what you think!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: