(WGL) windows 中的OpenGL
在这里主要介绍在windows窗体应用程序中使用OpenGL API;
OpenGL与windows窗体的协调工作是通过渲染环境来实现的。
在这里首先明白两个名词:渲染环境与设备环境
设备环境是由windows保存的一种数据结构,用来记录、描述图形设备的一些属性值;
渲染环境是由OpenGL提供的API来进行创建,渲染环境也是记录保存一些与绘图有关的属性值;
渲染环境与设备环境的设置应该保持一致。
下面结合一个简单的例子说明OpenGL与windows窗体的结合及应用:
首先需要说明的是,在这里创建应用程序之初,应选择窗体应用程序,否则就需要对项目属性进行设置。设置方法
下面言归正传对程序进行程序的结构进行说明:
1、利用windows API创建一个窗体 代码如下:

WNDCLASSEX windowClass; // window class HWND hwnd; // window handle MSG msg; // message DWORD dwExStyle; // Window Extended Style DWORD dwStyle; // Window Style RECT windowRect; g_glRender = new CGfxOpenGL; windowRect.left = (long)0; // Set Left Value To 0 windowRect.right = (long)windowWidth; // Set Right Value To Requested Width windowRect.top = (long)0; // Set Top Value To 0 windowRect.bottom = (long)windowHeight; // Set Bottom Value To Requested Height // fill out the window class structure windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = MainWindowProc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = hInstance; windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // default icon windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // default arrow windowClass.hbrBackground = NULL; // don't need background windowClass.lpszMenuName = NULL; // no menu windowClass.lpszClassName = "GLClass"; windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // windows logo small icon // register the windows class if (!RegisterClassEx(&windowClass)) return 0; if (fullscreen) // fullscreen? { DEVMODE dmScreenSettings; // device mode memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); dmScreenSettings.dmSize = sizeof(dmScreenSettings); dmScreenSettings.dmPelsWidth = windowWidth; // screen width dmScreenSettings.dmPelsHeight = windowHeight; // screen height dmScreenSettings.dmBitsPerPel = windowBits; // bits per pixel dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; // if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { // setting display mode failed, switch to windowed MessageBox(NULL, "Display mode failed", NULL, MB_OK); fullscreen = FALSE; } } if (fullscreen) // Are We Still In Fullscreen Mode? { dwExStyle = WS_EX_APPWINDOW; // Window Extended Style dwStyle = WS_POPUP; // Windows Style ShowCursor(FALSE); // Hide Mouse Pointer } else { dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style dwStyle = WS_OVERLAPPEDWINDOW; // Windows Style } AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size // class registered, so now create our window hwnd = CreateWindowEx(NULL, // extended style "GLClass", // class name "BOGLGP - Chapter 2 - OpenGL Application", // app name dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, // x,y coordinate windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, // width, height NULL, // handle to parent NULL, // handle to menu hInstance, // application instance NULL); // no extra params hDC = GetDC(hwnd); // check if window creation failed (hwnd would equal NULL) if (!hwnd) return 0; ShowWindow(hwnd, SW_SHOW); // display the window UpdateWindow(hwnd); // update the window
说明一下在这里fullscreen 变量设置为false 否则如果不加入OpenGL程序就会出错,对创建窗体程序不太熟悉的原因,感觉应该是需要重写窗体的事件。
下面一个与OpenGL无关的创建一个窗体的程序,就算是学习windows 窗体应用程序的相关知识吧!

