1、基于MFC的OpenGL程序

首先,使用的库是GLUT以及GLAUX,先下载两者,添加查找路径以及链接

 
一、单文本文件  
工程openGLMFC
1、创建单文本文件
 
1、基于MFC的OpenGL程序
2、添加路径、链接
方法如之前篇章所示,
链接库为opengl32.lib ;glu32.lib ;glut32.lib ;glaux.lib
 
3、头文件
在stdafx.h中加入下列语句:
//openGL 所需要的头文件
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#include <gl/glaux.h>
4、设置窗口风格
将窗口风格设为WS_CLIPCHILDREN和 WS_CLIPSIBLINGS,从而避免OpenGL绘制到其他窗口中去。这些应该放在PreCreateWindow()中。
BOOL CopenGLMFCView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
return CView::PreCreateWindow(cs);
}
5、变量、函数声明
初始化GL,考虑到使用设备上下文、绘图上下文,
先在openGLMFCView.h中声明需要的变量以及函数
HGLRC m_hRC;    //RC 绘图上下文
CDC* m_pDC; //DC 设备上下文
BOOL InitializeOpenGL(); //初始化 OpenGL
BOOL SetupPixelFormat(); //为 DC 建立像素格式
6、创建消息响应函数
初始化需要在WM_CREATE触发
资源释放在WM_DESTROY触发
窗口大小变化时 WM_SIZE触发,需要调整绘图
由于使用opengl绘制背景,故不需要窗口自己在绘制背景了,需要改写 WM_ERASEBACKGROUND消息触发的事件
 
故,打开classWizard,在openGLMFCView类中添加上面四个消息响应函数

OnCreate   OnDestroy  OnSize  OnEraseBkground

 
7、初始化

将通过建立像素格式和绘制上下文来初始化OpenGL. 在InitializeOpenGL()中会创建一个设备上下文(DC),为这个DC选择一个像素格式,创建和这个DC相关的绘制上下文(RC),然后选择这个RC.这个函数会调用SetupPixelFormat()来建立像素格式。

int CopenGLMFCView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1; // TODO: Add your specialized creation code here
InitializeOpenGL();
return 0;
} BOOL CopenGLMFCView::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;
}
//背景颜色
::glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
//深度缓存 1最大,让任何都能显示出来
::glClearDepth(1.0f);
//如果通过比较后深度值发生变化了,会进行更新深度缓冲区的操作
::glEnable(GL_DEPTH_TEST);
return TRUE;
}
//建立像素格式
/////////////////////////////////////////////////////////////////////////////
BOOL CopenGLMFCView::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;
}
8、绘制场景
在绘制场景时,一般包括如下步骤:1)清空缓存。2)绘制场景。3)Flush掉渲染流水线。4)若设置了双缓冲,则交换前后台缓冲区。
void CopenGLMFCView::OnDraw(CDC* /*pDC*/)
{
CopenGLMFCDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return; // TODO: add draw code for native data here // 清除颜色、深度缓存
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //可以添加渲染函数 // Flush掉渲染流水线
::glFinish();
// 交换前后缓存区
::SwapBuffers(m_pDC->GetSafeHdc()); }

9、背景绘制修改

 

试试改变窗口的大小,你会看到很严重的闪烁,并且关闭程序后会报告内存泄露,因此我们这就来解决这两个问题吧。

发生闪烁的原因是Windows先绘制背景,然后再是OpenGL绘制,因为我们已经让OpenGL负责清空背景色,因此我们不需要Windows去清空背景了

BOOL CopenGLMFCView::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default return TRUE;//CView::OnEraseBkgnd(pDC);
}

内存泄露的原因是我们在InitializeOpenGL()中使用了new运算符来为CClientDC对象分配内存,因此需要显示delete掉。

void CopenGLMFCView::OnDestroy()
{
CView::OnDestroy(); // TODO: Add your message handler code here
if (::wglMakeCurrent(0, 0) == FALSE)
{
MessageBox(L"Could not make RC non-current");
} //Delete the rendering context
if (::wglDeleteContext(m_hRC) == FALSE)
{
MessageBox(L"Could not delete RC");
}
//Delete the DC
if (m_pDC)
{
delete m_pDC;
}
//Set it to NULL
m_pDC = NULL;
}
10、大小调整
在OnSize()中一般用来设置视口和视锥,因为这些是和窗口大小相关的。基本操作包括设置视口,选择投影矩阵,设置模型视图矩阵。
void CopenGLMFCView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy); // TODO: Add your message handler code here
GLdouble aspect_ratio; // width/height ratio if (0 >= cx || 0 >= cy)
{
return;
}
// select the full client area
::glViewport(0, 0, cx, cy);
// compute the aspect ratio
// this will keep all dimension scales equal
aspect_ratio = (GLdouble)cx / (GLdouble)cy;
// select the projection matrix and clear it
::glMatrixMode(GL_PROJECTION);
::glLoadIdentity();
// select the viewing volume
::gluPerspective(45.0f, aspect_ratio, .01f, 200.0f); // switch back to the modelview matrix and clear it
::glMatrixMode(GL_MODELVIEW);
::glLoadIdentity();
}

通过上面得到运行结果:

1、基于MFC的OpenGL程序

二、基于对话框

 
工程:openGLMFCDialog
1-3步骤同上
4、变量函数声明
在openGLMFCDialogDlg.h文件中,声明如上。
其余步骤同上,不同点在于,需要将OnDraw里面的写到
OnPaint函数里面,其中IsIconic()是判断是否为最小化,可以放到else里面即可。
得到结果
1、基于MFC的OpenGL程序
 
上一篇:java-告诉addItemListener()时,JComboBox抛出NullPointerException


下一篇:SharePoint REST api