录十六

持之以恒

创建OpenGL设备上下文

具有Windows编程经验的人都知道,在Windows下用GDI作图必须通过设备上下文(DeviceContext简写DC)调用相应的函数;用OpenGL作图也是类似,OpenGL函数是通过"渲染上下文"(RenderingContext简写RC)完成三维图形的绘制

一、链接OpenGL库。

在创建OpenGL设备上下文之前,必须先引入项目所依赖的opengl32库。在这里按照下面的步骤即可:

1、打开【解决方案资源管理器】,在项目名称上右击,从菜单中找到【属性】,打开项目属性对话框,展开配置属性,依次选择【连接器】—【输入】。如下图所示:

6.jpg

2、在项目属性对话框的右侧,单击依赖附加项编辑框,从弹出的菜单中选择【编辑】,打开依赖附加项对话框,在编辑框中输入"openg32.lib"。如下图所示:

7.jpeg

3、最后依次点击确定按钮返回,这样我们便就成功引入了开发所需要的OpenGL库。

二、创建OpenGL渲染上下文

创建RC的过程,大致需要以下几个步骤:

  1. (1)获取窗口的DC

  2. (2)初始化窗口像素格式的对象结构体

  3. (3)检查素格式是否与窗口的DC匹配

  4. (4)设置窗口DC的象素格式

  5. (5) 创建OpenGL渲染上下文hRC

  6. (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

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

Copyright © 1999-2019, lu16.com, All Rights Reserved