本片博文主要是为了在Windows环境下,创建一个空的OpenGL窗口,并且可以使用指定的颜色清除窗口背景。
一、OpenGL窗口类定义
为了方便后续的代码维护和使用,可以将OpenGL窗口常用的函数封装成MCOpenGLWindow类。在这个类中,先定义OpenGL窗口初始化、窗口大小设置、场景绘制、以及窗口销毁等几个最常用的函数。完成后的类定义如下:
class MCOpenGLWindow { public: MCOpenGLWindow(); virtual ~MCOpenGLWindow(); //初始化OpenGL窗口 void InitGL(); //设置OpenGL窗口大小 void ReSizeGLScene(int width, int height); //绘制OpenGL场景 void DrawGLScene(); //销毁OpenGL窗口 void KillGLWindow(); };
为了在MCOpenGLWindow类的实现中,使用OpenGL函数,所以需要引入OpenGL头文件:
//OpenGL头文件 #include <gl\GL.h>
但仅仅引入OpenGL头文件是不够的,它依赖Windows系统的一些定义,因此还必须在OpenGL头文件之前引入Windows头文件。因此完整的MCOpenGLWindow.cpp如下:
#include "MCOpenGLWindow.h" #include <windows.h> #include <gl\GL.h> MCOpenGLWindow::MCOpenGLWindow() { } MCOpenGLWindow::~MCOpenGLWindow() { } void MCOpenGLWindow::InitGL() { } void MCOpenGLWindow::ReSizeGLScene(int width, int height) { } void MCOpenGLWindow::DrawGLScene() { } void MCOpenGLWindow::KillGLWindow() { }
接着在主程序的代码中,定义一个OpenGL窗口对象的全局变量,如下:
//OpenGL窗口 MCOpenGLWindow g_OGLWindow;
二、清除屏幕背景
1、设置OpenGL窗口大小
在清除窗口背景之前,必须使用glViewport函数先设置OpenGL窗口的大小,它的函数原型如下:
void glViewport(GLint x,GLint y,GLsizei w,GLsizei h)
x,y—指定了视口的左下角位置,以像素为单位。
w,h—表示视口矩形的宽度和高度,以像素为单位。
void MCOpenGLWindow::ReSizeGLScene(int width, int height) { //设置OpenGL窗口大小 glViewport(0, 0, width, height); }
2、设置清屏时的颜色
设置清除屏幕的颜色,需要使用OpenGL函数,它函数的原型如下:
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
参数类型为float,其中red、green、blue、alpha依次为颜色的红、绿、蓝分量和透明度。
这里可以使用使用深蓝色清除窗口背景,颜色值为:R=51,G=51,B=153,转换成float类型后为:R=0.2,G=0.2,B=0.6。初始化的完整代码如下:
void MCOpenGLWindow::InitGL() { //设置清屏时使用的颜色 glClearColor(0.2f, 0.2f, 0.6f,1.0f); }
3、清除窗口背景
void glClear(GLbitfield mask);
参数mask表示需要清除的缓冲区标志位,可以使用以下标志位:
GL_COLOR_BUFFER_BIT:颜色缓冲
GL_DEPTH_BUFFER_BIT:深度缓冲
GL_ACCUM_BUFFER_BIT:累积缓冲
GL_STENCIL_BUFFER_BIT: 模板缓冲
也可以使用位运算符"|" ,一次清除多个缓冲区。例如:glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)表示要清除颜色缓冲以及深度缓冲,
在每一帧绘图之前,为了防止缓冲区中原有的颜色信息影响本次绘图,所以必须使用指定的颜色清除整个颜色缓冲区。当然,这个颜色值就是初始化时,调用glClearColor所设置的颜色。绘制场景的代码如下:
void MCOpenGLWindow::DrawGLScene() { //使用之前设置的颜色,清除OpenGL窗口的背景 glClear(GL_COLOR_BUFFER_BIT); //以下是场景绘制代码…… }
三、窗口消息循环
Windows的WM_PAINT绘图消息是一个被动触发的消息,只有当改变窗口大小,窗口被遮挡,或者去除遮挡时,才会触发WM_PAINT消息。但是OpenGL绘图一般是循环的双缓冲绘图机制,需要不断的调用DrawGLScene()函数绘制场景。因此需要对原有的消息循环稍作调整,调整后的消息循环机制如下:
当系统有消息等待处理时,先完成系统消息处理,发现是WM_WM_QUIT消息,则退出消息循环;如果没有消息需要处理时,则调用DrawGLScene()函数绘制场景。
下面是新的消息循环代码,其中有详细的注释,这里不再赘述:
int run() { MSG msg; //4. 显示窗口 ShowWindow(g_windows_hwnd,SW_SHOW|SW_NORMAL); //5. 消息循环 BOOL done = FALSE; while(!done) { //如果有消息需要处理,则取出消息,并从消息队列中移除 if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { //判断取出的消息,是否是退出消息, if (msg.message==WM_QUIT) { //置为TRUE,退出while消息循环 done=TRUE; } else { TranslateMessage(&msg); // 转换加速键消息 DispatchMessage(&msg); // 分发消息到window pro } } else { //如果没有消息需要处理,则绘制OpenGL场景 g_OGLWindow.DrawGLScene(); //将渲染完成的后台缓冲区,交换至前台显示 SwapBuffers(g_hDC); } } return (int)msg.wParam; }
四、OpenGL窗口调用
OpenGL设备上下文创建成功后,可以初始化OpenGL窗口
case WM_CREATE: { g_windows_hwnd=hWnd; // 保存窗口句柄 if(createGLContext(hWnd, 32)) { //OpenGL窗口初始化 g_OGLWindow.InitGL(); } else { //创建失败,这里输出错误信息。 } } break;
在window_pro函数中处理WM_SIZE消息,在窗口大小发生改变时,重新设置OpenGL窗口的大小:
case WM_SIZE: { int width = LOWORD(lParam); int height = HIWORD(lParam); //设置OpenGL窗口大小 g_OGLWindow.ReSizeGLScene(width, height); } break;
在销毁OpenGL设备上下文之前,必须先销毁OpenGL窗口:
case WM_DESTROY: { //销毁OpenGL窗口 g_OGLWindow.KillGLWindow(); destroyGLContext(hWnd); ::PostQuitMessage(0); // 窗口退出消息 } break;
由于OpenGL绘图都是整个窗口刷新,不存在局部刷新的问题。因此Window中擦除窗口背景这一步,对于OpenGL来说是多余的。所以在接收到WM_ERASEBKGND消息时,可以直接返回。这样就屏蔽掉了系统默认的处理方式,提高了绘图效率。
case WM_ERASEBKGND: { //处理背景擦除消息,屏蔽系统默认处理方式 } break;
五、运行结果
编译链接,运行后会显示一个深蓝色的窗口,如下:
源码下载: OpenGLWindow