具有Windows编程经验的人都知道,在Windows下用GDI作图必须通过设备上下文(DeviceContext简写DC)调用相应的函数;用OpenGL作图也是类似,OpenGL函数是通过"渲染上下文"(RenderingContext简写RC)完成三维图形的绘制
一、链接OpenGL库。
在创建OpenGL设备上下文之前,必须先引入项目所依赖的opengl32库。在这里按照下面的步骤即可:
1、打开【解决方案资源管理器】,在项目名称上右击,从菜单中找到【属性】,打开项目属性对话框,展开配置属性,依次选择【连接器】—【输入】。如下图所示:
2、在项目属性对话框的右侧,单击依赖附加项编辑框,从弹出的菜单中选择【编辑】,打开依赖附加项对话框,在编辑框中输入"openg32.lib"。如下图所示:
3、最后依次点击确定按钮返回,这样我们便就成功引入了开发所需要的OpenGL库。
二、创建OpenGL渲染上下文
创建RC的过程,大致需要以下几个步骤:
(1)获取窗口的DC
(2)初始化窗口像素格式的对象结构体
(3)检查素格式是否与窗口的DC匹配
(4)设置窗口DC的象素格式
(5) 创建OpenGL渲染上下文hRC
(6) 将RC与窗口的DC关联起来
在windows上创建OpenGL渲染上下文,不管谁来写代码,这些步骤都是固定的,我们需要做的就是按照步骤,一步步实现就行了。先在源文件中定义一个创建渲染上下文的函数,该函数带有两个参数,窗口句柄HWND和色彩位深,返回值类型BOOL,创建RC成功返回TRUE,否则返回FALSE,函数定义如下:
BOOL createGLContext(HWND hWnd, int bits) { return TRUE; }
下面按照这六个步骤,一步步来完成OpenGL渲染上下文的创建。首先定义两个全局变量,窗口DC和OpenGL设备上下文:
//窗口DC HDC g_hDC = NULL; //OpenGL设备上下文hRC HGLRC g_hRC = NULL;
1、获取窗口的DC。
根据窗口句柄获取DC很简单,调用Windows函数GetDC即可,代码如下:
//1. 获取设备的DC g_hDC = GetDC(hWnd); if (!g_hDC) { return FALSE; }
2、初始化窗口像素格式的对象结构体。
窗口像素格式的结构体为PIXELFORMATDESCRIPTOR ,从MSDN中查询到它的定义如下:
// pfd typedef struct tagPIXELFORMATDESCRIPTOR { WORD nSize; WORD nVersion; DWORD dwFlags; BYTE iPixelType; BYTE cColorBits; BYTE cRedBits; BYTE cRedShift; BYTE cGreenBits; BYTE cGreenShift; BYTE cBlueBits; BYTE cBlueShift; BYTE cAlphaBits; BYTE cAlphaShift; BYTE cAccumBits; BYTE cAccumRedBits; BYTE cAccumGreenBits; BYTE cAccumBlueBits; BYTE cAccumAlphaBits; BYTE cDepthBits; BYTE cStencilBits; BYTE cAuxBuffers; BYTE iLayerType; BYTE bReserved; DWORD dwLayerMask; DWORD dwVisibleMask; DWORD dwDamageMask; } PIXELFORMATDESCRIPTOR;
看完之后,大家发现该结构体参数较多,但是目前我们只要需要关注色彩位深(cColorBits)和 颜色类型(iPixelType)两个参数即可,其它参数无需关注,随着OpenGL学习的深入,大家会慢慢理解这些参数的作用。色彩位深通过函数参数传递进来, 颜色类型我们选择RGBA,最终完成结构体如下:
//2. 初始化窗口像素格式的对象结构体 static PIXELFORMATDESCRIPTOR pfd= { sizeof(PIXELFORMATDESCRIPTOR), // 上述格式描述符的大小 1, // 版本号 PFD_DRAW_TO_WINDOW | // 格式支持窗口 PFD_SUPPORT_OPENGL | // 格式必须支持OpenGL PFD_DOUBLEBUFFER, // 必须支持双缓冲 PFD_TYPE_RGBA, // 申请 RGBA 格式 bits, // 选定色彩深度 0, 0, 0, 0, 0, 0, // 忽略的色彩位 0, // 无Alpha缓存 0, // 忽略Shift Bit 0, // 无累加缓存 0, 0, 0, 0, // 忽略聚集位 16, // 16位 Z-缓存 (深度缓存) 0, // 无蒙板缓存 0, // 无辅助缓存 PFD_MAIN_PLANE, // 主绘图层 0, // Reserved 0, 0, 0 // 忽略层遮罩 };
3、检查素格式是否与窗口的DC匹配
完成了窗口像素格式的初始化,接下来需要检查我们定义的像素格式与当前的窗口DC是否匹配,这里用到一个函数ChoosePixelFormat,该函数的参数很简单,传递窗口DC和像素格式结构体,匹配成功能返回非零值,否则返回零。代码如下:
//3. 检查素格式是否与窗口的DC匹配 int pixelFormat = ChoosePixelFormat(g_hDC, &pfd); if ( !pixelFormat ) { return FALSE; }
4、设置窗口DC的象素格式
当定义的像素格式与当前的窗口DC匹配成功后,根据检查结果的返回值,调用SetPixelFormat函数,将窗口DC的象素格式设置成我们自己定义的像素格式。代码如下:
//4. 如果匹配成功,设置窗口DC的象素格式 if(!SetPixelFormat(hDC, pixelFormat, &pfd)) { return FALSE; }
5、 创建OpenGL渲染上下文RC
窗口DC的像素格式设置完成后,下面便就是根据窗口的DC,创建我们所需的OpenGL渲染上下文,代码如下:
//5. 创建OpenGL设备上下文hRC g_hRC = wglCreateContext(g_hDC); if (! g_hRC) { return FALSE; }
6) 将RC与窗口的DC关联起来
最后激活所创建的RC,即将RC与当前窗口的DC关联起来。这样后面所有的OpenGL函数都是在该渲染上下文下进行绘图,从这里我们可以看出,OpenGL只能有一个激活的渲染上下文,而且OpenGL函数仅仅在当前激活的渲染上下文中绘图。代码如下:
//6. 将hRC与窗口的hDC关联起来。 if(!wglMakeCurrent(g_hDC, g_hRC)) { return FALSE; }
到此,我们的OpenGL渲染上下文创建完成了,完整的代码实现如下:
//创建opengl设备上下文 BOOL createGLContext(HWND hWnd, int bits) { //1. 获取设备的DC g_hDC = GetDC(hWnd); if (!g_hDC) { return FALSE; } //2. 初始化窗口像素格式的对象结构体 static PIXELFORMATDESCRIPTOR pfd= { sizeof(PIXELFORMATDESCRIPTOR), // 上述格式描述符的大小 1, // 版本号 PFD_DRAW_TO_WINDOW | // 格式支持窗口 PFD_SUPPORT_OPENGL | // 格式必须支持OpenGL PFD_DOUBLEBUFFER, // 必须支持双缓冲 PFD_TYPE_RGBA, // 申请 RGBA 格式 bits, // 选定色彩深度 0, 0, 0, 0, 0, 0, // 忽略的色彩位 0, // 无Alpha缓存 0, // 忽略Shift Bit 0, // 无累加缓存 0, 0, 0, 0, // 忽略聚集位 16, // 16位 Z-缓存 (深度缓存) 0, // 无蒙板缓存 0, // 无辅助缓存 PFD_MAIN_PLANE, // 主绘图层 0, // Reserved 0, 0, 0 // 忽略层遮罩 }; //3. 检查素格式是否与窗口的DC匹配 int pixelFormat = ChoosePixelFormat(g_hDC, &pfd); if ( !pixelFormat ) { return FALSE; } //4. 如果匹配成功,设置窗口DC的象素格式 if(!SetPixelFormat(g_hDC, pixelFormat, &pfd)) { return FALSE; } //5. 创建OpenGL设备上下文hRC g_hRC = wglCreateContext(g_hDC); if (! g_hRC) { return FALSE; } //6. 将hRC与窗口的hDC关联起来。 if(!wglMakeCurrent(g_hDC, g_hRC)) { return FALSE; } return TRUE; }
三、销毁OpenGL设备上下文
销毁过程比较简单。须先释放OpenGL设备上下文,再销毁。最后再释放之前获取的窗口DC。这样才可以保证整个过程中,没有任何的资源的泄露。
//销毁opengl设备上下文 void destroyGLContext(HWND hWnd) { if (g_hRC) { //释放OpenGL设备上下文hRC if (wglMakeCurrent(NULL,NULL)) { //删除OpenGL设备上下文hRC if (wglDeleteContext(g_hRC)) { g_hRC = NULL; } } } if(g_hDC) { //释放DC ReleaseDC(hWnd, g_hDC); g_hDC = NULL; } }
四、函数调用
修改window_proc函数,调用我们前面实现的OpenGL设备上下文的创建和销毁函数。
LRESULT CALLBACK window_proc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam) { LRESULT ret=0; switch(msg) { case WM_CREATE: { g_windows_hwnd=hWnd; // 保存窗口句柄 if(!createGLContext(hWnd, 32)) { //创建失败,这里输出错误信息。 } } break; case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd,&ps);//取得设备内容句柄 //添加Window绘图代码…… EndPaint(hWnd,&ps);//释放句柄 } break; case WM_DESTROY: { destroyGLContext(hWnd); ::PostQuitMessage(0); // 窗口退出消息 } break; default: ret = ::DefWindowProc(hWnd,msg,wParam,lParam); } return ret; }
源码下载:CreateGLContext