本文主要介绍如何在MFC框架下编写OpenGL程序。现有的OpenGL参考书多是介绍基于AUX库,或是基于GLUT库的Win32 OpenGL程序。这类程序结构非常简单,主要目的是让读者熟悉OpenGL语言的用法及功能。然而不得不承认,编写可视化程序MFC框架有其独到的优势,本文就是针对这种需求,详细介绍MFC框架下OpenGL程序的实现步骤。
编译器:Microsoft Visual Studio.NET 2003(中文版)
准备工作:下载OpenGL非标准额外GLUT库文件http://www.openglsource.com/download/download.htm(包括glut.h, glut32.lib, glut32.dll)。将glut.h放到目录VC2003.NET/Vc7/PlatformSDK/Include/gl下,glut32.lib放到目录VC2003.NET/Vc7/PlatformSDK/Lib下,再将glut32.dll放进WINDOWS/system32中,最后设置编译器的链接:【项目】-【属性页】-【链接器】-【输入】-【附加依赖项】-glut32.lib。
具体实现步骤:
1. 新建MFC多文档应用程序,项目名取为Render。
2. 新建两个文件WSDView.h和WSDView.cpp,并将这两个文件放入项目文件夹内。具体代码见附录。两文件的功能:创建类CWSDView,它继承自CView,成员函数包括设置像素格式,创建渲染描述表,各类鼠标消息相应函数如鼠标左键拖动,右键拖动及滚轴转动等。
3. 【文件】-【添加现有项】,选取WSDView.h, WSDView.cpp文件。
4. 在RenderView.h文件中添加代码 #include "WSDView.h",
将class CRenderView : public CView修改为class CRenderView : public CWSDView
在RenderView.cpp文件中修改IMPLEMENT_DYNCREATE(CRenderView, CView)为IMPLEMENT_DYNCREATE(CRenderView, CWSDView);修改 BEGIN_MESSAGE_MAP(CRenderView, CView)为BEGIN_MESSAGE_MAP(CRenderView, CWSDView)
5. 在CRenderView类中添加虚拟函数virtual void RenderScene(void)。实际上它是继承自父类CWSDView。
6. 在CRenderView::OnDraw(CDC* /*pDC*/)内添加代码:
// TODO: 在此处为本机数据添加绘制代码
//双缓存应用
static BOOL bBusy = FALSE;
if(bBusy) return;
bBusy = TRUE;
//背景色
glClearColor( 0.0f , 0.0f , 0.0f , 1.0f );
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清缓存
glMatrixMode(GL_MODELVIEW);//启动模型矩阵
glLoadIdentity();//初始化模型矩阵
// 绘制动作
glTranslatef(0.0, 0.0, -8.0); //将场景移至可视区
RenderScene();//绘制场景
glFinish();//完成绘制
SwapBuffers(wglGetCurrentDC());//双缓存应用:更新缓存
bBusy = FALSE;
7. 在CRenderView::RenderScene(void)内添加代码:
glPushMatrix();
//控制操作如下,与鼠标消息相应函数相关:
glTranslatef(m_translateX, m_translateY, 0.0);
glRotatef(rotate_x, 1.0, 0.0, 0.0);
glRotatef(rotate_y, 0.0, 1.0, 0.0);
glScalef(m_scale, m_scale, m_scale);
//绘制实体 例如茶壶
glPushMatrix();
glColor 3f (0.5, 0.5, 0.5);
glutSolidTeapot(0.3);
glPopMatrix();
glPopMatrix();
Ok,至此基本框架已经搞定。按照上述步骤,编译运行显示结果:黑色背景下的灰色茶壶。用户使用鼠标达到与场景交互的目的,左键的拖动实现茶壶的旋转,右键拖动可使茶壶平移,滚轴转动则具有放大或缩小的功能。更复杂的OpenGL程序都可基于此框架完成,例如读取几何数据模型文件则可在CRenderDoc类中实现,模型显示代码主要在CRenderView类中的RenderScene()函数中添加,其他功能可根据作者需求适当修改添加,在此不再唠叨。
附录:
WSDView.h:
#if !defined(AFX_GLVIEW_H__3CA5EA 8F _BFF7_ 43F 2_B571_2385D95E5006__INCLUDED_)
#define AFX_GLVIEW_H__3CA5EA 8F _BFF7_ 43F 2_B571_2385D95E5006__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif
#include "gl/glut.h" //添加glut库
#pragma comment(lib, "Opengl32.lib")
#pragma comment(lib, "glu32.lib")
//#pragma comment(lib, "glut32.lib")
class CWSDView : public CView
{
protected: // 仅从序列化创建
CWSDView();
DECLARE_DYNCREATE(CWSDView)
// 属性
public:
// 操作
public:
// 重写
public:
virtual void OnDraw(CDC* pDC); // 重写以绘制该视图
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
// 实现
public:
virtual ~CWSDView();
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
protected:
// 生成的消息映射函数
protected:
DECLARE_MESSAGE_MAP()
public:
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnDestroy();
//像素格式定义函数
bool pixelFormat(void);
// 显示设备环境类变量
CClientDC* my_pDC;
// 测试像素格式的函数
void testPixelFormat(void);
CRect my_oldRect;
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
// 绕X轴旋转角度
double rotate_x;
double rotate_y;
// 记录光标位置
CPoint m_oldPoint;
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);//避免屏幕闪烁
virtual void RenderScene(void);
afx_msg void OnRButtonDown(UINT nFlags, CPoint point);
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
// 沿X方向平移
double m_translateX;
double m_translateY;
// 缩放系数
double m_scale;
};
#endif // !defined(AFX_GLVIEW_H__3CA5EA 8F _BFF7_ 43F 2_B571_2385D95E5006__INCLUDED_)
WSDView.cpp:
// WSDView.cpp : CWSDView 类的实现
//
#include "stdafx.h"
#include "WSDView.h"
#include "math.h"
#include "./wsdview.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// CWSDView
#define PI 3.1415927
IMPLEMENT_DYNCREATE(CWSDView, CView)
BEGIN_MESSAGE_MAP(CWSDView, CView)
// 标准打印命令
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
ON_WM_CREATE()
ON_WM_SIZE()
ON_WM_DESTROY()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_WM_ERASEBKGND()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_MOUSEWHEEL()
END_MESSAGE_MAP()
// CWSDView 构造/析构
CWSDView::CWSDView()
: my_pDC(NULL)
, rotate_x(0)
, rotate_y(0)
, m_oldPoint(0)
, m_translateX(0)
, m_translateY(0)
, m_scale(1.0)
{
// TODO: 在此处添加构造代码
}
CWSDView::~CWSDView()
{
}
BOOL CWSDView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改 CREATESTRUCT cs 来修改窗口类或
// 样式
cs.style=WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|WS_CLIPSIBLINGS;
return CView::PreCreateWindow(cs);
}
// CWSDView 诊断
#ifdef _DEBUG
void CWSDView::AssertValid() const
{
CView::AssertValid();
}
void CWSDView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
#endif //_DEBUG
// CWSDView 消息处理程序
int CWSDView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
testPixelFormat();//调用该函数,完成渲染描述表的调用
return 0;
}
void CWSDView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// TODO: 在此处添加消息处理程序代码
if(cy > 0)
{
my_oldRect.right = cx;
my_oldRect.bottom = cy;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
//视场调节:
glFrustum(-1.0, 1.0, -1.0*cy/cx, 1.0*cy/cx, 5.0, 25.0);
glViewport(0, 0, cx, cy);
}
RedrawWindow();
}
void CWSDView::OnDestroy()
{
CView::OnDestroy();
// TODO: 在此处添加消息处理程序代码
//删除渲染描述表及绑定的设备描述表
HGLRC hrc;
hrc = ::wglGetCurrentContext();
::wglMakeCurrent(NULL, NULL);
if (hrc)
::wglDeleteContext(hrc);
if (my_pDC)
delete my_pDC;
}
//像素格式定义函数
bool CWSDView::pixelFormat(void)
{
//像素属性的设置
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW |
PFD_SUPPORT_OPENGL| // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA,
24,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
32,
0,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
int pixelformat;
if ( (pixelformat = ChoosePixelFormat(my_pDC->GetSafeHdc(), &pfd)) == 0 )
{
MessageBox("ChoosePixelFormat failed");
return FALSE;
}
if (SetPixelFormat(my_pDC->GetSafeHdc(), pixelformat, &pfd) == FALSE)
{
MessageBox("SetPixelFormat failed");
return FALSE;
}
return TRUE;
}
// 测试像素格式的函数
void CWSDView::testPixelFormat(void)
{
PIXELFORMATDESCRIPTOR pfd;
int n;
HGLRC hrc;
my_pDC = new CClientDC(this);
ASSERT(my_pDC != NULL);
if (!pixelFormat())
return;
n =::GetPixelFormat(my_pDC->GetSafeHdc());
::DescribePixelFormat(my_pDC->GetSafeHdc(), n, sizeof(pfd), &pfd);
hrc = wglCreateContext(my_pDC->GetSafeHdc());
wglMakeCurrent(my_pDC->GetSafeHdc(), hrc);
GetClientRect(&my_oldRect);
glClearDepth( 1.0f );
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
// CWSDView 绘制
//避免屏幕闪烁
BOOL CWSDView::OnEraseBkgnd(CDC* pDC)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
return true;
}
void CWSDView::RenderScene(void)
{
//控制操作如下:
/* glTranslatef(m_translateX, m_translateY, 0.0);
glRotatef(rotate_x, 1.0, 0.0, 0.0);
glRotatef(rotate_y, 0.0, 1.0, 0.0);
glScalef(m_scale, m_scale, m_scale);
//光照
GLfloat lAmb[4];
GLfloat lDif[4];
GLfloat lSpe[4];
GLfloat lPos[4];
lAmb[0]= 0.0f ; lAmb[1]= 0.0f ;
lAmb[2]= 0.0f ; lAmb[3]= 0.0f ;
lDif[0]= 1.0f ; lDif[1]= 0.0f ;
lDif[2]= 0.0f ; lDif[3]= 0.0f ;
lSpe[0]= 0.5f ; lSpe[1]= 0.0f ;
lSpe[2]= 0.0f ; lSpe[3]= 0.0f ;
lPos[0]= 1.0f ; lPos[1]= 0.0f ;
lPos[2]= 0.0f ; lPos[3]= 0.0f ;
glLightfv(GL_LIGHT1,GL_AMBIENT,lAmb);
glLightfv(GL_LIGHT1,GL_DIFFUSE,lDif);
glLightfv(GL_LIGHT1,GL_SPECULAR,lSpe);
glLightfv(GL_LIGHT1,GL_POSITION,lPos);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT1);
//绘制实体
glPushMatrix();
glColor 3f (0.5, 0.5, 0.5);
glutSolidTeapot(0.3);
glPopMatrix();*/
}
void CWSDView::OnDraw(CDC* /*pDC*/)
{
/* // TODO: 在此处为本机数据添加绘制代码
//双缓存应用
static BOOL bBusy = FALSE;
if(bBusy) return;
bBusy = TRUE;
//背景色
glClearColor( 0.0f , 0.0f , 0.0f , 1.0f );
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清缓存
glMatrixMode(GL_MODELVIEW);//启动模型矩阵
glLoadIdentity();//初始化模型矩阵
// 绘制动作
glTranslatef(0.0, 0.0, -8.0);
RenderScene();//绘制场景
glFinish();//完成绘制
SwapBuffers(wglGetCurrentDC());//双缓存应用:更新缓存
bBusy = FALSE;*/
}
void CWSDView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_oldPoint = point;
Invalidate(true);
CView::OnLButtonDown(nFlags, point);
}
void CWSDView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
Invalidate(true);
CView::OnLButtonUp(nFlags, point);
}
void CWSDView::OnMouseMove(UINT nFlags, CPoint point)
{
//TODO: 在此添加消息处理程序代码和/或调用默认值
if (nFlags & MK_LBUTTON) //左键旋转
{
rotate_x += (point.y - m_oldPoint.y) * 0.3;
rotate_y += (point.x - m_oldPoint.x) * 0.3;
m_oldPoint = point;
}
else if (nFlags & MK_RBUTTON) //右键平移
{
m_translateX += (point.x - m_oldPoint.x) * 0.003;
m_translateY += (m_oldPoint.y - point.y) * 0.003;
m_oldPoint = point;
}
Invalidate(true);
CView::OnMouseMove(nFlags, point);
}
void CWSDView::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_oldPoint = point;
Invalidate(true);
CView::OnRButtonDown(nFlags, point);
}
void CWSDView::OnRButtonUp(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
Invalidate(true);
CView::OnRButtonUp(nFlags, point);
}
BOOL CWSDView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
m_scale += 0.1 * zDelta / 120;
Invalidate(true);
return CView::OnMouseWheel(nFlags, zDelta, pt);
}
本文详细介绍了如何在Microsoft Visual Studio .NET 2003中利用MFC框架创建OpenGL程序。通过新建MFC多文档应用程序,添加WSDView类并实现双缓存、鼠标交互等功能,实现了在MFC环境中绘制并交互3D对象,例如茶壶。

682

被折叠的 条评论
为什么被折叠?



