Visual C++下OpenGL开发框架与应用

来源:互联网 发布:日语翻译网站 知乎 编辑:程序博客网 时间:2024/05/18 03:11
 

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 CTerraintestView::PreCreateWindow(CREATESTRUCT& cs)
{
//设置窗口类型
cs.style.|=WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
return CView::PreCreateWindow(cs);
}
void CTerraintestView::OnDraw(CDC* pDC)
{
//渲染场景
RenderScene();
}
BOOL CTerraintestView::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;
}
int CTerraintestView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
//获取客户区的设备描述表
m_pDC=new CClientDC(this);
//初始化OpenGL
InitializeOpenGL(m_pDC);
//初始化OpenGL的一些状态参数并对地形数据进行初始化
InitGL();
return 0;
}
BOOL CTerraintestView::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 CTerraintestView::InitGL()
{
//初始化整个场景和OpenGL的状态变量

//初始化地形数据以便于绘制
InitTerrainData();
// 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
}
void CTerraintestView::InitTerrainData()
{
//随机初始化地形数据,此处与程序框架无关,故省略,可以参加本文对应的源代码
}
BOOL CTerraintestView::RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
gluLookAt(0,7,0,4,4,4,0,1,0); //视图变换
DrawTerrain(); //绘制地形
::SwapBuffers(m_pDC->GetSafeHdc()); //交互缓冲区
return TRUE;
}
BOOL CTerraintestView::DrawTerrain()
{
//根据地形数据绘制出地形
float h1,h2,h3,h4;
float color1,color2,color3,color4;
int x,z;
for(x=1;x<29;x++)
{
for(z=1;z<29;z++)
{
//获取相邻的四个顶点的高程数据
h1=m_land[x][z];
h2=m_land[x][z+1];
h3=m_land[x+1][z];
h4=m_land[x+1][z+1];

//计算各个顶点的颜色数据
color1=h1*256/(m_Max-m_Min);
color2=h2*256/(m_Max-m_Min);
color3=h3*256/(m_Max-m_Min);
color4=h4*256/(m_Max-m_Min);

//地形由四边形构成
glBegin(GL_QUADS);
glColor3f(color1,0,color1);
glVertex3d((float)x,h1,(float)z);
glColor3f(color2,color2,color2);
glVertex3d((float)x,h2,(float)(z+1));
glColor3f(color3,color3,color3);
glVertex3d((float)(x+1),h3,(float)z);
glColor3f(color4,color4,color4);
glVertex3d((float)(x+1),h4,(float)(z+1));
glEnd();
}
}
return TRUE;
}
void CTerraintestView::OnDestroy()
{
CView::OnDestroy();
//删除当前的RC
::wglMakeCurrent(NULL,NULL);
//删除RC
::wglDeleteContext(m_hRC);
//删除DC
if (m_pDC)
delete m_pDC;
}
void CTerraintestView::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进行图形应用开发具有较高的借鉴意义。

原创粉丝点击