::[ PART 11 - OPENGL INITIALIZATIONS ]::[ Source ]::[ Printable Version ]::
::[ DISCLAIMER ]::
I will hold no responsibility to whatever happens to you, your computer, your sanity, your pet, or whatever that may happen to your existence for your reading these texts and for the outcome of the various source code(s) given in each tutorial. So in short, read at your own risk!

 
::[ INTRODUCTION ]::
Basic GDI graphics didn't suit your need for superb graphics? Want your graphics hardware accelerated? Or how about those who don't have a video card capable of hardware acceleration? Well, they too can benefit from the rather nice software rendering of...you guessed it...OpenGL!

If by some chance you skipped my tutorials and read this file and don't know how to create a window, then please read Part 6 and 7 of my tutorials...;). This is important because in this tutorial, I'll share with you guys how to create a window that supports OpenGL graphics!

The version of OpenGL that will be discussed in this tutorial (and in most of my latter OpenGL tutorials) will be 1.1. There is a 1.2 and heck there's OpenGL 1.4 already...but stubborn Microsoft didn't/haven't implement them, which means the new features that came out with newer versions are to be treated as extensions. We'll deal with extensions on a future tutorial...for now, just know that you can go a LONG, LONG, and I mean a LOOOONNNGG way with OpenGL 1.1...enjoy!

SideNote: It would be good to let you know that what I know here is purely self-taught. The information and knowledge that I have acquired are those that I have read and glanced around the Internet. I have no book or whatsoever on OpenGL (or anything about computing for that matter ...;) ), so it would be wise to look around the Internet for other tutorials regarding OpenGL. Some suggested sites may be found on the Reference Section at the bottom of this file.

 
::[ TUTORIAL ]::
So what exactly IS OpenGL??? Well, OpenGL stands for "Open Graphics Library"...it's a way for you to use (or interface) your graphics card. It is designed to be very portable and more importantly...FAST! (that's why games use it...;) ).

Under MS Windows, the two dominant graphics APIs are OpenGL and DirectX's DirectGraphics, formerly known as Direct3D and DirectDraw (DirectX is currently at version 8 as of this writing, but don't let the number '8' fool you...you might think that since there's a version 8 of DirectX that it's so much simpler to use its DirectGraphics... but the opposite is true...It seems Microsoft's not doing a very good job is it???...=) ).

If you are one of those that do not own a video card that offers hardware acceleration for OpenGL, then what you will see rendered on your screen will be that of Microsoft's generic implementation of OpenGL.

"Err...what's a 'generic implementation' Mr. You-Think- You-Know-It-All??!?" - Confused Reader

What? Mr. I-Think-I-Kn...No, no, no...as I said above, I am purely self-taught, which means I probably don't know a whole lot of what I'm blabbering here...;)...huh?...that wasn't your question?...oh, right, about the 'generic implementation', err..ahem...yeah, I know you were asking that...(Note to self: Just answer the question stupid).

OK, OK, here's my answer...

Using a generic implementation (from what I understand) is synonymous to software-only rendering. This means you don't care what video card the player of your program (err..I meant game...;) ) is using...this also means that your Quack 3 engine (your soon-to-be Quake 3 engine clone) will run at a rather snail's pace...=(...but software-rendering is nonetheless OK for small graphical stuffs...=).

So now that you know what OpenGL is, let's get on with the REAL tutorial proper (Yes, I haven't started yet...;) ).

First, of course, we include the header libraries that we need, they are usually located under the GL directory of your C/C++ compiler's INCLUDE directory.

  //----8<----[ CODE BEGIN ]--------//
    #include <gl/gl.h>
    #include <gl/glu.h>
  //----8<----[ CODE END   ]--------//
gl.h is the main OpenGL header file, while glu.h is OpenGL's utility header file. To those familiar with DirectX's DirectGraphics, glu.h is analogous to DirectX's Direct3DX. Basically, the utility file helps us programmers with common tasks. You'll see how it helps us as we go along.

Note that there is another file, named glaux.h included with the OpenGL SDK. As much as possible, try NOT to use it, it's obsolete.

