资源描述
基于MFC和OpenGL三维图形的开发
匡天君 滕远道 王乘 徐明毅
(华中科技大学 数字化工程中心,湖北 武汉 430074)
摘要:本文介绍了OpenGL的基本概念,通过对三维动态球体模型的具体分析,详细讨论了如何利用OpenGL函数库在MFC中实现三维绘图。
关键词:OpenGL; MFC;三维;设备描述表(DC);绘制描述表(RC)
中国分类号:TP391.41 文献标识码:A
3D Graphics Based on MFC and OpenGL
KUANG Tian-jun,TENG Yuan-dao,WANG Cheng,XU Ming-yi
(Digital Engineering Research Center ,HUST,HuBei WuHan 430074,China)
Abstract:This paper describles the basic concepts of OpenGL。 And how to implement three-dimentsions images in MFC used OpenGL is also particular argued through an three-dimentsions dynamical sphere specific analyzed。
Keywords:OpenGL;MFC;3D;DC;RC
引言
随着三维图形的应用愈来愈广泛,计算机图形学的发展也越来越迅速。那么如何快速实现三维图形的计算可视化,仿真可视化呢?OpenGL即可作为主要途径之一。OpenGL作为一种硬件图形的软件接口,其优点是作为一个独立的工作平台,独立于硬件设备、窗口系统和操作系统,用它编写的软件可以在Unix、Windows98/NT等系统间移植。但是现在很多有关OpenGL的书中的程序都是使用标准C调用OpenGL函数实现的,这样做使得它缺乏面向对象能力,不符合当前流行的软件设计思想。因此我们需要借助一个“窗口”系统来完成OpenGL三维图形的制作。众所周知,Visual C++中MFC包含了强大的基于Windows的应用框架,提供了丰富的窗口和事件管理函数,已经成为当前一种比较流行的工作平台。于是我们可以选用MFC调用OpenGL函数来进行三维图形的开发。
现在以三维动态球体模型来讲述MFC中的OpenGL编程
1.基本思想
在Windows98/NT平台下,GDI是原始窗口的图形接口。而GDI实现这些是通过一个设备描述表DC来实现的。现在通过OpenGL绘图需要创建绘图描述表RC。但是RC并不能直接完成绘图,只能与特定的DC联系起来,从而完成具体的绘图工作。最后要注意释放RC和DC。
2. 编程步骤
第一步,设置开发环境
现在以Windows2000为例,首先将glu.dll,glu32.dll,glut.dll,glut32.dll,opengl32.dll文件拷贝到操作系统WINNT/System32目录下,将gl.h,glaux.h,glu.h,glut.h拷贝到Microsoft Visual Studio/VC98/Include/GL目录中中,将glaux.lib,glu32.lib,glut32.lib,opengl32.lib拷贝到Microsoft Visual Studio/VC98/Lib目录中;然后在编译程序的时候选择Project|Setting菜单,在Link标签中的Object/library modules编辑框中输入“opengl32.lib, glut32.lib ,glu32.lib,glaux.lib”。到这时候环境就建立好了,可以进行下面的具体编程工作。
第二步,具体编程
本实例采用基于对话框的工程。至于用单文档方式和多文档方式其编程原理是一样的,这里不再作单独介绍。
2.1创建项目文件。选择File|New菜单项,新建一个基于对话框的项目文件MyDlgOpenGL;
2.2 修改对话框模板。删除对话框中的静态文本,调整控件的位置。
2.3 创建新类,添加消息映射。选择View|ClassWizard菜单项,打开MFC对话框,在Add Class之中选择New,以便添加一个新类COpenGL,且该类的基类选择generic CWnd;最后利用MFC ClassWizard为COpenGL类添加消息WM_CREATE,WM_PAINT的映射。
2.4 添加代码。
2.4.1 定义像素格式 ,创建OpenGL显示。
在OpenGL能够在一个绘图对象中绘图之前,该绘图对象必须初始化。为此引入像素格式的概念。像素格式能告诉OpenGL是否使用双缓存,颜色模式,颜色位数,深度位数等等重要信息。 它由一个被称作PIXELFORMATDESCRIPTOR的所描述。于是定义在OpenGL.h中定义函数int MySetPixelFormat(HDC hdc)。然后在OpenGL.cpp文件中加入如下代码:
int COpenGL::MySetPixelFormat(HDC hdc)
{
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // pfd结构的大小
1, // 版本号
PFD_DRAW_TO_WINDOW | // 支持在窗口中绘图
PFD_SUPPORT_OPENGL | // 支持 OpenGL
PFD_DOUBLEBUFFER, // 双缓存模式
PFD_TYPE_RGBA, // RGBA 颜色模式
24, // 24 位颜色深度
0, 0, 0, 0, 0, 0, // 忽略颜色位
0, // 没有非透明度缓存
0, // 忽略移位位
0, // 无累计缓存
0, 0, 0, 0, // 忽略累计位
32, // 32 位深度缓存
0, // 无模板缓存
0, // 无辅助缓存
PFD_MAIN_PLANE, // 主层
0, // 保留
0, 0, 0 // 忽略层,可见性和损毁掩模
};
int iPixelFormat;
// 为设备描述表得到最匹配的像素格式
if((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
{
MessageBox("ChoosePixelFormat Failed", NULL, MB_OK);
return 0;
}
// 设置最匹配的像素格式为当前的像素格式
if(SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
{
MessageBox("SetPixelFormat Failed", NULL, MB_OK);
return 0;
}
return 1;
}
现在就可以在OnCreate函数中创建绘图描述表。其代码如下:
int COpenGL::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
MySetPixelFormat(::GetDC(m_hWnd)); // 设置当前的绘图像素格式
hdc = ::GetDC(m_hWnd); // 获得绘图描述表
hglrc = wglCreateContext(hdc); // 创建绘图描述表
wglMakeCurrent(hdc, hglrc); // 使绘图描述表为当前调用现程的当前绘图描述表
return 0;
}
2.4.2 进行光照处理,添加OpenGL显示。
在OpenGL.h中定义函数LightShine函数,作为添加光源和材质属性的函数。在该函数中将创建两个光源,并使其位于不同位置,以产生不同的效果。定义:环境光,散射光,镜面光和镜面反射指数。因为没有一种光源完全由以上三种中的一种光源组成,所以任何一种光源都是由以上三种光源的光照成分组成的,即为混合光源。其成分值由RGBA定义。用材料对光的三原色的反射率来定义材料的颜色,与光源相对应,。代码如下:
void COpenGL::LightShine(void)
{
GLfloat mat_diffuse[4]={1,0.5,0.5,1.0};
GLfloat mat_specular[4]={1.0,1.0,1.0,1.0};
GLfloat mat_shininess[1]={100.0};
//光源 1
GLfloat light_position0[4]={0,500,500,0};
glMaterialfv(GL_FRONT,GL_DIFFUSE,mat_diffuse);
glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);
glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);
glLightfv(GL_LIGHT0,GL_POSITION,light_position0);
glEnable(GL_LIGHT0);
//光源 2
GLfloat light_position1[4]={1000,-1000,1000,0};
GLfloat mat_diffuse1[4]={0.5,0.5,1.0,1.0};
glLightfv(GL_LIGHT1,GL_DIFFUSE,mat_diffuse1);
glLightfv(GL_LIGHT1,GL_SPECULAR,mat_specular);
glLightfv(GL_LIGHT1,GL_SHININESS,mat_shininess);
glLightfv(GL_LIGHT1,GL_POSITION,light_position1);
glEnable(GL_LIGHT1);
//使模型能接受光照
glEnable(GL_LIGHTING);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
//计算定点法线
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
}
2.4.3 定义绘图窗口
所绘球体在哪个区域内进行绘制以及这个区域的风格是怎样的必须需要进行定义。为下一步的绘图工作打下基础。那么可以在MyDlgOpenGLDlg.cpp的函数OnInitDialog()中加入如下代码:
CRect rect(7, 7, 300, 300); //定义绘图区域的大小
m_pDisplay->Create( NULL,
NULL,
//定义窗口风格
WS_CHILD|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_VISIBLE,
rect,
this,
0);
2.4.4 绘制图象
在前面的准备之后,现在可以在OnPaint函数中绘制图象。在具体进行绘制物体时,需要注意的是:glPushMatrix()与glPopMatrix()的使用必须成对出现。此外,必须正确使用glScale() 函数和glRatate() 函数以及双缓存技术,以实现所绘球体的在指定窗口由远及近,由小变大的旋转。代码如下:
void COpenGL::OnPaint()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
space+=0.005;
if(space>1.0)
space=0.1;
step = step + 1.0;
if (step > 360.0)
step = step - 360.0;
glPushMatrix();
glScalef(space,space,space);
glRotatef(step,0.0,1.0,0.0);
glRotatef(step,0.0,0.0,1.0);
glRotatef(step,1.0,0.0,0.0);
LightShine(); //添加光照属性
glutSolidSphere(1.0,20,16); //绘制球体
glPopMatrix();
glFlush();
SwapBuffers(hdc); //使用双缓存技术实现交换缓冲区
}
2.4.5 结束运行
在结束运行时,我们必须将一些环境变量释放。于是应该在添加的类COpenGL的析构函数中进行操作,以释放DC和删除RC。代码如下:
COpenGL::~COpenGL()
{
wglMakeCurrent(NULL, NULL) ;
wglDeleteContext(hglrc); //删除绘图描述表
::ReleaseDC (m_hWnd, hdc) ; //释放设备描述表
}
结语
利用Visual C++强大的窗口功能完成了OpenGL绘制球体的程序的编制工作,上述程序运行正常,其结果是一个动态的球体。理解了上述编程过程,就可以使我们自如地运用MFC进行其他的OpenGL编程。
参考文献
⑴ WOOM,NEIDERJ,DAVIST等. OpenGL编程权威指南[M]. 吴斌等译. 北京:中国电力出版社,2001.
⑵ David J. Kruglinki,Scot Wingo, George Shepherd 著Programming VisualC++6.0技术内幕[M]希望图书创作室译. 北京:北京希望电子出版社,2001.
⑶ 田红鹏,马苗 OpenGL及其基于VC++6.0 的开发,西安科技学院学报:2001, Vol.21,No.4:366-368
作者简介:匡天君(1978-),男(汉族),湖北武汉人,研究生,主要研究方向:计算机可视化仿真;滕远道,研究生,主要研究方向:计算机可视化仿真;王乘,博士生导师,主要研究方向:大规模结构分析仿真及水土结构计算工作;徐明毅,博士后,主要研究方向:结构力学及计算机可视化仿真。
所投栏目:软件时空
第一作者介绍:
姓名:匡天君 性别:男 出生年月:1978年8月 籍贯:湖北武汉人 学位:硕士
简历:2001年9月至今就读于华中科技大学数字化工程学院
研究方向:计算机可视化仿真
通信地址:华中科技大学(主校区)东11舍317
邮政编码:430074
联系电话:027-87549465
展开阅读全文