OpenGL:Tutorials:Windows Setup

From GPWiki
Jump to: navigation, search

Setting up an OpenGL Project in Win32 with Visual Studio .NET

Sample Code for this tutorial Similar alternative code, OpenGL Windows Framework

Creating a Project

  • Close any open solution.
    • File->Close Solution
  • Create a new project.
    • File->New->Project
    • In the left panel select "Visual C++ Projects"
    • In the right panel select "Win32 Project"
    • Enter a name for the project
    • Press OK
    • In the next screen select Application Settings
      • Set application type to "Windows Application"
      • Uncheck "Empty project" under "Additional Options"
  • Visual studio will create the project and write several source files for you at this point, the next steps are to transform this project into a project into an OpenGL rendering project. You could use the empty project option mentioned above and write these functions yourself but there isn't much reason.

Transforming the project into an OpenGL compatible project

  • Set up linker to link in appropriate OpenGL libraries
    • In the Class View toolbar, right click on your project name and click "Properties"
    • At the top left drop down menu, select "All Configurations"
    • In the right panel of the Properties Window, select "Linker" and then in the submenu "Input"
    • In the "Additional Dependencies" area type "opengl32.lib glu32.lib glaux.lib"
    • Press OK
  • Include some or all of the OpenGL header files at the top of the source file with the same name as your project (the same file that contains the _tWinMain() function)

  1. include <gl/gl.h>
  2. include <gl/glu.h>
  3. include <gl/glaux.h>

  • Modify the generated code - In order for a window to be able to render OpenGL, an appropriate pixel format must be selected for that window so that we have things like a Z-buffer that normal windows don't use. Notably, you may only set a pixel format for a window once.
    • In function InitInstance, in the CreateWindow() call, replace "WS_OVERLAPPEDWINDOW" with "WS_CLIPSIBLINGS | WS_CLIPCHILDREN" so that any child or sibling windows don't get drawn over by our OpenGL code. This also removes the upper right system menu box from the window, which can cause problems noted later.
    • In order to set a pixel format we need an Handle to a Device Context (or HDC), to get an HDC we need a Handle to a Window (or HWND)
      • Add a global declaration for "HWND hWnd;"
      • Remove local definition in function InitInstance(), after calling InitInstance() we will then have an HWND in variable hWnd
    • After the InitInstance() call in _tWinMain(), add a call to GetDC(), and be sure its output isn't NULL

HDC hdc = GetDC(hWnd); if (hdc == NULL) return FALSE;

BOOL SetPF(HDC hdc) { PIXELFORMATDESCRIPTOR pfd;

pfd.cAccumBits = 0; pfd.cAccumAlphaBits = 0; pfd.cAccumBlueBits = 0; pfd.cAccumGreenBits = 0; pfd.cAccumRedBits = 0; pfd.cAlphaBits = 8; pfd.cAuxBuffers = 0; pfd.cColorBits = 32; pfd.cDepthBits = 24; pfd.cStencilBits = 0; pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; pfd.iLayerType = PFD_MAIN_PLANE; pfd.iPixelType = PFD_TYPE_RGBA; pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1;

// choose pixel format returns the number most similar pixel format available int n = ChoosePixelFormat(hdc, &pfd); // set pixel format returns whether it sucessfully set the pixel format return SetPixelFormat(hdc, n, &pfd); }

      • Call the function after the HDC code.

if (SetPF(hdc) == FALSE) return FALSE;

    • Finally, given an HDC with an appropriate pixel format, we can make a Handle to an openGL Rendering Context (HGLRC)
      • After the pixel format code, declare an HGLRC and call wglCreateContext() to assign it a value

HGLRC hglrc = wglCreateContext(hdc);

      • Lastly, make the context "current", the one we'll be rendering to.

wglMakeCurrent(hdc, hglrc);

Testing that OpenGL is rendering

The simplest way to test that OpenGL is rendering to you window is to clear the color buffer to something other than the default and swap the buffers glClearColor(1.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); SwapBuffers(hdc);

Practical Considerations

Under circumstances such as a game you'll probably want to modify the message loop from while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } to something like while (1) { // add drawing code here

SwapBuffers(hdc); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && !TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { if (msg.wParam == IDM_EXIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } } so that you can render continuously rather than whenever a paint message comes it, alternatively you could use the paint message structure and just post another paint message whenever one comes in. (I haven't tried this but it should work the same way) Also, if you use that exact structure you should make sure to remove the system menu from the window creation call so that the user must go to File->Exit rather than closing the window. The above structure will not stop the process when the window closes. One solution I've used is to create a boolean "exitstate" variable, set to true when either the window is destroyed or file->exit is called, and is used as the condition of the while loop: while(!exitstate)...

Doug Sheets (doug.sheets@gmail.com) November 19th, 2004