【OpenGL】-001 VS2015 MFC下配置OpenGL
最近在看《OpenGL SuperBiber》,该书示例代码是GLFW+OpenGL实现的,窗口系统采用了GLFW。虽然GLFW是一个优秀的窗口管理系统,但由于我更熟悉MFC,所以希望将该书的代码移植到VS2015 MFC下。
1、安装GLEW
1.1 下载GLEW
GLEW(OpenGL Extension Wrangler Library)是一个用C/C++实现的可移植的OpenGL库。由于微软官方支持的OpenGL版本只持续到1.1,而目前OpenGL已经发展到4.5到4.6版本了。对于1.1之后OpenGL函数必须使用扩展的形式进行支持。
GLEW下载网址:(https://sourceforge.net/projects/glew/files/glew/2.1.0/)。目前最新版本是2.1.0,可以根据时间从(http://glew.sourceforge.net/) 查询最新版本并下载。
此处有一小坑,Github上有glew的库,但选择master下载下来之后,在src目录下没有需要的glew.c,感觉有点坑爹。
1.2 编译
GLEW中包含windows下的生成工程文件,在build目录下。使用VS2015打开VC12下的glew.sln,升级编译工具即可顺利编译。
1.3 部署
GLEW编译完之后,根据各人爱好放置需要的.h,.lib..dll即可。网上很多人建议将相关文件丢到system32等系统目录下,而我喜欢将这些放置到当前工程能够通过相对目录访问的目录下,通常我放在解决方案的lib/include目录,将dll放到解决方案的输出目录下,这样,不至于因为单个解决方案污染整个操作系统的相关文件。
2、MFC下使用OpenGL
这里,我选择在MFC的对话框程序中使用OpenGL。
2.1 新建对话框程序
按照传统套路,一路默认建立MFC对话框程序即可。
2.2 引入OpenGL头文件和库文件
在stdafx.h中加入以下代码:
#include "GL/glew.h"
#ifdef _DEBUG
#pragma comment(lib,"./lib/Debug/Win32/glew32d.lib")
#else
#pragma comment(lib,"./lib/Release/Win32/glew32.lib")
#endif
#include <gl/GL.h>
#include <gl/GLU.h>
#include <gl/glaux.h>
#pragma comment(lib,"OpenGL32.lib")
#pragma comment(lib,"GLu32.lib")
#pragma comment(lib,"GLaux.lib")
注意,在包含glew.h之前不能包含GL.h。
2.3 加入必须的变量
在对话框中添加成员变量:
HGLRC m_hRC; //RC 绘图上下文
CDC* m_pDC; //DC 设备上下文
其中m_hRC是OpenGL渲染使用的绘图上下文,m_pDC是渲染窗口的设备上下文。
2.4 初始化OpenGL
使用OpenGL进行渲染之前,需要对OpenGL进行初始化。
BOOL COpenGL3DDlg::InitializeOpenGL()
{
//客户区获得DC
m_pDC = new CClientDC(this);
//Failure to Get DC
if (m_pDC == NULL)
{
MessageBox(L"Error Obtaining DC");
return FALSE;
}
//为DC建立像素格式
if (!SetupPixelFormat())
{
return FALSE;
}
//创建 RC
m_hRC = ::wglCreateContext(m_pDC->GetSafeHdc());
//Failure to Create Rendering Context
if (m_hRC == 0)
{
MessageBox(L"Error Creating RC");
return FALSE;
}
//设定OpenGL当前线程的渲染环境。
//以后这个线程所有的OpenGL调用都是在这个hdc标识的设备上绘制。
if (::wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC) == FALSE)
{
MessageBox(L"Error making RC Current");
return FALSE;
}
GLenum err = glewInit();
if (GLEW_OK != err)
{
/* Problem: glewInit failed, something is seriously wrong. */
fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
}
//背景颜色
::glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//深度缓存 1最大,让任何都能显示出来
::glClearDepth(1.0f);
//如果通过比较后深度值发生变化了,会进行更新深度缓冲区的操作
::glEnable(GL_DEPTH_TEST);
注意,使用GLEW库的函数之前需要对GLEW进行初始化,调用glewInit
函数并检查返回值,确定glew库初始化状态。
BOOL COpenGL3DDlg::SetupPixelFormat()
{
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
16, // 16-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int m_nPixelFormat = ::ChoosePixelFormat(m_pDC->GetSafeHdc(), &pfd);
if (m_nPixelFormat == 0)
{
return FALSE;
}
if (::SetPixelFormat(m_pDC->GetSafeHdc(), m_nPixelFormat, &pfd) == FALSE)
{
return FALSE;
}
return TRUE;
}
2.5 渲染与界面更新
在MFC对话框程序中,绘制操作一般都放置于OnPaint函数中,
void COpenGL3DDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
//CDialogEx::OnPaint();
// 清除颜色、深度缓存
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//可以添加渲染函数
g_oglobj.Render();
// Flush掉渲染流水线
::glFinish();
// 交换前后缓存区
::SwapBuffers(m_pDC->GetSafeHdc());
}
}
在这里调用OpenGL的渲染相关操作。先在BACK缓冲区绘制再交换buffer到前台。
2.6 背景擦除
由于窗口的背景由OpenGL进行擦除,因此可以将MFC的GDI擦除背景操作屏蔽,使其直接返回TRUE即可。
BOOL COpenGL3DDlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
return TRUE;
// return CDialogEx::OnEraseBkgnd(pDC);
}