#include <Windows.h> bool exiting = false; long windowWidth = 800; long windowHeight = 600; long windowBits = 32; bool fullscreen = false; HDC hDC; void SetupPixelFormat(HDC hDC) //设置像素格式 { int pixelFormat; PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size 1, // version PFD_SUPPORT_OPENGL | // OpenGL window PFD_DRAW_TO_WINDOW | // render to window PFD_DOUBLEBUFFER, // support double-buffering PFD_TYPE_RGBA, // color type 32, // prefered color depth 0, 0, 0, 0, 0, 0, // color bits (ignored) 0, // no alpha buffer 0, // alpha bits (ignored) 0, // no accumulation buffer 0, 0, 0, 0, // accum bits (ignored) 16, // depth buffer 0, // no stencil buffer 0, // no auxiliary buffers PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0, // no layer, visible, damage masks }; pixelFormat = ChoosePixelFormat(hDC, &pfd); SetPixelFormat(hDC, pixelFormat, &pfd); } /*当窗体触发任何一个事件时,便会调用此函数*/ LRESULT CALLBACK MainWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HDC hDC; static HGLRC hRC; int height, width; // dispatch messages switch (uMsg) { case WM_CREATE: // window creation hDC = GetDC(hWnd); SetupPixelFormat(hDC); //SetupPalette(); //hRC = wglCreateContext(hDC); //wglMakeCurrent(hDC, hRC); break; case WM_DESTROY: // window destroy case WM_QUIT: case WM_CLOSE: // windows is closing // deselect rendering context and delete it //wglMakeCurrent(hDC, NULL); //wglDeleteContext(hRC); // send WM_QUIT to message queue PostQuitMessage(0); break; case WM_SIZE: height = HIWORD(lParam); // retrieve width and height width = LOWORD(lParam); break; case WM_ACTIVATEAPP: // activate app break; case WM_PAINT: // paint PAINTSTRUCT ps; BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps); break; case WM_LBUTTONDOWN: // left mouse button break; case WM_RBUTTONDOWN: // right mouse button break; case WM_MOUSEMOVE: // mouse movement break; case WM_LBUTTONUP: // left button release break; case WM_RBUTTONUP: // right button release break; case WM_KEYUP: break; case WM_KEYDOWN: int fwKeys; LPARAM keyData; fwKeys = (int)wParam; // virtual-key code keyData = lParam; // key data switch (fwKeys) { case VK_ESCAPE: PostQuitMessage(0); break; default: break; } break; default: break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { WNDCLASSEX windowClass; // window class HWND hwnd; // window handle MSG msg; // message DWORD dwExStyle; // Window Extended Style DWORD dwStyle; // Window Style RECT windowRect; windowRect.left = (long)0; // Set Left Value To 0 windowRect.right = (long)windowWidth; // Set Right Value To Requested Width windowRect.top = (long)0; // Set Top Value To 0 windowRect.bottom = (long)windowHeight; // Set Bottom Value To Requested Height // fill out the window class structure windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = MainWindowProc; //当窗体触发任何一个事件时,便会调用该函数 windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = hInstance; windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // default icon windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // default arrow windowClass.hbrBackground = NULL; // don't need background windowClass.lpszMenuName = NULL; // no menu windowClass.lpszClassName = "GLClass"; windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // windows logo small icon // register the windows class if (!RegisterClassEx(&windowClass)) return 0; dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style dwStyle = WS_OVERLAPPEDWINDOW; // Windows Style AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); // Adjust Window To True Requested Size // class registered, so now create our window hwnd = CreateWindowEx(NULL, // extended style "GLClass", // class name "BOGLGP - Chapter 2 - OpenGL Application", // app name dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, // x,y coordinate windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, // width, height NULL, // handle to parent NULL, // handle to menu hInstance, // application instance NULL); // no extra params hDC = GetDC(hwnd); // check if window creation failed (hwnd would equal NULL) if (!hwnd) return 0; ShowWindow(hwnd, SW_SHOW); // display the window UpdateWindow(hwnd); // update the window while (true) { } return 1; }
2、接下来是利用OpenGL API创建渲染环境并绘图
这个主要是利用了一个渲染类,渲染类中的程序大都在OpenGL基础中讲解过
下面就看OpenGL的渲染应该写在什么地方
OpenGL程序写在创建完窗体程序后的一个循环中。代码如下:

g_glRender->Init(); while (!exiting) { g_glRender->Prepare(0.0f); g_glRender->Render(); SwapBuffers(hDC); while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (!GetMessage(&msg, NULL, 0, 0)) { exiting = true; break; } TranslateMessage(&msg); DispatchMessage(&msg); } } delete g_glRender;
那么设备环境与渲染环境是如何协调工作的呢?
这个是在创建窗体之初(还未display)时设定的,写在了窗体的事件回调函数中MainWindowProc

switch (uMsg) { case WM_CREATE: // window creation hDC = GetDC(hWnd); //设备环境句柄 SetupPixelFormat(hDC); //设定设备环境像素格式 hRC = wglCreateContext(hDC); //创建渲染环境 但返回值是一个句柄 而不是渲染环境 通过句柄将该渲染环境作为参数传递给其他函数 //创建完渲染环境之后 需要使其生效 wglMakeCurrent(hDC, hRC); break; …… …… }
OK了 ,这一节就写到这儿吧 ,感觉还是没有完全写清楚,但还是貌似明白了不少东西……