作者:i_dovelemon
日期:2017-03-12
来源:
主题: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进行支持。
转载请注明原文地址: https://ju.6miu.com/read-32892.html