马马虎虎的看完了《3D数学基础:图形与游戏开发》和《OpenGL编程指南》,感觉没看明白的跟看明白的一样多...但还是算入门了吧!辞职以后,在家玩的甚欢,太过得以忘形了。这两天收敛了许多,做了一个简单的程序,这里想做一些相应的总结。
文章根据网络上同名《Visual C++下OpenGL开发框架与应用》改写,主要是问了节省时间,并且总结一下OpenGL在MFC中的多文档框架下的搭建过程,加以备忘。下面是这两天做的程序的截图,特此留念!
以下是《Visual C++下OpenGL开发框架与应用》内容:
OpenGL全称"开放式图形库",是由SGI公司开发的低层三维图形API,目前在图形开发领域已经成为工业标准。现今市面上关于OpenGL方面的书籍不在少数,但是大多是讲解句法和实例,缺乏对其整个程序开发框架的总结与把握,所以总体上显得比较凌乱。本篇文章主要针对初学者(最好要有图形方面的基础知识)而制作的,旨在通过对OpenGL 的整个知识结构的介绍,来具体剖析其内在运行机制,并且结合实际开发经验总结出在VC平台下的OpenGL开发框架,最后给出一个例程来说明这一框架的具体应用。
一、OpenGL基础知识
OpenGL是一种开放式的图形软件开发包,它采用C语言风格,提供大量的函数来进行图形方面的处理,一般编程使用的函数库包括:
OpenGL图形库-----函数以gl开头,可以实现比较简单的绘制功能,核心函数共115个。这些函数可以运行在现在任何主流操作系统中。
OpenGL实用库-----函数以glu开头,其函数功能更高级一些,如绘制复杂的曲线曲面、高级坐标变换、多边形分割等,共有43个。这些函数可以运行在现在任何主流操作系统中。
OpenGL辅助库-----函数以aux开头,它们是一些特殊的函数,包括简单的窗口管理、输入事件处理、某些复杂三维物体绘制等函数,共有31个。它只能在Win32平台下运行。
OpenGL实用工具开发库----函数以glut开头,它们提供更为复杂的绘制功能,此函数由glut.dll来负责解释执行。
Windows专用函数库-----以wgl开头,负责OpenGL与Windows窗口系统的连接,共有6个。
Win32函数------无专用前缀,实际上为API函数,共5个,用来处理比如象素格式的选择及双缓冲等功能。
OpenGL提供的函数一般是以客户机/服务器的模式来运行的,即执行绘制图形功能的应用程序作为客户机,而OpenGL函数库(实际上是一些动态链接库,比如opengl32.dll,glu.dll等)作为服务器,当应用程序发出绘制请求时,服务器负责对这些绘制请求进行解释,然后把这些处理过的请求发送给图形显示硬件,这样就实现了绘图的目的。另外由于它这种特有的运行机制也实现了网络的透明性,即当应用程序与核心图形库不在同一台机器上时,其程序的代码完全跟它们在同一台机器上的一样,节约了通讯开销。
那么在Windows操作平台下,如果使用OpenGL图形库函数来开发应用程序呢?我们知道,使用GDI(图形设备接口)开发应用程序时,首先需要获得一个Device Context(设备描述表,简称DC),然后才能在这个DC下完成绘图工作,这一过程就类似于现实生活中纸和笔的关系,DC就是纸,而象刷子、画笔这样的GDI对象就是笔。从Windows内部运行机制来分析,DC应该理解为状态保持器,就是它可以而且必须保存当前系统的状态,这些状态包括:当前的画笔、刷子等GDI的具体类型(颜色、粗细等),当前的调色板类型以及系统的其他信息。当用户开始在DC上进行绘制工作时,系统就会先查看DC中相应的当前状态值,然后利用这些状态值进行图形绘制,如果用户希望改变当前状态值,那么可以通过SelectObject这样的Win32函数来将指定的状态或者对象选入DC即可。
实际上基于OpenGL的应用程序也是这样的,只是这里不是直接在DC上进行绘制工作,而是通过Render Context(渲染描述表或者绘制描述表,简称为RC)这样一个桥梁在DC上进行绘制工作,对于程序来说实际上可以理解为就是在RC上绘制图形。另外只要RC不被释放(有效),那么就可以进行绘制工作;相比之下,DC却需要不断的创建和释放。
所以要使用OpenGL图形库进行图形应用开发,首先要获得RC,然后要将其设置为"当前RC",最后后面所有的绘制工作都是在"当前RC"下面进行的,直到"当前RC"无效为止。
二、VC下的OpenGL的程序框架
在目前众多的Windows应用程序开发工具中,微软公司的VC6.0已经成为OpenGL图形应用的首选开发工具。而要使用OpenGL图形库来开发2D/3D的应用程序,就必须解决程序框架的问题。由前面介绍的基础知识可以清楚的看到,不能直接象利用GDI开发图形程序那样使用OpenGL,以下就介绍多文档应用程序情况下的开发框架,其步骤为:
1. 首先在视图类的PreCreateWindow函数内设置窗口类型,防止在窗口重叠时把图形绘制到子窗口和兄弟窗口。实现代码如下:
cs.style |=WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
2. 然后在视图类的OnCreate函数下面进行OpenGL的初始化工作,这部分是此框架里最主要的代码,具体包括:获取视图设备描述表、设置合适的象素格式和调色板、创建绘制描述表并将其设置为当前RC。其实现代码见后面的例程(这部分的代码基本上所有的程序都一致)。这一步完成后即可进行图形的绘制工作。
3. 在视图类的OnSize函数下面进行视口变换,代码见后面例程。
4. 如果需要定时器的数据驱动,那么可以在视图类的OnTimer下修改数据并调用OnDraw函数即可。
5. 在OnDestroy函数执行RC及DC的销毁工作,释放资源。详见后面的代码。
由上面的框架可以看出,所有的关于OpenGL的程序操作都是在指定的视图类中完成的,核心就是OnCreate内的函数代码,而这部分的代码在大部分程序里面是雷同的,所以后面例程的代码具有很大的通用性。另外,虽然上面只是讨论了多文档的情况,实际上单文档的开发框架与其完全类似,这里就不多赘述了。
三、例程分析
3D游戏里面经常会出现地形这一三维实体,下面的例程就是在一个单文档应用程序下实现3D地形的显示。大概思路是建立OpenGL的程序框架,然后初始化地形数据,在此框架上绘制3D地形,由于为了介绍框架,所以程序中涉及到地形数据的初始化和绘制部分都比较简单,详见本文的源代码部分。另外这里也没有涉及到纹理映射、材质等高级内容,但是实际编程中,为了使3D图形更生动,往往要应用这些技术。
此例程的核心代码及分析如下:
BOOL CTrafficView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: 在此处通过修改 CREATESTRUCT cs 来修改窗口类或
// 样式
// OpenGL的要求:设置窗口类型
cs.style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
// MDI 应用的要求:
// cs.lpszClass = AfxRegisterWndClass(CS_OWNDC | CS_HREDRAW | CS_VREDRAW);
return CView::PreCreateWindow(cs);
}
void CTrafficView::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: 在此处添加消息处理程序代码
// 不为绘图消息调用 CView::OnPaint()
RenderScene();
}
int CTrafficView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
//获取客户区的设备描述表
m_pDC=new CClientDC(this);
//初始化OpenGL
InitializeOpenGL(m_pDC);
//初始化OpenGL的一些状态参数并对地形数据进行初始化
InitGL();
return 0;
}
BOOL CTrafficView::InitializeOpenGL(CDC *pDC)
{
//进行opengl的初始化工作
m_pDC=pDC;
//首先把DC的象素格式调整为指定的格式,以便后面对DC的使用
SetupPixelFormat();
//根据DC来创建RC
m_hRC=::wglCreateContext(m_pDC->GetSafeHdc());
//设置当前的RC,以后的画图操作都画在m_pDC指向的DC上
::wglMakeCurrent(m_pDC->GetSafeHdc(),m_hRC);
//下面可以进行画图操作了
return TRUE;
}
BOOL CTrafficView::SetupPixelFormat()
{
//初始化象素格式以及选取合适的格式来创建RC
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // pfd结构的大小
1, // 版本号
PFD_DRAW_TO_WINDOW | // 支持在窗口中绘图
PFD_SUPPORT_OPENGL | // 支持 OpenGL
PFD_DOUBLEBUFFER, // 双缓存模式
PFD_TYPE_RGBA, // RGBA 颜色模式
24, // 24 位颜色深度 ,color depth
0, 0, 0, 0, 0, 0, // 忽略颜色位
0, // 没有非透明度缓存
0, // 忽略移位位
0, // 无累加缓存
0, 0, 0, 0, // 忽略累加位
32, // 32 位深度缓存
0, // 无模板缓存
0, // 无辅助缓存
PFD_MAIN_PLANE, // 主层
0, // 保留
0, 0, 0 // 忽略层,可见性和损毁掩模
};
//在DC中选择合适的象素格式并返回索引号
int pixelformat;
pixelformat=::ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd);
if (pixelformat==0)
{
AfxMessageBox("no matched pixelformat!");
return FALSE;
}
//设置指定象素格式
if (::SetPixelFormat(m_pDC->GetSafeHdc(),pixelformat,&pfd)==FALSE)
{
AfxMessageBox("can't set specified pixelformat!");
return FALSE;
}
return TRUE;
}
BOOL CTrafficView::InitGL()
{
//初始化整个场景和OpenGL的状态变量
//初始化地形、图形等相关数据以便于绘制
// OpenGL场景初始化(光照、雾化等〕
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
return TRUE; // Initialization Went OK
}
BOOL CTrafficView::RenderScene()
{
wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC); // 尤为重要,MDI必不可少
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0,7,0,4,4,4,0,1,0); //视图变换
// 场景绘制
glFlush();
::SwapBuffers(m_pDC->GetSafeHdc()); //交互缓冲区
return TRUE;
}
void CTrafficView::OnDestroy()
{
CView::OnDestroy();
// TODO: 在此处添加消息处理程序代码
//删除当前的RC
::wglMakeCurrent(NULL,NULL);
//删除RC
::wglDeleteContext(m_hRC);
//删除DC
if (m_pDC)
delete m_pDC;
}
void CTrafficView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
//添加窗口缩放时的图形变换函数,即视口变换
glViewport(0,0,cx,cy);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f,(GLfloat)cx/(GLfloat)cy,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
四、小结
前面介绍了VC下的OpenGL应用程序的开发框架,借助此框架,使得开发人员象使用Win32函数一样来使用OpenGL提供的图形库函数,减轻了开发的难度,增强了程序代码的鲁棒性,因此对使用OpenGL进行图形应用开发具有较高的借鉴意义。
posted on 2010-10-20 00:24
vic.MINg 阅读(948)
评论(0) 编辑 收藏 引用 所属分类:
OpenGL