After including the header libraries, we include the .lib files. In Microsoft VC++ 6.0 (I'm not sure about earlier versions), you can include .lib files through the following lines:

  //----8<----[ CODE BEGIN ]--------//
    #pragma comment(lib, "OpenGL32.lib")
    #pragma comment(lib, "Glu32.lib")
  //----8<----[ CODE END   ]--------//

The two .lib files we included above (if it's not obvious enough) were OpenGL32.lib and Glu32.lib.

The next step is to define the pixel format that we're going to use. The pixel format describes how pixels are used. And what better way to describe our pixel format than through the PIXELFORMATDESCRIPTOR structure! (No, I'm not joking, it's really named that way!...:) ).

OK, Microsoft's rather lousy implementation of OpenGL resulted in us not really being able to utilize the real power of OpenGL. Which means we cannot use all of the flags that we might want to. Anyway, to use the PIXELFORMATDESCRIPTOR, we define it like so:

  //----8<----[ CODE BEGIN ]--------//
    PIXELFORMATDESCRIPTOR myPFD;

    ZeroMemory(&myPFD, sizeof(myPFD));
    myPFD.nSize    = sizeof(myPFD);
    myPFD.nVersion = 1;
    myPFD.dwFlags  = PFD_DRAW_TO_WINDOW |
                     PFD_SUPPORT_OPENGL |
                     PFD_DOUBLEBUFFER;
    myPFD.cColorBits = 16;
    myPFD.cDepthBits = 16;
    myPFD.iPixelType = PFD_TYPE_RGBA;
    myPFD.iLayerType = PFD_MAIN_PLANE;
  //----8<----[ CODE END   ]--------//

What ZeroMemory() does is set the whole myPFD to zeroes...meaning, I'm initializing EVERY field of the structure to zero (nice trick huh? ;) ).

ZeroMemory() is equivalent to:

     memset(&myPFD, 0, sizeof(myPFD));

It's up to you guys which one you'd like to use.

Briefly explaning each field:

  • nSize - Size of the structure (rather obvious, 'no?).
  • nVersion - ALWAYS set to 1...need I say more?...;)
  • dwFlags - Define the capabilities of our pixel format. PFD_DRAW_TO_WINDOW says we want to be able to draw to our window (duh Willie!), PFD_SUPPORT_OPENGL says we want to support OpenGL (double duh Willie!!)...OK, OK, I won't bother saying what PFD_DOUBLEBUFFER is...but just in case some of you don't know yet, double buffering allows flicker-free rendering.
  • cColorBits - Bit depth that we want. In the above case, we requested for 16bit color for each pixel.
  • cDepthBits - Precision of depth buffer. 16 is usually OK, but use 32 for maximum accuracy.
  • iPixelType - Type of pixel data. Here we told OpenGL to use RGBA mode. I don't think you'd normally want to change this flag...;). Another possible flag tells OpenGL to use color index mode...which is so UNCOOL...:(.
  • iLayerType - ALWAYS set to PFD_MAIN_PLANE. Why? Dunno, ask Microsoft...:P.
I believe it would be wise to tell you guys more about the cDepthBits field.

In OpenGL, every pixel has a corresponding depth value stored in the depth buffer (wow, that's deep...:P ). Anyway, this "depth value" of each pixel is what makes the sorting of pixels work.

For example, you have created/loaded a 3D model of a tree, you placed the tree in front of yet another 3D model, which is a house. Now, let us assume you're standing in front of the tree and looking at it, to see this tree properly, EVERY pixel of the tree should be rendered IN FRONT of the house.

Now, we specified a 16bit precision to our depth buffer. To me, this is more of a concept thing, the greater the value, the more the precision (duh Willie!), the more the precision the less the z-fighting...:).

So what's z-fighting you ask? It's when two objects (let's use the tree and the house again) share the same location. Imagine rendering the tree close to the house, and the branches and leaves of the tree hits the roof of the house, the part where the branches and the roof hits will have z-fighting! (I hope that's clear now...:) ).

To those wise guys thinking, "I could solve the tree-and-roof problem by moving my tree a bit further then!"...well, think again. If you were rendering a thick forest instead, the branches will SURELY hit each other, there you'll see the importance of specifying a nice precision for your depth buffer...:). (Actually, you'll notice the z-fighting only when you're moving)

The two values used under MS Windows to specify the precision is 16 and 32. But 16 will usually do fine...:).

There are more fields to the PIXELFORMATDESCRIPTOR, but we'll only use the listed fields above.

"OK Willie, what's next?" - Impatient Reader

What's next, my dear Impatient Reader, is for us to choose the best pixel format that OpenGL could find. So why do we have to choose the best pixel format when we've just described what we want? The answer's simple:

PIXELFORMATDESCRIPTOR is just a structure that holds the, you guessed it, pixel format that is requested. Should the requested format be unavailable (for example, some people don't have video cards that can show 32bit color), then the nearest possible format will be chosen for you.

We choose the best pixel format like so (It couldn't be simpler):

  //----8<----[ CODE BEGIN ]--------//
    UINT myBestPixelFormat;

    myBestPixelFormat = ChoosePixelFormat(HDC, &myPFD);    
  //----8<----[ CODE END   ]--------//

where HDC is the Device Context of the window (you should know how to grab one by now) and myPFD is a structure of type PIXELFORMATDESCRIPTOR!!!

After that step, we set our pixel format (like giving OpenGL the go-signal to use the chosen best pixel format), and again, it couldn't have been simpler:

  //----8<----[ CODE BEGIN ]--------//
    SetPixelFormat(HDC, myBestPixelFormat, &myPFD);
  //----8<----[ CODE END   ]--------//

Again, HDC is the Device Context of our window, myBestPixelFormat is the chosen pixel format and myPFD is the structure of type PIXELFORMATDESCRIPTOR.

So far so good? I hope so...:).

Let's recap first, what we've done so far:

  • Fill a structure of type PIXELFORMATDESCRIPTOR with values that describes the pixel format that we want to request.
  • Choose the best pixel format that would match our requested pixel format.
  • Set the chosen best pixel format for OpenGL to use.
The next and second-to-the-last step is for us to grab our (drum rolls please) Rendering Context!

So what's a Rendering Context? It's OpenGL's version of a Device Context...;).

We grab it like so:

  //----8<----[ CODE BEGIN ]--------//
    HGLRC myRC;

    myRC = wglCreateContext(HDC);
  //----8<----[ CODE END   ]--------//

And the last step is to activate (read: use) the Rendering Context that we got, like so:

  //----8<----[ CODE BEGIN ]--------//
    wglMakeCurrent(HDC, myRC)
  //----8<----[ CODE END   ]--------//

where HDC is the Device Context of the window...;).

And that's it, we're done! Look at the NOMAD.SOURCE Section below to see how it all fits together.

One more thing, when using OpenGL, we don't draw our screen every frame under the WM_PAINT message anymore, we create a separate function that draws what we want to our window. This function is usually placed at the part where we grab our messages.

Here's the idea:

  • Check if there's a message that arrived
  • If a message arrived, translate and send the message to our WndProc
  • If no message arrived, call our main rendering function/loop
  • Keep looping until program closes
And here's the implementation:

  //----8<----[ CODE BEGIN ]--------//
    BOOL Done = FALSE;

    while(Done==FALSE)                     // Loop That Runs While Done=FALSE
    {
       if (PeekMessage(&Msg,NULL,0,0,PM_REMOVE))  // Is There A Message Waiting?
       {
	  if (Msg.message==WM_QUIT)       // Have We Received A Quit Message?
	    Done = TRUE;                  // If So Done=TRUE
	  else                            // If Not, Deal With Window Messages
	  {
	    TranslateMessage(&Msg);       // Translate The Message
	    DispatchMessage(&Msg);        // Dispatch The Message
	  }
	} else                            // If There Are No Messages
        {
 	  Render();                       // Call Our Main Rendering Loop
        }
    }
  //----8<----[ CODE END   ]--------//

And now we're done!

 
::[ ENDING ]::
And that's that! Not hard eh? If there is anything that's not clear to you, your mind, or to that part of you that lets you comprehend the things here, you may email me at:

Tadah! Your first OpenGL-supported window! Oh what joy! (Believe me, I'm being sarcastic...)...don't worry though, we'll show something in that window on my next tutorial.

The codes found in the NOMAD.SOURCE Section below are the base codes that will be used from now on. I'll be building up on these as my OpenGL tutorials progresses...:).

Part 12 will be about showing simple polygons to the screen.

Briefly, we specify coordinates (called vertices, plural for vertex...;) ) and just tell OpenGL that they're the endpoints of a triangle (note that it could also be a quad too! or a line, or a point...;) ), just watch out for Part 12!

 
::[ REFERENCE ]::
I want to recommend the following sites to you:

The first link is one of the most reknowned OpenGL tutorial sites out there on the web.

The other link is the official website of OpenGL. It too has links to tutorials and some downloadable tutorials/papers from SIGGRAPH!

 
::[ NOMAD.SOURCE ]::

  gfxOpenGL1.zip - MSVC++ 6.0 source
This tutorial was written on: 04.21.2002 | Updated: 05.03.2002
:.Site.:.Menu.:
:// .site..news.
:// .tutorials..1.
:// .tutorials..2.
:// .projects.
:// .forums.
:// .gbook.
:// .links.