作者:i_dovelemon
日期:2017-03-12
来源:CSDN
主题:OpenGL, Render Context
引言
才开始学习OpenGL的同学,大部分都是通过使用GLUT来进行学习的。确实,GLUT为我们简化了很多的工作,使得我们能够专心的学习OpenGL相关的知识,而忽略和操作系统相关的操作。但是,当我们需要自己管理操作系统的相关操作的时候,GLUT的限制就出现了,所以今天,我们就来讲讲如何在不使用GLUT的情况下,在Windows上自行管理窗口,使用OpenGL进行绘制。
渲染上下文(Render Context)
OpenGL的所有调用命令都是在一个渲染上下文中进行的,而创建渲染上下文的API并不是由OpenGL标准来定义,而是由具体的操作系统平台来定义的。如果你想要使用OpenGL进行绘图的操作,你就必须要创建一个绘图上下文,然后通过对应操作系统的API将上下文中绘制的东西显示到屏幕上。
像素格式(Pixel Format)
当我们用OpenGL渲染上下文进行渲染的时候,我们需要为显示的像素指定格式,然后询问操作系统,此种格式是否被支持,如果支持就使用该格式,反之则使用与之相近的格式进行设置。
在windows平台,这个描述像素格式的结果为:PIXELFORMATDESCRIPTOR。
设置像素格式
在Window平台上,绘制图像一般是通过GDI接口中的设备上下文(Device Context, DC)来进行的。所以,如果寻求像素,设置像素格式自然需要通过它来进行。
当我们创建了窗口之后,我们就能够通过GetDC(HWND)函数来获取相对应的设备上下文。后面我们只要通过如下的函数调用来询问,设置像素格式即可:
g_DC = GetDC(g_Hwnd); // Set surface pixel format PIXELFORMATDESCRIPTOR pixel_desc; pixel_desc.nSize = sizeof(pixel_desc); //WORD nSize; pixel_desc.nVersion = 1; //WORD nVersion; pixel_desc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;//DWORD dwFlags; pixel_desc.iPixelType = PFD_TYPE_RGBA; //BYTE iPixelType; pixel_desc.cColorBits = 32; //BYTE cColorBits; pixel_desc.cRedBits = 0;//BYTE cRedBits; pixel_desc.cRedShift = 0;//BYTE cRedShift; pixel_desc.cGreenBits = 0;//BYTE cGreenBits; pixel_desc.cGreenShift = 0;//BYTE cGreenShift; pixel_desc.cBlueBits = 0;//BYTE cBlueBits; pixel_desc.cBlueShift = 0;//BYTE cBlueShift; pixel_desc.cAlphaBits = 0;//BYTE cAlphaBits; pixel_desc.cAlphaShift = 0;//BYTE cAlphaShift; pixel_desc.cAccumBits = 0;//BYTE cAccumBits; pixel_desc.cAccumRedBits = 0;//BYTE cAccumRedBits; pixel_desc.cAccumGreenBits = 0;//BYTE cAccumGreenBits; pixel_desc.cAccumBlueBits = 0;//BYTE cAccumBlueBits; pixel_desc.cAccumAlphaBits = 0;//BYTE cAccumAlphaBits; pixel_desc.cDepthBits = 24;//BYTE cDepthBits; pixel_desc.cStencilBits = 8;//BYTE cStencilBits; pixel_desc.cAuxBuffers = 0;//BYTE cAuxBuffers; pixel_desc.iLayerType = PFD_MAIN_PLANE;//BYTE iLayerType; pixel_desc.bReserved = 0;//BYTE bReserved; pixel_desc.dwLayerMask = 0;//DWORD dwLayerMask; pixel_desc.dwVisibleMask = 0;//DWORD dwVisibleMask; pixel_desc.dwDamageMask = 0;//DWORD dwDamageMask; int pixel_fmt = ChoosePixelFormat(g_DC, &pixel_desc); if (pixel_fmt == 0) { MessageBox(NULL, NULL, L"Error", MB_OK); return; } if (SetPixelFormat(g_DC, pixel_fmt, &pixel_desc) == FALSE) { MessageBox(NULL, NULL, L"Error", MB_OK); return; } int n = GetPixelFormat(g_DC); DescribePixelFormat(g_DC, n, sizeof(pixel_desc), &pixel_desc);
创建渲染上下文
当我们为DC指定了我们想要的像素格式之后,我们就需要通过操作系统提供的函数来创建OpenGL的渲染上下文。在Windows操作系统中,和OpenGL渲染上下文相关的函数都是以wgl开头的,这里我们需要使用的是如下三个函数:wglCreateContext, wglMakeCurrent, wglDeleteContext。
以下是如何使用这些函数的相关代码。
首先,我们需要根据DC创建渲染上下文,并将它设置为当前使用的渲染上下文,如下调用所示:
// Create opengl render context g_GLRC = wglCreateContext(g_DC); if (g_GLRC == NULL) { MessageBox(NULL, NULL, L"Error", MB_OK); return; } wglMakeCurrent(g_DC, g_GLRC);
当我们使用完毕渲染上下文的时候,我们就可以删除它,如下代码:
wglMakeCurrent(g_DC, NULL); wglDeleteContext(g_GLRC);
渲染
当我们创建好渲染上下文,并且将它设置为当前启用的渲染上下文之后,我们就可以通过OpenGL的API调用来渲染我们需要的场景。当我们将OpenGL的渲染命令Flush了之后,我们就可以通过操作系统提供的SwapBuffers函数来将绘制的图像显示在屏幕上。
相关设置
为了能够使用wglCreateContext, wglDeleteContext, wglMakeCurrent这几个函数,我们需要链接Opengl32.lib到我们的项目中去,同时为了使用OpenGL的相关扩展,我们可以手动获取相关的函数,也可以通过GLEW库来实现。我这里通过GLEW来实现。
完整代码
以下是完整的使用该方法进行OpenGL渲染的代码:
#include <Windows.h> #include <GL\glew.h> //-------------------------------------------------------------------------------------- // Variables //-------------------------------------------------------------------------------------- HWND g_Hwnd; HDC g_DC; HGLRC g_GLRC; //-------------------------------------------------------------------------------------- // Function Declaration //-------------------------------------------------------------------------------------- void opengl_init(); void opengl_run(); void opengl_destroy(); LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); //-------------------------------------------------------------------------------------- // Function Definition //-------------------------------------------------------------------------------------- int _stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPreInstance, LPSTR cmdLine, int nShowCmd) { // Register window class WNDCLASSEX wnd; wnd.cbClsExtra = 0; wnd.cbSize = sizeof(wnd); wnd.cbWndExtra = NULL; wnd.hbrBackground = HBRUSH(COLOR_WINDOW + 1); wnd.hCursor = LoadCursor(NULL, IDC_ARROW); wnd.hIcon = NULL; wnd.hIconSm = NULL; wnd.hInstance = hInstance; wnd.lpfnWndProc = WndProc; wnd.lpszClassName = L"OpenGLRC"; wnd.lpszMenuName = NULL; wnd.style = CS_HREDRAW | CS_VREDRAW; if (!RegisterClassEx(&wnd)) { MessageBox(NULL, NULL, L"Fuck you", MB_OK); return -1; } // Create window RECT client_rect = {0, 0, 800, 600}; AdjustWindowRect(&client_rect, WS_OVERLAPPEDWINDOW, FALSE); g_Hwnd = CreateWindowEx(0, L"OpenGLRC", L"OpenGL", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT , client_rect.right - client_rect.left, client_rect.bottom - client_rect.top, NULL, NULL, hInstance, NULL); if (g_Hwnd == NULL) { int err = GetLastError(); MessageBox(NULL, NULL, L"Error", MB_OK); return -1; } ShowWindow(g_Hwnd, nShowCmd); opengl_init(); // Message loop MSG msg = {0}; while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { opengl_run(); } } opengl_destroy(); return 0; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, msg, wParam, lParam); } return 0; } void opengl_init() { g_DC = GetDC(g_Hwnd); // Set surface pixel format PIXELFORMATDESCRIPTOR pixel_desc; pixel_desc.nSize = sizeof(pixel_desc); //WORD nSize; pixel_desc.nVersion = 1; //WORD nVersion; pixel_desc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;//DWORD dwFlags; pixel_desc.iPixelType = PFD_TYPE_RGBA; //BYTE iPixelType; pixel_desc.cColorBits = 32; //BYTE cColorBits; pixel_desc.cRedBits = 0;//BYTE cRedBits; pixel_desc.cRedShift = 0;//BYTE cRedShift; pixel_desc.cGreenBits = 0;//BYTE cGreenBits; pixel_desc.cGreenShift = 0;//BYTE cGreenShift; pixel_desc.cBlueBits = 0;//BYTE cBlueBits; pixel_desc.cBlueShift = 0;//BYTE cBlueShift; pixel_desc.cAlphaBits = 0;//BYTE cAlphaBits; pixel_desc.cAlphaShift = 0;//BYTE cAlphaShift; pixel_desc.cAccumBits = 0;//BYTE cAccumBits; pixel_desc.cAccumRedBits = 0;//BYTE cAccumRedBits; pixel_desc.cAccumGreenBits = 0;//BYTE cAccumGreenBits; pixel_desc.cAccumBlueBits = 0;//BYTE cAccumBlueBits; pixel_desc.cAccumAlphaBits = 0;//BYTE cAccumAlphaBits; pixel_desc.cDepthBits = 24;//BYTE cDepthBits; pixel_desc.cStencilBits = 8;//BYTE cStencilBits; pixel_desc.cAuxBuffers = 0;//BYTE cAuxBuffers; pixel_desc.iLayerType = PFD_MAIN_PLANE;//BYTE iLayerType; pixel_desc.bReserved = 0;//BYTE bReserved; pixel_desc.dwLayerMask = 0;//DWORD dwLayerMask; pixel_desc.dwVisibleMask = 0;//DWORD dwVisibleMask; pixel_desc.dwDamageMask = 0;//DWORD dwDamageMask; int pixel_fmt = ChoosePixelFormat(g_DC, &pixel_desc); if (pixel_fmt == 0) { MessageBox(NULL, NULL, L"Error", MB_OK); return; } if (SetPixelFormat(g_DC, pixel_fmt, &pixel_desc) == FALSE) { MessageBox(NULL, NULL, L"Error", MB_OK); return; } int n = GetPixelFormat(g_DC); DescribePixelFormat(g_DC, n, sizeof(pixel_desc), &pixel_desc); // Create opengl render context g_GLRC = wglCreateContext(g_DC); if (g_GLRC == NULL) { MessageBox(NULL, NULL, L"Error", MB_OK); return; } wglMakeCurrent(g_DC, g_GLRC); // Init glew glewInit(); glClearColor(0.0f, 1.0f, 0.0f, 1.0f); } void opengl_run() { GLfloat clear_c[4] = {rand() % 100 / 100.0f, rand() % 100 / 100.0f ,rand() % 100 / 100.0f, 1.0f}; glClearColor(clear_c[0], clear_c[1], clear_c[2], 1.0f); glClear(GL_COLOR_BUFFER_BIT); glFlush(); SwapBuffers(g_DC); } void opengl_destroy() { wglMakeCurrent(g_DC, NULL); wglDeleteContext(g_GLRC); ReleaseDC(g_Hwnd, g_DC); }
总结
有了这个方法,我们就能够自行的管理窗口相关的操作,接下来我将根据这个方法来修改GLB代码库,以便于后面对DX11进行支持。