1、第一章 引言 1.1 简介 室外地形地貌的实时渲染技术在很多领域中都有着极其重要的作用,如GIS系统,飞行模拟系统,VR系统、数字地球技术以及游戏编程世界中等都离不开室外地形场景的实时渲染技术。一个好的、优秀的室外地形场景实时渲染技术在保证实时性以外还能创造出非常逼真的虚拟自然环境。 一个室外场景的理想大小是无限的,在场景中,视野也是无限的。如果站在高出,要可以看见低的地方,所有有可能看见的。 所以在地形的绘制上,我们总是希望我们的地形越大越好,无限的大的场景需要无限的场景数据,当然这是不可能的。我们的地形数据的多少决定了场景的大小。所以如何保存这些地形数据成了首要的问题,但是在随
2、着存储器成本迅速下降的今天,这个问题已经变的不是十分的突出。其次是无限的视野问题,无限的视野就表示渲染无限的图元(图元即是3DAPI支持的简单的几何图形,详见OpenGL/Direct3D SDK),这也是不可能。图元的数量是以场景大小的平方的速度增长的。光考虑地形数据,一个2048X2048的地形,如果不考虑减低细节程度和裁剪的话,它将要渲染8M的三角形,这样的三角形量在PC级别上目前还是远不能实现交互式帧率的。所以,如何减少要渲染地形时候的图元数目成了室外场景实时渲染的关键问题。 这篇论文以建立一个真实的地形地貌为目标,采用现在业内流行的LOD技术,借助OpenGL,通过递归算法来实现室
3、外地形渲染。 1.2 相关技术发展现状 既然是3D场景的渲染当然需要3D API,计算机三维图形是指将用数据描述的三维空间通过计算转换成二维图像并显示或打印出来的技术,API(Application Programming Interface)即“应用程序接口”是连接应用程序与操作系统、实现对计算机硬件控制的纽带,Direct3D和OpenGL是目前的两大3D图形API,要在你的3D显卡上进行3D特效的制作、实现都必须通过它们。 这两种API各自有各自的优点,这里使用的OpenGL完成的设计。实际上D3D在8.0以后已经很像OpenGL了。 Direct 3D是基于微软的
4、通用对象模式COM(Common Object Mode)的3D图形API。它是由微软(Microsoft)一手树立的3D API规范,微软公司拥有该库版权,它所有的语法定义包含在微软提供的程序开发组件的帮助文件、源代码中。Direct3D是微软公司DirectX SDK集成开发包中的重要部分,适合多媒体、娱乐、即时3D动画等广泛和实用的3D图形计算。自1996年发布以来,Direct3D以其良好的硬件兼容性和友好的编程方式很快得到了广泛的认可,现在几乎所有的具有3D图形加速的主流显示卡都对Direct3D提供良好的支持。但它也有缺陷,由于是以COM接口形式提供的,所以较为复杂,稳定性差,另外
5、目前只在Windows平台上可用。 作为微软DirectX技术的组件之一,Direct 3D也随着DirectX的升级而不断更新,同时在微软的全力扶植下,Direct 3D技术的发展速度极快,DirectX 7:正式支持硬件T&L(光影变换)、DirectX 8:对Pixel Shader(像素着色器)Vertex Shader(顶点着色器)的支持、DirectX 9:提供2.0版本的可编程顶点和像素着色模式,显卡硬件厂商也纷纷以对最新的D3D特效的硬件支持为卖点。遗憾的是,由于平台的局限性等原因,D3D应用至今仍主要集中于游戏和多媒体方面,专业高端绘图应用方面,老牌的3D API---O
6、penGL仍是主角。 OpenGL的英文全称是“Open Graphics Library”即“开放的图形程序接口”,它是计算机工业标准应用程序接口,主要用于定义二维三维图形。 OpenGL是一套底层三维图形API,之所以称之为底层API,是因为它没有提供几何实体图元,不能直接用以描述场景。但通过一些转换程序,可以很方便的将AutoCAD、3DS等图形设计软件制作的DFX和3DS模型文件转换成OpenGL的顶点数据。 OpenGL是与硬件无关的软件接口,使用它图形软件生产厂商再不用为各种不同的机型开发设计不同的软件,只要操作系统使用了OpenGL适配器就可以达到相同的效果,它是一个开放图形库
7、目前在Windows、MacOS、OS/2、Unix/X-Windows等系统下均可使用,且仅在窗口相关部分(系统相关)略有差异,因此具有良好的可移植性,同时调用方法简洁明了,深受好评,应用广泛。OpenGL能在网络环境下以客户机/服务器模式工作,充分发挥集群运算的威力,是专业图形处理、科学计算等高端应用领域的标准图形库。 将OpenGL称之为SGI的OpenGL毫不为过,它源于SGI公司为其图形工作站开发的IRIS GL,在跨平台移植过程中发展成为OpenGL。SGI在1992年7月发布1.0版,后成为工业标准,由成立于1992年的独立财团OpenGL Architecture Revi
8、ew Board (ARB)控制。SGI等ARB成员以投票方式产生标准,并制成规范文档(Specification)公布,各软硬件厂商据此开发自己系统上的实现。只有通过了ARB规范全部测试的实现才能称为OpenGL,现在的ARB投票成员包括SGI、Intel、IBM、nVIDIA、ATi、Microsoft、Apple等业界群英。2001年8月ARB批准了1.1版本,2002年7月24日通过1.4版本。[1] 开发人员相继被OpenGL所吸引,尽管面临着压力,硬件厂商们还是必须要满足开发人员,因为是他们编制了在硬件上运行的软件。最终,消费者的美元将决定哪个标准幸存,而使用OpenGL的开发人
9、员能够比他们的竞争者在更短的时间内制造出更好的应用程序。今天,许多游戏公司先制作OpenGL版本,然后移植到Direct3D中,因为这样做是他们能够更快地完成和演示游戏。 第二章 相关技术 2.1 渲染技术 2.1.1 Voxel(Volumetric Pixel) Voxel 也就是Volumetric Pixel。也就是所谓的“体素”,VOXEL 技术其实是一种很简单的技术,但它对于即时渲染地形确实又是一种十分有效的技术。 VOXEL 技术出现得相当早,据 James Sharman 自称他在 95 年时就想出了这种方法。而 NO
10、VALOGIC 则是这种技术的忠实拥护者,从早期的 COMMANCHE 到 DELTA FORCE 和 DELTA FORCE II,都一直使用 VOXEL 技术来搭建图形引擎的核心。[2] 图2-1 一个Voxel技术生成的地表 VOXEL 技术的思想很简单,它不是用的现在很流行的多边形方法来描述地形,而是用的一种线性插值的办法来形成自然连续的起伏地形。如果只用一句话来描述就是由近及远地依次画出对应的地形轮廓线,同时记录屏幕投影的高度坐标值,以供画下一条轮廓线时比较,从而只画后面新露出来的部分。 如图2-1 为一个Voxel技术生成的地表示例。 具体实现的
11、时候,可以采用了一个数组来储存范围内的每一个点的高度.而地表的光泽, 也是预先算好的;同样储存在一个数组.渲染的方法是利用坡度(即和周围点的高度差来决定的),当然也可以考虑点的绝对高度,比如在绝对高度高的区域白一些,以造成一种山顶积雪的感觉.在生成地表时候,第一步是决定外形的概况,可以采用随机的方式.由粗到细,逐步细化,每次地表上下波动的幅度,由运算的面积来决定.而在实际运用时,可以在随机的过程中, 加入一些限制,来控制地表的生成.第二步,就是将前面生成的图象做平滑处理,让每个点去和周围的点运算,取平均值,使不至于出现过大的变化,这个过程多重复几次,就可以得到上佳的效果了。 Voxel有一个
12、天生的优点就是渲染的时候它和场景的大小没有关系,而且绝对不会渲染多余的东西(自带裁剪功能)。它的复杂度只和我们需要的视野,以及分辨率有关。而且可以在不使用硬件加速的情况下达到比较理想的速度(Delta Force I就没有使用硬件加速),生成的图象也比较的细腻。它的缺点就是不够的灵活。 2.1.2 LOD(Level of Detail) LOD 也就是层次细节(Level of Detail)的简称,不同于Voxel技术,它是一种使用多边形的,真正的 3D渲染技术。它根据一定的规则来简化物体的细节,我们可以根据需要来选择不同细节程度的物体表达方式。如离观察者近的选择较高的细节程度、
13、反之选择较低的细节程度。用在地形渲染中,有时我们也称它为多分辨率地形(Muti-resolution terrain)渲染技术。也是作为业内的一种发展趋势的技术。 LOD算法对场景的处理比较复杂,但是该算法提供了很高的自由度给设计者去渲染场景,同时也能方便的使用县卡的硬件加速功能。而且可以很容易的在场景中组合其他的物体,例如,树木,太阳,以及粒子系统等,LOD可算可以很方便的让观察者在任意的角度去观察场景,只需要将摄像机旋转一定的角度就可以了。而这一点在Voxel中是困难的,Voxel中实现非水平的视线是非常麻烦的。[3] 本作品就是用LOD技术,以后将对其的设计与实现做详细介绍。
14、2.2 渲染方式 地形渲染的方式上主要有两种:静态渲染与动态渲染 静态渲染的地形,也就是细节在事先式计算好的,平原或者远点的时候细节较低,高地或者近点的时候细节较高.用这种方式建立起模型可以节省大量的空间。 动态渲染的地形则是和视点相关的,是一种实时的。它也是本作品将要采用的方法。随着视点的移动,地形网格将被不断更新。这种方式建立起来的场景更加符合人的视觉感受,可以看到的细节的变化。其中需要在精度(尽可能多的多边形描绘地表)和速度(多边形的数量大大影响着运行的速度)之间选择一个合适的平衡点。 2.3 开发环境 采用Microsoft Visual S平台下Visual C
15、 .net开发。 Visual Studio .NET 是一个功能强大、高效并且可扩展的编程环境。它充分展现了应用程序开发的潜能,并提供了生成应用程序所需的工具和技术。这些应用程序将给当今的企业、机构提供强大的支持,并推动下一代基于 XML Web服务软件的发展。 本文DEMO实现使用.net的开发平台,对其中的网络特性并没有涉及。 采用其中的OpenGL Version 1.5 OpenGL (for “Open Graphics Library”) is a software interface to graphics hardware. OpenGL version 1.
16、5, released on July 29, 2003, is the fifth revision since the original version 1.0. In addition to additions to the classical fixed-function GL pipeline in OpenGL 1.5, the OpenGL ARB also approved a related set of ARB extensions including the OpenGL Shading Language specification and the ARB shader
17、 objects, ARB vertex shader, and ARB fragment shader extensions through which high-level shading language programs can be loaded and used in place of the fixedfunction pipeline. Following are brief descriptions of each addition to OpenGL 1.5. The low-level and high-level shading languages are impor
18、tant adjuncts to the OpenGL core. They are described in more detail in appendix , and their corresponding ARB extension specifications are available online as described in that appendix.[4] 第三章 系统分析与设计 地形渲染是一个室外渲染引擎的核心部分。而实现一个大规模的地形渲染系统关键是如何简化地形,抛弃不必要的渲染动作(例如看不见的物体背面,不需要的细节)来加快渲染速度。而通过以上技术分析,动态LO
19、D技术无疑是一个强有力的解决方案。 在这一论文中,采用OpenGL技术,通过读入高度图(raw)文件,在地形构建,纹理映射,视点剪裁,摄像机移动等方面完成了代码实现。下面就分别对这些各个方面进行介绍。 3.1 LOD算法 层次细节(Levels of Details)[3]是一种符合人的视觉特性的技术。当场景中的物体离观察者很远的时候,它们经过观察,投影变换后在屏幕上往往只是几个、甚至是一个像素。所以没有必要再去描绘它的全部细节,只需要适当的合并一些多边形而不损失画面的视觉效果。 实现LOD算法时,除了如何对几何物体进行简化以外,还有一个很重要的问题就是如何决定是否对一个物体进行简化
20、或者说在某个时刻该如何决定使用哪个层次细节度的模型来表示物体。这时候需要建立一个评价系统,由这个评价系统来决定要对物体简化到何种层度。这种评价系统通常是视点相关的,离视点远的物体通常只需要较少的细节,反之则需要比较多的细节。除此之外,物体本身的特性也必须考虑在内。比如说,一个平坦的表面只需要很少的三角形就能较好表现出来。而一个凹凸不平的表面是理所当然的需要更多的三角形去描绘。 本文DEMO中的评价系统是在Terrain类中的Updata函数中实现的,其中分远距离与近距离两种情况,结合距离、坡度、细分精度的因素,进行评价判断。 Lod算法中具体实现时其中采用四叉树算法,可有效把地形不在视野
21、内的网格去掉,降低程序的时间复杂度,如图3-1。 其基本思路是:先把地形一分为四,用递归的方法对每个网格渲染。对每个网格,通过评价系统判断,如果达到最高精度,则退出;如果不在视野内,也退出。再对符合条件的网格递归下去。 图3-1 一个地形的四叉树表示 一个LOD地形法则的优秀概述可以从三篇论文中了解到,作者分别为:微软的Hoppe、 Lindstrom、Duchaineau。在第一位作者的论文中描绘了一个基于Progressive Meshes的法则,这是一个与增加三角形到任意网格来达到你需要的细节相关的新的技术。这篇论文很精彩但有点复杂,同时这项技术需要大量的内存。第二篇
22、论文的作者是Lindstrom,他描述了一个叫四叉树(Quad Tree)的结构用于描绘地形碎片(PATCH),一个四叉树递归的把一个地形分割成一个一个小块(tessellates)并建立一个近似的高度图。四叉树非常简单但很有效。第三篇论文的作者是Duchaineau,他描述了一个基于二元三角树结构的法则ROAM(实时优化自适应网格)。这里每一个小片(PATCH)都是一个单独的正二等边三角形,从它的顶点到对面斜边的中点分割三角形为两个新的正等边三角形,分割是递归进行的可以被子三角形重复直到达到希望的细节等级。 渲染地形时,如果采用固定地形精度等级,则会随着地形面积的增大,渲染的三角形数量会随
23、着面积的增大而增长。采用动态精度等级,越远处的地形画的三角形面积越大(精度越低),越近的地形面积越小(精度越高),坡度高的地方精度高,平坦的地方精度低,这样能有效地减少画三角形的数量,提高游戏引擎的速度.在图3-2中,可以看到距离视点近的与距离视点远的地方所构造的三角形数量有着明显的区别 图3-2 通过LOD处理的地形网格(图片来自本文DEMO) 具体的代码实现LOD算法在下一章中的4.3.8中介绍的类Terrain中的成员函数Update()进行网格递归细分,成员函数RenDerQuad()进行网格递归渲染。 3.2 高度图文件格式 高度图使用一个RAW的
24、数据格式来保存,这个格式包含了8位的高度信息。通常高度图必须从头至尾保存在内存中。 3.3 LOD算法优化 在进行地形绘制的时候,采用的是GL_TRIANGLE_FAN方法,但是这样在相邻精度等级相差1倍或1倍以上时,有一个注意的地方就是两个不同分辨率的节点拼接处会产生 T 形裂缝。 图3-3 裂缝示例 如图3-3中第一、二张图所示。我们必须消除这种裂缝,第三张图中演示了在拼接地方增加一条边的方法来消除裂缝,而第四张图则采用了去掉一条边的方法。相对来说,第一种方法更加的复杂,但是也更加的全面,因为拼接处的两个节点的分辨率可以相差任意大。第二种方法则更加简单,它要求拼接处
25、的两个节点的层次差距最多不超过。本文DEMO采用第一种方法,对于具体代码将在4.3.8中介绍的类Terrain中的成员函数RemedyTop(),RemedyRight(),RemedyBottom(),RemedyLeft()中得到实现。 效果图如图3-4。 图3-4 裂缝弥补 在视点裁剪方面,当一个节点的四个子节点中有一部分被分割,另一部分不被分割的时候,给渲染带来很大的麻烦,而且每处理一个节点的时候,我们都要检查四个子节点,比较麻烦。给出如下规则:当一个节点的四个子节点中任何一个需要继续分割的时候,四个子节点都进行分割。在本文的里,这个规则进一步成:一
26、个节点需要分割的时候,就把其四个子节点都生成并放入到下一层次的队列中去。 3.4 纹理产生方法 步骤: 1.读出每张贴图对应象素的RGB值。 2.从高度图中获得对应点的高度。 3.根据高度计算混合因子,混合第一步得出的RGB值,并写入新纹理的对应位置。 4.重复步骤1,直到所有象素被处理完(如贴图的大小是256*256,则从(0,0)处理到(255,255)),便可得到一张新的纹理。 5.根据高度图,计算多边形顶点坐标,并设置纹理坐标。 代码实现在下一章4.3.2中介绍的类CTexture中实现,读入事先准备好的纹理文件。 如图3-5为映射前准备的bmp纹
27、理图画。 图3-6是本文DEMO完成纹理映射的效果图。 图3-5 BMP纹理原图 图3-6 纹理映射实现效果图 3.5 裁剪方法 在3D裁剪中,通常有两种裁剪是比较容易实现的:View Frustum Culling 和 Back face Culling。本文DEMO在世界空间实现一个View Frustum裁剪器。 一个投影体由六个面组成,一个平面的方程可以表示为A*x+B*y+C*y+D=0。规定朝投影体内部的方向为平面的正方向,判断一个顶点是否在投影体内部时,我们只要把顶点坐标代入到六个面
28、的方程中,通过检查结果的符号就可以判断点是不是在投影体内部(所有的符号都为正)。 代码实现在下一章4.3.4中介绍的类CFrustum中实现 3.6 OpenGL简介 OpenGL不是像C或C++那样的编程语言。它更像C的运行库,提供一些预封装的函数。 OpenGL是一种过程性而不是描述性的图形API,它并不描述场景以及其外观,它包括了超过200条命令和函数,这些命令可用于三维空间中绘制图元,比如点、线和多边形。另外,OpenGL支持光照和阴影、纹理贴图、混合、透明度、动画以及许多其它特殊效果和功能。 OPENGL编程可用到的函数库包括: OPENGL实用库:函数
29、以glu开头 OPENGL辅助库:函数以aux开头 Windows专用函数库:函数以wgl开头 Win32API:无专用前缀 OPENGL中有115个核心函数,可以在任何OPENGL平台上使用 OPENGL实用库比上面这115个函数高一级,提供高级调用 OPENGL辅助库本来是提供初学者入门的函数,不保证在任何平台的使用 但可以在WIN32下使用 OPENGL函数、变量命名准则[5] 变量: 前缀 类型 对应C变量 b 8-bit int signed char s 16-bit int short i 32
30、bit int long f 32-bit float float d 64-bit float double ub 8-bit unsigned int unsigned char us 16-bit unsigned int unsigned short ui 32-bit unsigned int unsigned long 例如1.0f实际就是1.0,一般写成1.0f看上去好辨认一些。 函数:函数参数类型作为函数后缀 例如:glVertex2i(2,4)表明是opengl基本函数(gl
31、 是绘点的函数(-Vertex-) 是两个整型参数(-2i) 将来对一个函数掐头去尾就知道它是干什么的。 CALLBACK函数 是一些用来让系统调用的函数,系统要调用它们,例如显示、接受键盘输入... 你就好比擂积木一样把它们列在主程序初始化后(顺序一般无关紧要), 系统会自动执行它们,例如: void CALLBACK display(void)中写好了想画什么,然后主程序中使用auxMainLoop(display);就可以让这个一直显示了,很类似VC中的OnPaint() OPENGL基本库的绘图方法 先由“类型”指定画什么平面: GL_POINTS 单个顶
32、点集 GL_LINES 多组线,2个点一条线 GL_GL_POLYGON 单个简单填充凸多边形 GL_TRAINGLES 多组三角型,3个点一个三角 初始化OpenGL大致步骤如下: 一, 获取所需要在上面绘图的设备环境(DC); 二, 为该设备环境设置像素格式; 三, 创建基于该设备环境的OpenGL设备; 四, 初始化OpenGL绘制场景及状态设置。 OpenGL是一种状态机模式,比如使用glEnable打开一个状态,在以后的绘图中将一直保留并应用这个状态,除非你调用glDisable及同类函数来改变该状态或程序退出。OpenGL绝大多数函数都是一种状态机
33、如果设置了当前的纹理,那么纹理将不会自动改变。 第四章 系统实现 4.1 项目建立 首先在VC.net下自动建立一个可以运行的SDK程序,名字叫做STprj,。 自动建立的SDK可以运行生成一个简单的窗口程序。 下面添加OpenGL的头文件放到StdAfx.h中,一般在VC.net中,OpenGL的头文件是存放在系统头文件目录的子目录GL中的,所以在指定包含的时候要指定一下相对路径。 gl.h是基本头文件,glu.h是应用头文件,大多数应用程序都需要同时包含这两个头文件。opengl32.lib则是OpenGL的win32实现的
34、标准导入库。 glaux.h是OpenGL的辅助库头文件。 由于项目设计需要读入文件,以及一些地方需要数学计算等,所以还要包括一些C的头文件,如stdlib.h,math.h等 之后宏定义一些所需要的定义的。 GL_TEXTURE0_ARB,GL_TEXTURE1_ARB,GL_COMBINE_ARB,GL_RGB_SCALE_ARB为多纹理的一些定义说明 typedef void (APIENTRY*PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t) typedef void (APIENTRY *
35、PFNGLACTIVETEXTUREARBPROC) (GLenum target) 为定义的多纹理的函数原型。 extern PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB; extern PFNGLACTIVETEXTUREARBPROC glActiveTextureARB; 声明连接实现多纹理功能的函数指针。 static HWND m_hWnd; 宏定义窗口句柄 static HDC m_hDC; DC句柄 static HGLRC m_hRC; Our Rendering Context
36、 for OpenGL 4.2 Windows窗口设计以及功能实现 文件:STprj.cpp 设置标题栏文本 szTitle[MAX_LOADSTRING] 设置主窗口类名 szWindowClass[MAX_LOADSTRING] 首先是消息循环,大多数时实渲染的应用程序都会把绘图的代码放在空闲事件里,空闲事件与其说是一种事件倒不如说它是“没有事件”,先看一下消息循环: while(1) { if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { if (msg.message==WM_Q
37、UIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else { if(bActive()) { m_Scene.CheckForMovement(); DrawGLScene(); ::SwapBuffers(wglGetCurrentDC()); } else { ShowWindow(m_hWnd,SW_SHOWMINIMIZED);
38、 WaitMessage(); } } } 使用PeekMessage而不是GetMessage,这样当消息队列中没有消息时便不会等待而是返回一个FALSE值,这样我们就可能知道当前应用程序处理空闲状态了。另外值得注意的是,如果得到的消息是WM_QUIT,PeekMessage一样会返回FALSE值,所以我们需要做一些特殊处理。 当允许Active运行时,调用CScene类中的CheckForMovement();以及DrawGLScene()函数。 函数InitInstance(HINSTANCE hInstance,int nCmdShow)其中改动比较大,并且
39、将注册窗口类融入其中。即定义的注册窗口类wc,其中参数如下: wc.style= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;//刷新屏幕,添加句柄 wc.lpfnWndProc = (WNDPROC) WndProc; //窗口句柄消息 wc.cbClsExtra = 0; //无特别窗体数据 wc.cbWndExtra = 0; wc.hInstance = GetModuleHandle(NULL); //设置实例 wc.hIcon = LoadIcon(NULL, ID
40、I_WINLOGO); //读取默认图标 wc.hCursor = LoadCursor(NULL, IDC_ARROW); //读取箭头指针 wc.hbrBackground = NULL; //无GL必需的背景 wc.lpszMenuName = NULL; //无菜单 wc.lpszClassName = "OpenGL"; //设置类名 该函数中定义窗口默认大小为1024*800,像素格式为16位。 构建Windows窗口则使用函数CreateWindowEx( dwExStyle, //继承win
41、dows窗口风格 szWindowClass, //类名 szTitle, //窗口标题 dwStyle | //定义窗口风格 WS_CLIPSIBLINGS | //必需的窗口风格 WS_CLIPCHILDREN, //必需的窗口风格 0, 0, //窗口位置 WindowRect.right-WindowRect.left, //窗口宽 WindowRect.bottom-WindowRect.top,
42、//窗口高 NULL, //没有父窗口 NULL, //没有菜单 GetModuleHandle(NULL), //实例 NULL) 用来设置一个匹配的像素格式 static PIXELFORMATDESCRIPTOR pfd= { sizeof(PIXELFORMATDESCRIPTOR), // 像素格式描述的大小 1, // 指定像素格式的数据结构 PFD_DRAW_TO_WINDOW | // 格式支持window PFD_SUP
43、PORT_OPENGL | // 格式支持OpenGL PFD_DOUBLEBUFFER, // 支持双缓冲 PFD_TYPE_RGBA, // 要求RGBA格式 bits, // 选择颜色深度 0, 0, 0, 0, 0, 0, // 忽视色位 0, // 无Alpha缓冲 0, // 忽视位转移 0, // 无堆缓冲 0, 0, 0, 0, // 忽视堆位数据 16, // 16位Z缓冲深度
44、0, // 无模版深度 0, // 无辅助缓冲 PFD_MAIN_PLANE, // 主绘图层 0, // 保留的参数 0, 0, 0 // 忽略修饰层 }; 出错处理:1、判断注册窗口类是否建立。 2、判断是否获取你需要在上面绘图的设备环境。 3、判断是否获得匹配的环境设备的像素格式。 4、判断是否设置像素格式。 5、判断创建OpenGl设备是否成功。 6、判断OpenGl
45、设备是否启动。 7、判断初始化是否成功。 设置窗口的消息相应: LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) WM_ACTIVATE 监视Avtivate消息。 WM_SYSCOMMAND 用来截获系统消息。 WM_CLOSE 关闭消息。 WM_COMMAND 控制消息。IDM_EXIT 关闭窗口。 WM_KEYDOWN 键盘响应消息 设置L键,切换三角形填充模式与接线框模式。 WM_SIZE 调整窗口大小,重新刷新屏幕。
46、 其他情况下, DefWindowProc(hWnd, message, wParam, lParam);不然GDI的绘图会与OpenGL冲突,并且阻止程序空闲。 函数ReSizeGLScene(GLsizei width, GLsizei height)用于调整窗口大小时刷新屏幕。 函数KillGLWindow(GLvoid)用于释放窗口,其中判断是否能够释放DC与RC设备,判断是否能够删除RC,判断是否能够释放DC设备,是否能够删除窗口,是否成功能够注册OpenGL类. 4.3 各个类(Class)、结构体(struct)详细设计说明:
47、 4.3.1 结构体CVector3 任何像素点在三维空间内都存在着他自己的坐标值,该结构体用来定义空间点的存在。 在该结构体定义的头文件中,重载了一些基本的运算符操作。 定义出计算点到原点距离的成员函数ComputeLength()。 在Vector3.cpp中,定义给出了计算两个点乘积的函数DotProduct(); 计算向量积,也就是交叉乘积的函数CrossProduct(); 计算向量大小的函数Magnitude(); 单位规格化向量的函数Normalize(); 判断两点是否同一边的函数SameSign(); 判断点是否在射线上的函数PntFrontOrBa
48、ck();PntFrontOrBack2(); 计算两点之间距离的函数Dist(); 以上列出的成员函数,一些并没有被用到,只是作为完善,不时之需。 4.3.2 类CTexture 该类用来控制纹理的映射。 该类定义如下: class CTexture { GLuint m_nTxt; public: inline GLint GetTxtID(){return m_nTxt;} int LoadGLTextures(char *Filename); CTexture() { m_nTxt = -1; } ~CTexture() {
49、 if(m_nTxt!=-1) glDeleteTextures(1,&m_nTxt); } }; 其中主要包括了,读取bmp纹理文件的函数LoadGLTextures(char *Filename) 实现步骤如下: 1、 设置一个file型指针,以及指向内存纹理数据的指针 AUX_RGBImageRec *pImage 2、 出错判断,Filename为空的情况,文件没有能够读取到的情况。 3、 读取bitmap在glaux.lib中使用的辅助存储函数 auxDIBImageLoad(Filename); 4、确认读入了有效的图像数据 5、通过结合存储在数据队列中的纹理ID产生一个可用纹理 语句:glGenTextures(1, &m_nTxt); 6、放置从起点开始每一行的像素点的队列需求. 语句:glPixelStorei (GL_UNPACK_ALIGNMENT, 1); 7、从纹理索引中确定纹理,并初始化它们 语句:glBindTexture(GL_TEXTURE_2D, m_nTxt); 8、 建立Mipmaps (纹理的目标,纹理中颜色数量的成分,纹理图像的宽,纹理图像的高,像素点数据格式,内存中指向数据的指针) 语句:gluBuild






