SIGGRAPH '97
Course 24: OpenGL and Window System Integration
OpenGL and Win32
A Simple Example
In order to use OpenGL with Win32 to render images, there are some
initialization steps that must be taken. These steps are outlined
below.
Create a Window
Before creating a window, a window class must be registered. A
window class is a basic template that is used to create a window in an
application. Every window is associated with a window class. To
register a window class, a WNDCLASS structure is filled out
with the desired settings and then the Win32 function
RegisterWindowClass() is called with a pointer to this
structure as an argument. Multiple windows can be associated with a
single class. When the application that registered a window class
exits, the window class is destroyed. A window class can be
identified by its class name (a character string).
The window class contains the window procedure. A window
procedure is a callback function that is used by Win32 to notify the
application of messages that should be processed by the window. A
window procedure must have the form: LONG WINAPI WindowProc(HWND,
UINT, WPARAM, LPARAM). See the next section on messages for more information
about window procedures.
The following code fragment shows how to register a new window class.
code fragment from oglCreateWindow() function in simple.c
/* oglCreateWindow
* Create a window suitable for OpenGL rendering
*/
HWND oglCreateWindow(char* title, int x, int y, int width, int height)
{
WNDCLASS wc;
HWND hWnd;
HINSTANCE hInstance;
/* get this modules instance */
hInstance = GetModuleHandle(NULL);
/* fill in the window class structure */
wc.style = 0; /* no special styles */
wc.lpfnWndProc = (WNDPROC)WindowProc; /* event handler */
wc.cbClsExtra = 0; /* no extra class data */
wc.cbWndExtra = 0; /* no extra window data */
wc.hInstance = hInstance; /* instance */
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); /* load a default icon */
wc.hCursor = LoadCursor(NULL, IDC_ARROW); /* load a default cursor */
wc.hbrBackground = NULL; /* redraw our own bg */
wc.lpszMenuName = NULL; /* no menu */
wc.lpszClassName = title; /* use a special class */
/* register the window class */
if (!RegisterClass(&wc)) {
MessageBox(NULL,
"RegisterClass() failed: Cannot register window class,",
"Error", MB_OK);
return NULL;
}
. . .
}
Although the settings above should be sufficient for many
applications, there are many values each field of the
WNDCLASS structure can assume. For more information on the
WNDCLASS structure and its options, see the Microsoft
Developer Studio InfoViewer topic WNDCLASS.
Once a window class has been successfully registered, a new window can
be created. When creating a window suitable for OpenGL rendering, the
window style must have the WS_CLIPSIBLINGS and
WS_CLIPCHILDREN attribute bits set.
The following code shows how to create a window.
code fragment from oglCreateWindow() function in simple.c
/* oglCreateWindow
* Create a window suitable for OpenGL rendering
*/
HWND oglCreateWindow(char* title, int x, int y, int width, int height)
{
WNDCLASS wc;
HWND hWnd;
HINSTANCE hInstance;
. . .
/* create a window */
hWnd = CreateWindow(title, /* class */
title, /* title (caption) */
WS_CLIPSIBLINGS | WS_CLIPCHILDREN, /* style */
x, y, width, height, /* dimensions */
NULL, /* no parent */
NULL, /* no menu */
hInstance, /* instance */
NULL); /* don't pass anything to WM_CREATE */
/* make sure we got a window */
if (hWnd == NULL) {
MessageBox(NULL,
"CreateWindow() failed: Cannot create a window.",
"Error", MB_OK);
return NULL;
}
/* show the window (map it) */
ShowWindow(hWnd, SW_SHOW);
/* send an initial WM_PAINT message (expose) */
UpdateWindow(hWnd);
return hWnd;
}
A common style attribute which is used quite often (and bears
mentioning here) is the WS_OVERLAPPEDWINDOW style. This
creates a window that has resize handles and a system menu as well as
the three icons (minimize, maximize and close) common to most Win32
windows in the upper right hand corner of the title (caption) bar. In
the next section on messages, there are
some example programs that use this style. Another style that can be
used allows for the window to take up the whole screen. See the fullscrn.c program for an example of this
style.
While in the example we only use the minimum style options necessary
for OpenGL (WS_CLIPCHILDREN and WS_CLIPSIBLINGS),
there are many options that can be used when creating a window. See
the Microsoft Developer Studio InfoViewer topic CreateWindow
for a list of all the available options.
After creating a new window it must be shown if the rendering is to be
seen. It is also a good idea (though not strictly necessary) to force
an initial paint by making a call to the window procedure in order to
"prime the message pump". This is accomplished by calling the
ShowWindow() and UpdateWindow() functions as shown
in the example above.
Set the Pixel Format
After a window class has been registered and a new window has been
successfully created, the pixel format must be set. The
simplest way to set the pixel format is to use the
ChoosePixelFormat() function. More sophisticated methods for
choosing the pixel format will be discussed in a later section.
The pixel format specifies several properties of an OpenGL context.
Common properties are depth of the Z buffer, whether a stencil buffer
exists or not, whether the framebuffer is double buffered and many
others.
In order to specify the many properties available, a
PIXELFORMATDESCRIPTOR structure is employed. The members of
this structure correspond to different properties. In order to set
these properties, the corresponding field is set in the
PIXELFORMATDESCRIPTOR structure and a format that best fits
the criteria defined by the PIXELFORMATDESCRIPTOR structure
is selected by the ChoosePixelFormat() function. The "best
fit" is somewhat ambiguous and methods for finding exactly the pixel
format desired are covered, as mentioned above, in a later section.
The following code fragment illustrates how to set the pixel format.
code defining the oglSetPixelFormat() function in simple.c
/* oglPixelFormat()
* Sets the pixel format for the context
*/
int oglSetPixelFormat(HDC hDC, BYTE type, DWORD flags)
{
int pf;
PIXELFORMATDESCRIPTOR pfd;
/* fill in the pixel format descriptor */
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1; /* version (should be 1) */
pfd.dwFlags = PFD_DRAW_TO_WINDOW | /* draw to window (not bitmap) */
PFD_SUPPORT_OPENGL | /* draw using opengl */
flags; /* user supplied flags */
pfd.iPixelType = type; /* PFD_TYPE_RGBA or COLORINDEX */
pfd.cColorBits = 24;
/* other criteria here */
/* get the appropriate pixel format */
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
MessageBox(NULL,
"ChoosePixelFormat() failed: Cannot find format specified.",
"Error", MB_OK);
return 0;
}
/* set the pixel format */
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
MessageBox(NULL,
"SetPixelFormat() failed: Cannot set format specified.",
"Error", MB_OK);
return 0;
}
return pf;
}
Note that type is one of PFD_TYPE_RGBA for
non-paletted or PFD_COLORINDEX for paletted (indexed) display
mode. flags is a bitwise OR (|) of several options. We'll
use only PFD_DOUBLEBUFFER which selects a doublebuffered
framebuffer for these simple examples. For more information on what
other values it can assume, see the next section on pixel formats or the Microsoft
Developer Studio InfoViewer topic
PIXELFORMATDESCRIPTOR.
Create a Rendering Context
The final step in setting up for OpenGL rendering is to create the
OpenGL context. An OpenGL rendering context in Win32 has the type
HGLRC. All OpenGL rendering must go through an HGLRC. A
context must be current for OpenGL calls to affect to it.
The procedure for creating and making a context current is shown
below.
code from main() function in simple.c
/* main()
* Entry point
*/
int main(int argc, char** argv)
{
HDC hDC; /* device context */
HGLRC hRC; /* opengl context */
HWND hWnd; /* window */
. . .
/* create an OpenGL context */
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
/* now we can start changing state & rendering */
while (1) {
/* rotate a triangle around */
glClear(GL_COLOR_BUFFER_BIT);
glRotatef(1.0, 0.0, 0.0, 1.0);
glBegin(GL_TRIANGLES);
glColor3f(1.0, 0.0, 0.0);
glVertex2i( 0, 1);
glColor3f(0.0, 1.0, 0.0);
glVertex2i(-1, -1);
glColor3f(0.0, 0.0, 1.0);
glVertex2i( 1, -1);
glEnd();
glFlush();
SwapBuffers(hDC); /* nop if singlebuffered */
}
/* clean up */
wglMakeCurrent(NULL, NULL); /* make our context 'un-'current */
ReleaseDC(hDC, hWnd); /* release handle to DC */
wglDeleteContext(hRC); /* delete the rendering context */
DestroyWindow(hWnd); /* destroy the window */
return 0;
}
After this is done, OpenGL calls can be made to change state and
render to the context as shown in the example above. In order to
clean up the resources allocated for OpenGL rendering, first make the
HGLRC 'un'-current, release the HDC and delete the context. Lastly,
destroy the window.