录十六

持之以恒

创建OpenGL窗口

本片博文主要是为了在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;

五、运行结果

编译链接,运行后会显示一个深蓝色的窗口,如下:

4.jpg

源码下载: OpenGLWindow

发表评论:

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

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