为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

简单的三维场景制作毕业论文

2018-12-14 50页 doc 228KB 27阅读

用户头像

is_496339

暂无简介

举报
简单的三维场景制作毕业论文简单的三维场景制作毕业论文 简单的三维场景制作 摘要: 随着计算机图形学应用研究不断深入,以及应用不断扩大,虚拟场景的渲染 技术在应用当中占有相当重要的位置。虚拟场景渲染主要目的是真实地显示三维 (3D)环境,并且允许与人们在其中进行漫游观察。如何渲染一个逼真的虚拟场 景,是现在图形学领域的一个重要研究方向,OpenGL提供的强大图象处理功能, 让虚拟场景的渲染工作变得更加方便、灵活,渲染效果更加逼真。 本文尝试使用最简单又高效的算法,并力求渲染效果的仿真性达到应用的需 要。最终本文在地域生成渲染方面得到相对简单高效...
简单的三维场景制作毕业论文
简单的三维场景制作毕业 简单的三维场景制作 摘要: 随着计算机图形学应用研究不断深入,以及应用不断扩大,虚拟场景的渲染 技术在应用当中占有相当重要的位置。虚拟场景渲染主要目的是真实地显示三维 (3D)环境,并且允许与人们在其中进行漫游观察。如何渲染一个逼真的虚拟场 景,是现在图形学领域的一个重要研究方向,OpenGL提供的强大图象处理功能, 让虚拟场景的渲染工作变得更加方便、灵活,渲染效果更加逼真。 本文尝试使用最简单又高效的算法,并力求渲染效果的仿真性达到应用的需 要。最终本文在地域生成渲染方面得到相对简单高效又效果逼真的渲染方法,并 基于这些算法以及OpenGL提供的强大功能,开发了一个逼真的室外场景,并在 这个虚拟场景中进行漫游观察。 关键词:图形学 虚拟场景 OpenGL Abstract: With the application of computer graphics deeper study ,and continuously expand the application ,the technology of Virtual Scene Rendering occupies a very important position in the applications .The main purpose of Virtual scene rendering is to demonstrate the true three-dimensional(3D) environment ,and allow user to roam for observation .Exaggeration of a realistic virtual environment is an important research direction in graphics field ;OpenGL provides a powerful graphic processing function for virtual scene rendering work has become more convenient ,flsxible ,and simulation effects more realistic. We attempt to use the most simple and efficient algorithm ,and sought to exaggerate the effect of simulations to achieve application needs .Finally ,this paper foud a relatively simple and efficient results also realistic rendering method of Terrain Generation .Based on these algorithms ,and the powerful functions OpenGL provided ,we developed a system which shows realistic outdoor scenes .And user can go in the virtual scene to toam for observation. Keywords:Computer Graphics ,Virtual Scene ,OpenGL(Open Graphic Library) 毕业设计(论文)原创性声明和使用授权说明 原创性声明 本人郑重承诺:所呈交的毕业设计,论文,~是我个人在指导教师的指导下进行的研究工作及取得的成果。尽我所知~除文中特别加以标注和致谢的地方外~不包含其他人或组织已经发表或公布过的研究成果~也不包含我为获得 及其它教育机构的学位或学历而使用过的材料。对本研究提供过帮助和做出过贡献的个人或集体~均已在文中作了明确的说明并表示了谢意。 作 者 签 名: 日 期: 指导教师签名: 日 期: 使用授权说明 本人完全了解 大学关于收集、保存、使用毕业设计,论文,的规定~即:按照学校要求提交毕业设计,论文,的印刷本和电子版本,学校有权保存毕业设计,论文,的印刷本和电子版~并提供目录检索与阅览服务,学校可以采用影印、缩印、数字化或其它复制手段保存论文,在不以赢利为目的前提下~学校可以公布论文的部分或全部内容。 作者签名: 日 期: 学位论文原创性声明 本人郑重声明:所呈交的论文是本人在导师的指导下独立进行研究所取得的研究成果。除了文中特别加以标注引用的内容外~本论文不包含任何其他个人或集体已经发表或撰写的成果作品。对本文的研究做出重要贡献的个人和集体~均已在文中以明确方式标明。本人完全意识到本声明的法律后果由本人承担。 作者签名: 日期: 年 月 日 学位论文版权使用授权书 本学位论文作者完全了解学校有关保留、使用学位论文的规定~同意学校保留并向国家有关部门或机构送交论文的复印件和电子版~允许论文被查阅和借阅。本人授权 大学可以将本学位论文的全部或部分内容编入有关数据库进行检索~可以采用影印、缩印或扫描等复制手段保存和汇编本学位论文。 涉密论文按学校规定处理。 作者签名: 日期: 年 月 日 导师签名: 日期: 年 月 日 指导教师评阅书 指导教师评价: 一、撰写,设计,过程 1、学生在论文,设计,过程中的治学态度、工作精神 ? 优 ? 良 ? 中 ? 及格 ? 不及格 2、学生掌握专业知识、技能的扎实程度 ? 优 ? 良 ? 中 ? 及格 ? 不及格 3、学生综合运用所学知识和专业技能分析和解决问的能力 ? 优 ? 良 ? 中 ? 及格 ? 不及格 4、研究方法的科学性,技术线路的可行性,设计的合理性 ? 优 ? 良 ? 中 ? 及格 ? 不及格 5、完成毕业论文,设计,期间的出勤情况 ? 优 ? 良 ? 中 ? 及格 ? 不及格 二、论文,设计,质量 1、论文,设计,的整体结构是否符合撰写规范, ? 优 ? 良 ? 中 ? 及格 ? 不及格 2、是否完成指定的论文,设计,任务,包括装订及附件,, ? 优 ? 良 ? 中 ? 及格 ? 不及格 三、论文,设计,水平 1、论文,设计,的理论意义或对解决实际问题的指导意义 ? 优 ? 良 ? 中 ? 及格 ? 不及格 2、论文的观念是否有新意,设计是否有创意, ? 优 ? 良 ? 中 ? 及格 ? 不及格 3、论文,设计说明书,所体现的整体水平 ? 优 ? 良 ? 中 ? 及格 ? 不及格 建议成绩:? 优 ? 良 ? 中 ? 及格 ? 不及格 (在所选等级前的?内画“?”) 指导教师: (签名) 单位: (盖章) 年 月 日 评阅教师评阅书 评阅教师评价: 一、论文,设计,质量 1、论文,设计,的整体结构是否符合撰写规范, ? 优 ? 良 ? 中 ? 及格 ? 不及格 2、是否完成指定的论文,设计,任务,包括装订及附件,, ? 优 ? 良 ? 中 ? 及格 ? 不及格 二、论文,设计,水平 1、论文,设计,的理论意义或对解决实际问题的指导意义 ? 优 ? 良 ? 中 ? 及格 ? 不及格 2、论文的观念是否有新意,设计是否有创意, ? 优 ? 良 ? 中 ? 及格 ? 不及格 3、论文,设计说明书,所体现的整体水平 ? 优 ? 良 ? 中 ? 及格 ? 不及格 建议成绩:? 优 ? 良 ? 中 ? 及格 ? 不及格 (在所选等级前的?内画“?”) 评阅教师: (签名) 单位: (盖章) 年 月 日 教研室(或答辩小组)及教学系意见 教研室(或答辩小组)评价: 一、答辩过程 1、毕业论文,设计,的基本要点和见解的叙述情况 ? 优 ? 良 ? 中 ? 及格 ? 不及格 2、对答辩问题的反应、理解、表达情况 ? 优 ? 良 ? 中 ? 及格 ? 不及格 3、学生答辩过程中的精神状态 ? 优 ? 良 ? 中 ? 及格 ? 不及格 二、论文,设计,质量 1、论文,设计,的整体结构是否符合撰写规范, ? 优 ? 良 ? 中 ? 及格 ? 不及格 2、是否完成指定的论文,设计,任务,包括装订及附件,, ? 优 ? 良 ? 中 ? 及格 ? 不及格 三、论文,设计,水平 1、论文,设计,的理论意义或对解决实际问题的指导意义 ? 优 ? 良 ? 中 ? 及格 ? 不及格 2、论文的观念是否有新意,设计是否有创意, ? 优 ? 良 ? 中 ? 及格 ? 不及格 3、论文,设计说明书,所体现的整体水平 ? 优 ? 良 ? 中 ? 及格 ? 不及格 评定成绩:? 优 ? 良 ? 中 ? 及格 ? 不及格 (在所选等级前的?内画“?”) 教研室主任(或答辩小组组长): (签名) 年 月 日 教学系意见: 系主任: (签名) 年 月 日 前言: 随着计算机的不断地飞速发展,计算机应用已经深入渗透到人们日常生活的每一个角落。计算机图形学作为计算机计算机科学与技术学科的一个重要分支已经经历了近40年的发展历程。一方面作为一个学科,计算机图形学在图形算法,图形软件与图形硬件三方面取得了长足的进步,成为当代几乎所有科学和工程技术领域用来加强信息理解和传递的技术和工具。另一方面,计算机图形学的硬件和软件本身已发展成为一个巨大的产业,1996年总产值达500亿美元,预计到2000年将达到1000亿美元。因此,当前全世界从事计算机图形学研究、应用和产业的队伍十分庞大,这也是为什么每年参加SIG-GRAPH年会的人数多达3,4万人的理由。越来越多的计算机应用都向着可视化的方向迈进。计算机图形学主要是研究图形(图像)的计算机生成,其研究方向众多。在图形基础研究方面可归纳为两个主要方向,即建模(modeling)技术(又称"造型技术")和绘制(rendering)技术。建模技术又可分为两大分支,即计算机辅助几何设计和自然景物建模。计算机辅助几何设计追求建模的精确度、可靠性和建模的速度;自然景物建模追求建模的逼真度和速度。计算机图形学中的绘制技术是指基于光栅图形显示技术的"真实感图形"绘制技术,包括各种光照模型、明暗(shading)处理和纹理生成等内容。绘制技术追求的是真实感(逼真度)和绘制速度。综合上述两大研究方向的追求目标可以看出,计算机图形学研究水平的高低就是反映在"真实感"和"速度"的高低以及两者的结合上,也就是既要逼真地反映客观世界的对象,又能高速地、通常又称"实时"地绘制它们。众所周知,"真实感"与"实时性"是一对尖锐的矛盾,如何解决这一矛盾是当代计算机图形学工作者奋斗的目标。综观SIGGRAPH 96的大会论文报告、专题讨论会内容及产品展览会,明显感到计算机图形学的主攻方向不再是孤立地追求图形的真实感和绘制的实时性,而是把重点转移到如何把两者结合在一起,即向更高的目标迈进。操作系统,应用软件,计算机游戏由以前的非图形的到绚丽的图形界面,从二维的到三维的,无不见证计算机图形学的迅猛发展,以及它给人们带来的各种便利。三维互动式场景建模更是计算机三维图形应用所必须解决的问题。OpenGL是优秀且功能强大的图形库,利用OpenGL进行三维建模,从而能够实现各种仿真。OpenGL作为一项开放性的、主攻专业图形应用和3D游戏开发的图形API,即便其发展处于目前的低潮状态,OpenGL仍然牢牢把持着专业绘图领域,而DirectX在此毫无竞争力,功能更强大的OpenGL 2.0无疑将继续保持垄断性地位。因此,研究OpenGL图形库的结构和实现原理,深入了解OpenGL的各种机制,有一定的商业价值。 1 绪论 1.1 课题的来源 随着计算机的普及,计算机图形已不再是稀罕物,它是所有计算机用户界面的一个组成部分,并且对于二维(2D)、三维(3D)和更高维对象的可视化是不可或缺的,诸如教育、科学、工程、医学、商业、军事、广告和娱乐等各种各样的领域都离不开计算机图形。正因为计算机图形与我们日常生活如此密切,美国SGI公司为图形工作站开发了功能强大的三维图形和模型库――OpenGL(即开放性图形库,Open Graphics Library)。而这个图形学领域的事实标准诞生,又更加促进了图形学应用范围迅速扩大,其中很重要的影响就是在简单的物品摆放和材质渲染方面。如何渲染出一个逼真的场景,是现在图形学领域一个重要的研究方向。OpenGL提供强大的图形处理功能,使简单的三维场景制作变得方便、灵活,效果更加逼真。本文基于OpenGL开发了一个简单的三维场景,并在这个场景中添加了材质渲染,灯光,运动轨迹效果。 1.2 三维建模的意义 20世纪中后期,随着计算机技术的飞速发展,计算机描述现实世界的方式越来越丰富:从声音到图像再到视频,计算机所能表示的世界越来越复杂。三维模型作为一种新的媒体形式进入了人们的生活、学习和工作中,并且很快被普通大众所接受。它在影视娱乐,建筑,机械制造,医疗,军事,电子商务,虚拟现实,考古等很多方面都得到了越来越广泛的应用。 在影视娱乐中,基于三维建模技术的CG(计算机图形学)影片(如图1.1)层出不穷,CG技术已经在这个领域得到了广泛的应用。将三维建模技术应用到电影,可以让电影导演能有更广泛的想象空间来实现现实生活中无法完成的场面,从而降低影片制作中的风险和为影片减少开支。 图1.1 影片“最终幻想”场景 图1.2 游戏“完美世界”人物模型 CPU的发展一直跟不上GPU的发展,这是三维游戏推动的结果。三维游戏虽然和民生关系不大,但是确是一种具有强有力号召力的娱乐方式。计算机图形学技术的发展很大程度上得益于三维游戏的发展需求,随着GPU的不断发展,三维游戏中的画面越来越美观和真实。图1.2展示了一个游戏人物模型,这种简单的人物模型最开始就是由网格模型组成,再通过对模型贴图和渲染而达到这种效果。很明显,在这种大型的游戏中数据的处理过程是非常复杂的。 三维建模技术也广泛应用电子商务领域。如图1.3所示是耐克鞋官方主页上提供的一种服务,客户可以通过IE浏览器来设计和订购自己的产品。这是目前电子商务领域比较热门的一种服务,其中就涉及到三维建模以及模型的传输等技术。 图1.3 耐克官方主页提供的订购服务 图1.4 文物模型的虚拟拼接 在考古学领域,可以对破损的文物进行采样和收集数据,然后通过三维建模技术复原文物模型。图1.4现实的是北京大学虚拟化实验室对洛阳龙门石窟古阳洞高树龛中的破损佛像的数据进行搜集然后再三维建模从而得到的完整的模型。 在医疗领域,三维建模技术很久以前就得到了广泛的应用。使用CT或者MRI(核磁共震)技术可以得到人体各个部位的横截面的图像,然后通过三维建模技术可以对这些图像合并成各种人体组织的三维模型。这种三维模型能比二维的平面图像更加准确,从而让医务人员能更加准确的对病情进行诊断。 在建筑和机械制造等领域,三维建模技术的作用更不用说,CAD技术在工业设计领域已经应用了几十年。在军事和虚拟现实等领域三维建模技术也有广泛的应用,例如航天员可以在虚拟的航天环境下进行模拟训练。 上面的里例子充分说明了三维建模技术已经深入到社会各个方面,对人们的生活已经产生了巨大的影响。因此,三维建模具有非常重要的意义。 1.3 三维场景建立概述及其意义 随着计算机信息技术的高速发展,计算机三维动画技术被广泛地应用于许多方面。利用计算机和三维动画软件的强大功能,通过三维建模、材质及动画 的设定可以将真实世界中的各种对象在计算机中真实再现。 但是三维动画在中国的宣传力度不是很大,许多人还没有概念。外国动画垄断中国市场的现状与后果令人不安,但真正惊人的是背后的损失。首先是民族精神的弱化。动画的受众主体是青少年,如长期接受国外动画的影响,其价值观、审美观和趣味观必然发生偏移;其次是经济利益的损失;第三是动画产业的衰弱。 所以该课题的应用前景十分广阔,不但锻炼了三维动画的实现能力,还提高了编程的总体思路,大大提高了编程水平。更何况中国市场的匮乏,这无疑是一块很大的蛋糕~ 1.4 本文的主要工作 本论文的主要研究的内容是如何利用OpenGL开发出一个微型游戏。因此,所要解决的主要问题有四个:一是如何用数学方法建立所需三维场景的几何描述,并将它们输入到计算机中,这部分工作由三维实体造型系统来完成。场景的几何描述直接影响了图形的复杂性和图形绘制的计算耗费,选择合理的有效的数据表示和输入手段是极其重要。二是将三维几何描述转换为二维透视,通过场景的透视变换来完成。三是确定场景中的所有可视面,这需要使用隐藏面消除算法将视域之外或其它物体遮挡的不可见面消去。四是计算机场景中所有可见面的颜色,这就需要根据基于光学物理的光照明模型计算可见面投影到观察者眼中的光亮度的大小和色彩组成,并将它转换成适合图形设备的颜色值,从而确定投影画面上每一象素的颜色,最终生成图形。 2 几何三维建模技术概述 绪论已经讨论了本文的基本思路,并且说明了制作三维场景的前提是进行三维建模。在计算机图形学中,对于三维空间的点P(X,Y,Z),如果要将点P变换到 ’’’’一个新的点P,设其坐标为(X,Y,Z),则其解析表达式如下: 'XaXbYcZ,,, 'YeXfYgZ,,, 'ZhXiYjZ,,, 如果将上面的式子改用矩阵的形式表示,则可以写为: aeh,, ,,''',,XYZXYZbfi, ,,,,,, ,,cgj,, 其中矩阵[X Y Z]称为几何信息矩阵,或者称为位置矢量矩阵;3×3的矩阵aeh,, ,,’ ’’bfi称为变换矩阵;而[XY Z]称为变换后的位置矢量矩阵。显然,变换,, ,,cgj,, 后的矩阵除了和原来的位置矢量有关外,同时还与变换矩阵的每一个元素的大小有关系。 一般来说可以采用齐次坐标来表示空间上的一个点,则有四个分量,即P(x,y,z,1)。显然,相应的变换矩阵应该是4×4的方阵。习惯上用小写字母[x y z 1]来表示变换前的空间点的位置矢量;用大写字母[X Y Z H]来表示变换后的空间点的位置矢量。通常情况下,变换后的第四个分量H往往不是1,为了用正常化的齐次坐标表示变换后的位置矢量,可以用H去除四个分量,得到正常化的齐次坐标,并用带字母表示为: ''',, xyzTXYZHXHYHZHxyz1///11,,,,,,,,,,,对于空间中的三维体,例如线段、平面、立体,都可以将其n个顶点以点集的形 ’式用齐次坐标写成n行四列矩阵P,则三维图形变换可以表示为P=PT。其中, ’P是空间点集的齐次坐标矩阵;P是经过变换后而且是以正常化的齐次坐标表示的空间点集矩阵;T则是4×4阶的变换矩阵,其一般形式为: aehp,, ,,bfiq,, ,,cgjr ,,lmns,, 根据变换矩阵中各个元素在变换过程中的作用,可以将变换矩阵划分成四个子矩阵: 3331,,,, ,,1311,,,, 概括起来,这四个矩阵分别对应的作用是: (1) 3×3矩阵是产生比例变换,对称变化,旋转变换和错切变换。 (2) 1×3矩阵是产生平移变换。 (3) 3×1矩阵产生透视变换。 (4) 1×1矩阵将产生整体的比例变换。 下面将介绍几种常见的三维变换矩阵: 2.1 比例变换 比例变换主要用于三维模型的放大和缩小操作。对于模型的放大和缩小可以分为全局比例放大和轴向比例放大。对于全比例变换,其变换矩阵为: 1000,, ,,0100,, ,,0010 ,,000s,, 其变换过程如下: ''',, xyzTXYZSXSYSZSxyz1///11,,,,,,,,,,, 01,,SS,1当时,模型沿三个轴向等比例放大;当时,则模型沿着三个轴 S,0向等比例缩小;当时模型将产生对坐标原点的对称变换加比例变换。 对于轴向比例变换,变换矩阵中主对角线上的元素a,f,j,s的作用是使空间几何元素产生比例变换,其中a,f,j分别为X,Y,Z轴三个方向的缩放因子。当a=f=j时三个轴向的缩放比例相同;否则三个轴方向的缩放比例不相同。 2.2 对称变换 对称变换可以分成如下三种变换方式: 1000,, ,,0100,,(1) 对称于XOY坐标面的对称变换,变换矩阵为:;其变换过T,,,0010, ,,0001,, XYZxyzTxyz111,,,程为:。 ,,,,,, ,1000,, ,,0100,,(2) 对称于YOZ坐标面的对称变换,变换矩阵为:;其变换过T,,,0010 ,,0001,, XYZxyzTxyz111,,,程为:。 ,,,,,, 1000,, ,,0100,,,(3) 对称于XOZ坐标面的对称变换,变换矩阵为:;其变换过T,,,0010 ,,0001,, XYZxyzTxyz111,,,程为:。 ,,,,,, 2.3 平移变换 平移变换是指在空间坐标中的模型从一个位置移动到另外一个位置时,其形状、大小均不发生改变的变换。其变换矩阵如下: 1000,, ,,0100,, T,,,0010 ,,lmn1,, 很明显,l,m,n三个元素分别产生沿X轴、Y轴和Z轴方向的平移,三为空间的点的平移过程如下: ''',, xyzTxlymznxyz111,,,,,,,,,,,2.4 旋转变换 [5]旋转变换,很明显是指对于空间中的模型沿着某个轴或者某条直线旋转。可以将其分成如下几种情况,如图2.1所示的旋转变换的示意图: 1000,, ,,0cossin0,,,,,(1) 绕X轴旋转角度。其变换矩阵为:。 T,,,0sincos0,,, ,,0001,, cos0sin0,,,,, ,,0100,,,(2) 绕Y轴旋转角度。其变换矩阵为:。 T,,,sin0cos0,,,,0001,, cossin00,,,, ,,,sincos00,,,,,(3) 绕Z轴旋转角度。其变换矩阵为:。 T,,,0010 ,,0001,, 图2.1 三维旋转变换示意图 (4) 绕过原点的任意倾斜直线旋转。假设该直线为ON,它对三个坐标轴方向的余弦已知。设三个方向的余弦分别为;可以先在ON上取一个单位矢量n、n、n123 OK,将OK先绕OY旋转,再绕OZ旋转,使得OK和ON重合,这样就可以得,,12 到ON轴的方向余弦和、的关系如下: ,,12 nnn1sincossinsincos1,,,,,,,,,,12312121 ,这样可以得到最终的绕ON轴旋转变换角度的变换矩阵如下: 22,,nnnnnnnn,,,,,,(1)cos(1cos)sin(1cos)sin0,,,,,11123132,,22nnnnnnnn(1cos)sin(1)cos(1cos)sin0,,,,,,,,,,12322231,,T, 22,,nnnnnnnn(1cos)sin(1cos)sin(1)cos0,,,,,,,,,,,13223133,,0001,, 3 OpenGL三维建模原理概述 3.1 OpenGL简介 3.1.1 什么是OpenGL OpenGL是个专业的3D程序接口,是一个功能强大,调用方便的底层3D图形库。OpenGL的前身是SGI公司为其图形工作站开发的IRIS GL。IRIS GL是一个工业标准的3D图形软件接口,功能虽然强大但是移植性不好,于是SGI公司便在IRIS GL的基础上开发了OpenGL。OpenGL的英文全称是“Open Graphics Library”,顾名思义,OpenGL便是“开放的图形程序接口”。 3.1.2 OpenGL概览 OpenGL是一个图形硬件的软件接口,包括大约250个函数(其中200个在OpenGL核心中,其他50个在OpenGL核心库中),您可以使用它们来指定物体和操作,以创建交互式三维应用程序。它的主要作用是将二维或三维的对象绘入一个帧缓冲区中。对象被描述为一系列的顶点(用来定义几何对象)或像素(用来定义图像)。OpenGL对数据进行几个步骤的处理从而将其转换成像素,这些像素将在帧缓冲区中形成最终需要的图形。 OpenGL是一个独立于硬件的高效接口,可以在很多平台上实现。因此,OpenGL中没有执行窗口任务或获取用户输入的函数,程序员必须通过窗口系统来控制硬件。另外OpenGL也没有提供用于面熟三维物体模型(如汽车,人体,飞机或分子模型)的高级函数。在OpenGL中,只能使用几种几何图元(geometric primitive)(点,直线,多边形)来构建所需的模型。 当然,可以在OpenGL之上建立提供上述高级特性的复杂库。OpenGL实用库(GLU,OpenGL Utility Library)提供了很多建模(modeling)特性,如二次 曲面,NURBS曲线和曲面。GLU是OpenGL实现的标准组成部分。 OpenGL在屏幕上渲染图像时执行的主要图形操作: (1)使用几何图元建立模型,即是说所建立的模型由图元构成(使用若干个三角形来建立曲面),这里的几何图元包括:点,直线,多边形,图形和位图。 (2)在三维空间排列物体,选择观察场景的有利位置,也即一系列的变换。 (3)计算所有物体的颜色。物体的颜色可以由程序来指定,根据光照来确定,进行纹理贴图,或者上述操作的组合来实现的。 (4)将物体的数学描述和相关颜色信息转换为屏幕像素,这被称为光栅化(rasterization)。 在上述步骤中,OpenGL还可以执行其他操作,如隐藏面(线)消除。另外,在场景被光栅化后,但被绘制到屏幕之前,可以对像素数据执行某些操作。 3.1.3 OpenGL的基本特点 从程序开发人员的角度来看,OpenGL是一组绘图命令的API集合。利用这些API能够方便地描述二维和三维几何物体,并控制这些物体按某种方式绘制到显示缓冲区中。OpenGL的API集提供了物体描述、平移、旋转、缩放、光照、纹理、材质、象素、位图、文字、交互以及提高显示性能等方面的功能,基本涵盖了开发二、三维图形程序所需的各个方面。与一般的图形开发工具相比,OpenGL具有以下几个突出特点: (1) 应用广泛 OpenGL是目前最主要的二、三维交互式图形应用程序开发环境,已成为业界最受推荐的图形应用编程接口。自从1992年发表以来,OpenGL已被广泛地应用于CAD/CAM、三维动画、数字图象处理以及虚拟现实等领域,Kinetix公司的3D Studio Max就是突出的代表。无论是在PC机上,还是在工作站甚至是大型机和超级计算机上,OpenGL都能表现出它的高性能和强大威力。 (2) 跨平台性 OpenGL能够在几乎所有的主流操作系统上运行,包括UNIX、Mac OS、OS/2、Windows NT/9X/ME/2K/XP、Linux等;也能够与其中绝大多数的窗口系统一起工作。 (3) 高质量和高性能 无论是在CAD/CAM、三维动画还是可视化仿真等领域,OpenGL高质量和高效率的图形生成能力都能得到充分的体现。在这些领域中,开发人员可以利用OpenGL制作出效果逼真的二、三维图象来。 (4) 出色的编程特性 OpenGL体系结构评审委员会(ARB)独立负责管理OpenGL的规范,这使得OpenGL 具有充分的独立性。OpenGL在各种平台上已有多年的应用实践,加上严格的规范控制,因此OpenGL具有良好的稳定性。良好的前瞻性、伸缩性和易使用性等也是OpenGL的突出编程特点。 (5) 网络透明性 建立在客户/服务器模型上的网络透明性是OpenGL的固有特性,它允许一个运行在工作站上的进程在本机或通过网络在远程工作站上显示图形。利用这种透明性能够均衡地共同承担图形应用任务的各工作站的负荷,也能使得没有图形功能的服务器能够使用图形工具。 3.2 OpenGL的体系结构 OpenGL是一套图形标准,它严格按照计算机图形学原理设计而成,符合光学和视觉原理,非常适合可视化仿真系统。 首先,在OpenGL中允许视景对象用图形方式表达,如由物体表面顶点坐标集合构成的几何模型,这类图形数据含有丰富的几何信息,得到的仿真图像能充分表达出其形体特征;而且在OpenGL中有针对三维坐标表示的顶点的几何变换,通过该变换可使顶点在三维空间内进行平移和旋转,对于由顶点的集合表达的物体则可以实现其在空间的各种运动。 其次,OpenGL通过光照处理能表达出物体的三维特性,其光照模型是整体光照模型,它把顶点到光源的距离、顶点到光源的方向向量以及顶点到视点的方向向量等参数代入该模型,计算顶点颜色。因此,可视化仿真图像的颜色体现着物体与视点以及光源之间的空间位置关系,具有很强的三维效果。 另外,为弥补图形方法难于生成复杂自然背景的不足,OpenGL提供了对图像数据的使用方法,即直接对图像数据读、写和拷贝,或者把图像数据定义为纹理与图形方法结合在一起生成视景图像以增强效果。为增强计算机系统三维图形的运算能力,有关厂家已研制出了专门对OpenGL进行加速的三维图形加速卡,其效果可与图形工作站相媲美。一个完整的窗口系统的OpenGL图形处理系统的结构为:最底层为图形硬件,第二层为操作系统,第三层为窗口系统,第四层为OpenGL,第五层为应用软件。如图3.1所示。 应用软件 OpenGL 窗口系统 操作系统 图形硬件 图3.1 OpenGL图形处理系统的层次结构 OpenGL的作用机制是客户(client)/服务器(sever)机制,即客户(用OpenGL绘制景物的应用程序)向服务器(即OpenGL内核)发布OpenGL命令,服务器则解释这些命令。大多数情况下,客户和服务器在同一机器上运行。正是OpenGL的这种客户/服务器机制,OpenGL可以十分方便地在网络环境下使用。因此Windows NT下的OpenGL是网络透明的。 图3.2给出了网络下的OpenGL体系结构,在实际操作中,应用程序发出OpenGL命令,由动态链接库OpenGL32.dll接受打包后,发送到服务器端的WINSRV.DLL,然后由它通过DDI层发往视频显示驱动程序。如果系统安装了硬件加速器,则由硬件相关的DDI来处理。从程序员的角度看,在编写基于Windows的OpenGL应用程序之前必须掌握两点,一个是OpenGL本身是一个复杂的系统,这可以通过简化的OpenGL辅助库函数来学习和掌握;另一个是必须清楚地了解和掌握Windows与OpenGL的接口。 应 用 程 序 OpenGL32.DLL GDI32.DLL 可安装的客户端 驱动程序 WINSRV.DLL 相关DDI WIN32.DDI 视频显示驱动程序 图3.2 OpenGL/NT体系结构 3.3 OpenGL的渲染流水线 可以把OpenGL的工作过程看成是一条生产流水线,原料是场景,物体的顶点,表面细节等信息,产品是看起来有三维感觉的平面位图。在OpenGL中是以面边界(B-REP)模型来描述物体的,或者说是使用多边形造型系统。在OpenGL中每个物体都是由一组平面构成,这组平面记录了该物体的表面,需要用户提供围绕平面边缘的线段的顶点参数,平面内图案的位图两组信息,术语称为Vertex(顶点),Texture(纹理)。显然这些平面越小画出不断的物体越逼真。 OpenGL生成三维图形可以分成以下几个步骤: (1) 坐标变换,生成基本图元:根据基本图形单元建立景物模型,并对所建立的模型进行数学描述。 (2) 裁剪变换:把景物模型放在三维空间核实的位置上,并且设置视点一观察所感兴趣的场景。 (3) 色彩与光照:根据应用要求确定色彩,同时确定光照条件。 (4) 光栅化,生成图形片段:把景物模型的数学描述及其色彩信息转换成计算机屏幕上的像素点。 OpenGL显示三维图形流程图如图3.3所示。 当命令进入流程时,你可以选用两种方法对其进行处理:一种是通过流程立即执行这些命令;另一种是将其中一些命令组织到一个“显示列表”,过一段时间再执行它们。 流程中的“评价器”阶段通过将输入值赋给多项式命令提供了一种非常有效的方法来生成几何曲线和曲面的近似值。接下来的“各顶点操作”阶段主要是处理OpenGL的几何图元:点,线段和多边形。所有这些图元均由顶点来描述。顶点可以被转换和照亮,接下来图元图元被剪切到视口,为下一阶段做好准备。 “光栅化”生成了一系列的帧缓冲区地址以及相应的用于描述点、线段或多边形的二维值。这些生成的“片断”将被送到最后一个阶段—“各片断的操作”,这一阶段是数据以像素形式存入“帧缓冲区”之前的最后操作。这些操作包括根据 几何物体 顶点 评价器 顶点操作 显示列表 光栅化 基本操作 帧缓存器 图形 像素 像素操作 纹理存贮 器 图3.3 OpenGL渲染流水线 帧缓冲区中原有的深度值(用于深度缓存操作)与输入值而有条件地更新帧缓冲区的操作,还包括对输入的像素颜色与已存储的颜色所进行的融合操作,对像素值所进行的屏蔽操作及其他的逻辑操作。 数据是以像素形式而非顶点形式输入的。这些数据可以用来描述一个用于纹理映射的图像,它将跳过第一阶段(如前面所述),而通过“像素操作”阶段作为像素来处理。这一阶段的处理将导致两种结果:其一是被存入“纹理存储器”,以备光栅化阶段所用;其二是直接被光栅化。后者所形成的片断将被存入帧缓冲区,就好象它们是由几何数据生成的一样。 一个OpenGL应用程序可以获得OpenGL状态的所有元素,其中包括纹理存储器的内容,甚至还包括帧缓冲区的内容。 3.4 OpenGL开发库的基本组成 3.4.1 OpenGL开发组件 Windows 9x/NT下的OpenGL组件有2种,一种是SGI公司提供的,一种是Microsoft公司提供的。两者的开发库大体上,没有什么区别,都是有3大部分组成。 (1)函数的说明文件。gl.h,glu.h,glut.h和glaux.h. (2)静态链接库文件。glu32.lib,glut32.lib,glaux.lib和opengl32.lib。 (3)动态链接库文件。glu.dll,glu32.dll,glut.dll,glut32.dll 和opengl32.dll。 3.4.2 OpenGL的库函数 开发基于OpenGL的应用程序开发基于OpenGL的应用程序,必须先了解OpenGL的库函数。在OpenGL中有115个核心函数,这些函数是最基本的,他们可以在任何OpenGL工作平台上应用。这些函数用于建立各种各样的形体,产生光照效果,进行反走样纹理映射,投影变换等操作。由于这些核心函数有许多种形式并能够接受不同类型的参数,实际上这些函数可以派生出300多个函数。 OpenGL库函数的命名方式非常有规律,每个库函数均有前缀gl、glu、aux,分别表示该函数属于OpenGL基本库、实用库和辅助库。 OpenGL的库函数大致可以分为六类: (1) OpenGL核心库:包含有115个函数,函数名的前缀为gl。这部分函数用于常规的、核心的图形处理。由于许多函数可以接收不同数据类型 的参数,因此派生出来的函数原形多达300多个。 (2) OpenGL实用库:包含有43个函数,函数名的前缀为glu。这部分函数通过 调用核心库的函数,为开发者提供相对简单的用法,实现一些较为复杂的操作。如:坐标变换、纹理映射、绘制椭球、茶壶等简单多边形。OpenGL中的核心库和实用库可以在所有的OpenGL平台上运行。 (3) OpenGL辅助库:包含有31个函数,函数名前缀为aux。这部分函数提供窗口管理、输入输出处理以及绘制一些简单三维物体。OpenGL中的辅助库不能在所有的OpenGL平台上运行。 (4) OpenGL工具库:包含大约30多个函数,函数名前缀为glut。这部分函数主要提供基于窗口的工具,如:多窗口绘制、空消息和定时器,以及一些绘制较复杂物体的函数。由于glut中的窗口管理函数是不依赖于运行环境的,因此OpenGL中的工具库可以在所有的OpenGL平台上运行。 (5) Windows专用库:包含有16个函数,函数名前缀为wgl。这部分函数主要用于连接OpenGL和Windows 95/NT,以弥补OpenGL在文本方面的不 足。Windows专用库只能用于Windows 95/98/NT环境中。 (6) Win32 API函数库:包含有6个函数,函数名无专用前缀。这部分函数主要用于处理像素存储格式和双帧缓存。这6个函数将替换Windows GDI中原有的同样的函数。Win32API函数库只能用于Windows 95/98/NT环境中。 3.5 OpenGL程序运行方式 运行OpenGL主要有以下3种方式: 3.5.1 OpenGL硬件加速方式 一些显示芯片如3Dalbs公司的GliNT进行了优化,OpenGL的大部分功能均可由硬件实现,仅有少量功能由操作系统开完成。这样极大地提高了图形显示的性能,并且能够获得工作站级的图形效果,但是这样的图形硬件价格十分昂贵,非一般用户所能承担。 3.5.2三维图形加速模式 一些中低档的图形芯片往往也具备一定的三维加速功能,由硬件来完成一些较为复杂的图形操作。一些重要的OpenGL操作,例如Z缓存等就能够直接由显示卡硬件来完成,显示卡所不能支持的图形功能,则通过软件模拟的方式在操作系统中进行模拟。采用这种方法,显示速度尽管无法与硬件加速方法相比,但与采用纯软件模拟方式相比,速度要快得多。 3.5.3纯软件模式 对于不具备三维加速功能的显示卡,要想运行OpenGL,只能采用纯软件模拟方式。由于所有复杂的OpenGL图形功能均通过主机来模拟,所以速度将会受到很大的影响。但正是由于有了软件模拟方式,才使得更多的用户能够领略OpenGL的强大功能,并能在硬件性能较差的机器上对对OpenGL进行开发。 采用了OpenGL技术,大大降低了开发高质量图形软件对软,硬件的依赖程度。 (1) OpenGL对硬件的要求如下: CPU:Pentinum或Pentinum Pro 时钟频率:90MHz以上 内存:16/32/64MB以上 硬盘:512MB以上 其它可选。 (2) OpenGL对软件环境的最低要求是: 操作系统:Windows NT4.0以上或Windows 95以上 OpenGL库:Visual Studio 5.0以上版本已包含该库。 以当前的计算机发展水平,达到以上软、硬件配置水平实在易之又易。由此对于OpenGL对系统的低要求也可略见一斑。 3.6动画 3.6.1问题的引入 对于看到过电影胶片或者学过Flash的人,这一部分应该是很容易理解。计算机能够想人们展示绚丽多姿的精彩画面,无论你是利用计算机搞平面设计,三维设计,看电影还是玩游戏,动画都是计算机图形学的一个重要组成部分。 在电影院里,屏幕上的运动画面是通过拍摄大量的图片,然后以每秒24帧的频率把它们投影到屏幕上来实现的。每一帧移动到镜头后的一个位置,接着快门打开,然后这一帧便被显示。 在影片切换到下一帧的一瞬间,快门关闭,接下来又打开显示下一帧,以此类推。尽管所看到的是每秒24帧不同的画面,但大脑会把它们混合成一段平滑的动画。(老式的卓别林电影每秒播放16帧,因此有比较明显的跳动现象。)在一般情况下,计算机图形屏幕每秒大约刷新(重绘画 面)60,76次,有些甚至每秒刷新120次。显然,60帧每秒比30帧每秒的效果更为平滑,而120 帧每秒的效果又强于60帧每秒。但是,如果刷新率超过120帧每秒,就有可能到达衰减点,具体取决于人眼感知的极限。 运动图片投影方法之所以可行的关键原因是每个帧在显示的时候已经完成绘制。假如试图用一个如下的程序来实现计算机动画,播放数以百万帧计的影片: Open_window(); for(i=0;i<1000000;i++) { Clear_the_window(); Draw_frame(i); th Wait_Until_a_24_of_a_second_is_over(); } 如果加上系统清除屏幕以及绘制一个典型的帧所需要的时间,这个程序将会产生越来越差的效果,具体取决于清除和绘图所占用的时间与1/24秒的接近程度。假如绘图时间差不多需要1/24 秒。那么第一个被绘制的物体在这1/24秒的时间内是可见的,并在屏幕上显示一幅实体图像。但是,这一帧中临近最后被绘制的物体将被立即清除,因为程序紧接着要显示下一帧。这就导致了 一个非常可怕的场景:在这1/24秒的绝大部分时间里,所看到的并不是最后被画好的物体,而是 被清除的背景。问题在于这个程序并不是显示已经完整画好的帧,所看到的是帧的绘制过程。 3.6.2双缓存技术 (1)什么是缓存 在opengl里你将经常看到帧缓存这个名词,可以是opengl的核心的吧。随着OpenGL的渲染而改变内容的那一部分图形内存区域叫做帧缓存(frame buffer)。 (2)双缓存技术 绝大多数OpenGL实现提供了双缓冲(硬件或软件),提供两个完整的颜色缓冲区。当一个 缓冲区被显示时,另一个正在进行绘图。当一个帧绘制完成后,两个缓冲区就进行交换。这样, 刚刚用来显示的那个缓冲区现在就用于绘图,反之亦然。这类似于循环播放两个帧的电影放映 机。当其中一帧被投影到屏幕上时,画家迅速擦掉并重绘那个当前未显示的帧。只要画家的动 作足够快,观众并不会注意到这种方式和事先画好所有的帧然后再投影的区别。电影放映机只 是简单地一幅又一幅地显示它们而已。使用双缓冲,每一帧只在绘图完成后才被显示,观众永远不会看到不完整的帧。 有了双缓存技术就可以对前面的程序进行修改了,用双缓冲平滑地显示动画: Open_window_in_double_buffer_mode(); for(i=0;i<1000000;i++) { Clear_the_window(); Draw_frame(i); Swap_the_buffers(); } 3.6.3暂停刷新 在有些OpenGL实现中,除了简单地交换显示和绘图缓冲区之外,还使用swap_the_buffer() 函数进行等待,直到当前的屏幕刷新周期结束,这样前一个缓冲区的内容就能够完整地显示。 这个函数还允许从头开始完整地显示新缓冲区。假定系统每秒刷新60次,意味着可以实现的最 快帧率是60帧每秒(fps)。 如果所有的帧都可以在不到1/60秒的时间内完成清除和绘制,动画将在这个帧率下非常平滑地显示。 如果帧的内容过于复杂,无法在1/60秒的时间内完成绘制,那会发生什么情况呢,此时每帧 被显示的次数将不止一次。例如,如果每帧需要1/45秒的时间才能完成绘制,而帧率是30fps, 这样每帧就有1/30秒-1/45秒,1/90秒(或者说三分之一)的空闲时间。 此外,视频刷新频率是固定的,这就有可能导致一些意想不到的性能问题。例如,在一台 刷新率为1/60秒并要求帧率固定的显示器上,可以在60fps、30fps、20fps、15fps、12fps(60/1、60/2、60/3、60/4、60/5、...)等帧率下运行。这意味着如果正在编写一个应用程序,并且逐渐 增加功能(假如应用程序是一个飞行模拟器,并且正在添加地面场景),最初所添加的每个特性 对总体性能不会有影响,仍然可以获得60fps的帧率。突然之间,当又增加了一个新特性之后, 系统无法在1/60秒的时间画完一帧中的所有物体,于是动画从60fps下降到30fps,因为系统错过 了第一次缓冲区交换的时间。当每帧的绘图时间超过1/30秒时,也会发生类似的事情,动画的帧 率将从30fps下降到20fps。 如果场景的复杂度接近于任一魔幻时间(指本例中的1/60秒、2/60秒、3/60秒等临界时间), 由于随机变化,有些帧会稍微多于这个时间,有些帧则稍微小于这个时间。这样,帧率便会变 得无规律,可能会导致视觉上的混乱。在这种情况下,如果无法对场景进行简化,使所有的帧 都足够快,最好有意增加一小段延迟,使它们都错过这个魔幻时间,统一到下一个更慢的固定 帧率。如果各个帧的复杂性具有极大的差异,可能需要采取一种更为复杂的方法。 3.6.4 动画,重绘,交换 说了这么多那么计算机中动画到底是什么呢,或者说是怎么样实现的呢,联想一下放电影或者你在制作flash动画,那么很快就可以得出结论。简单地说就是:动画,重绘,交换。 实时动画程序的结构与这里所描述的相差并不大。通常,对于每个帧而言,较之判断缓冲区的哪部分需要重绘,重新绘制整个缓冲区要更容易一些。对于诸如三维飞行模拟器这样的应用程序,情况更是如此。在这种应用程序中,飞机方向的略微改变就可能导致窗外所有物体的位置都发生改变。 在绝大多数动画中,场景中的物体简单地根据不同的转换进行重绘,例如根据观察者移动的视点、根据路上前行的汽车或根据一个略微旋转的物体。如果非绘图操作所涉及的重新计算量非常大,动画的帧率可能会降低。然而,可以使用swap_the_buffer()函数执行后的空闲时间进行这样的计算。 4 VC6.0下使用OpenGL实现三维场景建模 4.1建模说明 本文将详细介绍在Windows环境下如何使用VC++6.0中绘制基于MFC的单文档OpenGL三维场景的方法。本文实现的是一个三维地形漫游。 4.2理论基础 4.2.1渲染上下文(RC) OpenGL的绘图方式与Windows的一般绘图方式是不同的,其主要区别表现在以下3个方面: (1) Windows采用的是GDI绘图方式。 (2) OpenGL采用的是渲染上下文RC(Render Context,又称渲染描述符表)绘图。 (3) OpenGL使用的是特殊的像素格式。 在Windows中使用GDI绘图时必须指定在哪个设备上下文(Device Context,又称设备描述表)中绘制,同样地,在使用OpenGL函数时也必须指定一个所谓的渲染上下文。正如设备上下文DC要存储GDI的绘制环境信息如笔、刷和字体等,渲染上下文RC也必须存储OpenGL所需的渲染信息如像素格式等。渲染上下文主要由以下六个wgl函数来管理,下面分别对其进行介绍。 (1) HGLRC wglCreateContext(HDC hdc)该函数用来创建一个OpenGL可用的渲染上下文RC。Hdc必须是一个合法的支持至少16色的屏幕设备描述表DC或内存设备描述表的句柄。该函数在调用之前,设备描述表必须设置好适当的像素格式。成功创建渲染上下文之后,hdc可以被释放或删除。函数返回NULL值表示失败,否则返回值为渲染上下文的句柄。 (2) BOOL wglDeleteContext(HGLRC hglrc)该函数删除一个RC。一般应用程序在删除RC之前,应使它成为非现行RC。不过,删除一个现行RC也是可以的。此时,OpenGL系统冲掉等待的绘图命令并使之成为非现行RC,然后删除之。注意在试图删除一个属于别的线程的RC时会导致失败。 (3) HGLRC wglGetCurrentContext(void)该函数返回线程的现行RC,如果线程无现行RC则返回NULL。 (4) HDC wglGetCurrentDC(void)该函数返回与线程现行RC关联的DC,如果线程无现行RC则返回NULL。 (5) BOOL wglMakeCurrent(HDC hdc, HGLRC hglrc)该函数把hdc和hglrc关联起来,并使hglrc成为调用线程的现行RC。如果传给hglrc的值为NULL,则函数解除关联,并置线程的现行RC为非现行RC,此时忽略hdc参数。传给该函数的hdc可以不是调用wglCreateContext时使用的值,但是,它们所关联的设备 必须相同并且拥有相同的像素格式。注意,如果hglrc是另一个线程的现行RC,则调用失败。 (6) BOOL wglUseFontBitmaps(HDC hdc, DWORD dwFirst, DWORD dwCount, DWORD dwBase)该函数使用hdc的当前字体,创建一系列指定范围字符的显示表。可以利用这些显示表在OpenGL窗口画GDI文本。如果OpenGL窗口是双缓冲的,那么这是往后缓冲区中画GDI文本的唯一途径。 一般地,在使用单个RC的应用程序中,在相应WM_CREATE消息时创建RC,当WM_CLOSE或WM_DESTROY到来时再删除它。在使用OpenGL命令往窗口中绘图之前,必须先建立一个RC,并使之成为现行RC。OpenGL命令无需提供RC,它将自动使用现行RC。若无现行RC,OpenGL将简单地忽略所有的绘图命令。一个RC是指现行RC,这是针对调用线程而言的。一个线程在拥有现行RC进行绘图时,别的线程将无法同时绘图。一个线程一次只能拥有一个现行RC,但是可以拥有多个RC;一个RC也可以由多个线程共享,但是它每次只能在一个线程中是现行RC。在使用现行RC时,不应该释放或者删除与之关联的DC。如果应用程序在整个生命期内保持一个现行RC,则应用程序也一直占有一个DC资源。注意,Windows系统只有有限的DC资源。 下面介绍两种管理RC与DC的方法。 方法一:RC由WM_CREATE消息响应时创建,创建后立即释放DC;当WM_PAINT消息到来时,程序再获取DC句柄,并与RC关联起来,绘图完成后,立即解除RC与DC的关联,并释放DC;当WM_DESTROY消息到来时,程序只需简单地删除RC即可。如图4.1所示。 WM_CREATE Get the DC handle(GetDC) Create the DC Release the DC handle(Release DC) WM_PAINT Get the DC handle(GetDC) Make the RC current 消息循环 Draw with OpenGL Make the RC not current Release the DC handle(Release DC) WM_DESTROY Delete the RC 图4.1 RC与DC的管理方法一 方法二:RC在程序开始时创建并使之成为现行RC。它将保持为现行RC直至程序结束。相应地,GetDC在程序开始时调用,ReleaseDC在程序结束时才调用。此种方法的好处是在响应WM_PAINT消息时,无需调用十分耗时的wglMakeCurrent函数,一般它要消耗几千个时钟周期。如图4.2所示。如果应用程序需要使用动画或实时图形,建立采用第二种方法。 WM_CREATE Get the DC handle(GetDC) Create the RC and make it current WM_PAINT 消息循环 Draw with OpenGL WM_DESTROY Make the RC not current and delete Release the DC handle(ReleaseDC ) 图4.2 RC与DC的管理方法二 4.2.2调色板的使用 (1) Windows下的调色板 OpenGL可以使用16色、256色、64K和16M真彩色。真彩模式下不需要调色板,而在16色模式下根本不可能得到较为满意的效果,因此对OpenGL而言,调色板只有在256色模式下才有意义。 Windows把调色板分为系统调色板和逻辑调色板。每个应用程序都拥有一套自己的逻辑调色板(或使用缺省调色板),当该应用程序拥有键盘输入焦点时可以最多使用从16M种色彩中选取的256种颜色(20种系统保留颜色和236种自由选取的颜色),而失去焦点的应用程序可能会有某些颜色显示不正常。系统调色板由Windows内核来管理,它是由系统保留的20种颜色和经仲裁后各个应用程序设置的颜色组成,并与硬件的256个调色板相对应。应用程序的逻辑调色板与硬件的调色板没有直接的对应关系,而是按照最小误差的原则映射到系统调色板中,因此即使应用程序自由选取256种不同颜色构成自己的逻辑调色板,也有可能某些颜色显示到屏幕上时是一样的。 当应用程序的窗口接收到键盘输入焦点时,Windows会向它发送一条WM-QUERYNEWPALETTE消息,让它设置自己的逻辑调色板,此时Windows会在系统调色板中尽量多地加入该应用程序需要的颜色,并生成相应的映射关系。接着Windows会向系统中所有的覆盖型窗口和顶级窗口(包括拥有键盘输入焦点的窗口)发送一条WM-PALETTECHANGED消息,让它们设置逻辑调色板和重绘客户区,以便能更充分地利用系统调色板,已拥有键盘输入焦点的窗口不应再处理这条消息,以避免出现死循环。 (2) OpenGL的颜色表示与转换 OpenGL内部用浮点数来表示和处理颜色,红绿蓝和Alpha值这四种成份每种的最大值为1.0,最小值为0.0。在256色模式下,OpenGL把一个象素颜色的内部值按线性关系转换为8比特(Bit)来输出到屏幕上,其中红色占最低位的3比特,绿色占中间的3比特,蓝色占最高位的2比特,Windows将这个8比特值看作逻辑调色板的索引值。例如OpenGL的颜色值(1.0、0.14、0.6667)经过转换后二进制值为10001111(红色为111、绿色为001、蓝色为10),即第143号调色板,该调色板指定的颜色的RGB值应与(1.0、0.14、0.6667)有相同的比率,为(255、36、170),如果不是该值,那么显示出来的颜色就会有误差。 (3) 调色板的生成算法 很明显,OpenGL输出的8比特值中直接表明了颜色的组成,为了使图形显示正常,我们应以线性关系来设置逻辑调色板,使其索引值直接表明颜色的组成。因此生成调色板时,把索引值从低位到高位分成3-3,2共三个部分,将每一部分映射到0 — 255中去,这样3比特映射为,0、36、73、109、146、182、219、255,,2比特映射为,0、85、170、255,,最后把三部分组合起来成为一种颜色。 经过上面的处理后,256种颜色均匀分布在颜色空间中,并没有完全包含系统保留的20种颜色(只包含了7种),这意味着将会有数种颜色显示成一样,从而影响效果。一个较好的解决办法是按照最小均方误差的原则把13种系统颜色纳入到逻辑调色板中。 从原理上来说,并非一定要使用线性映射,还可以用其它一些映射关系,如加入Gamma校正以便更能符合人眼的视觉特性,不过这些映射关系应用得并不广泛,在此不再讨论。 4.2.3像素格式设置 像素格式是OpenGL窗口的重要属性,它包括是否使用双缓冲,颜色位数和类型以及深度位数等。像素格式可由Windows系统定义的所谓像素格式描述子结构来定义(PIXELFORMATDESCRIPTOR),该结构定义在windows.h中。在该结构中包含有26个属性信息,其形式为: typedef struct tagPIXELFORMATDESCRIPTOR { WORD nSize; WORD nVersion; DWORD dwFlags; BYTE iPixelType; BYTE cColorBits; BYTE cRedBits; BYTE cRedShift; BYTE cGreenBits; BYTE cGreenShift; BYTE cBlueBits; BYTE cBlueShift; BYTE cAlphaBits; BYTE cAlphaShift; BYTE cAccumBits; BYTE cAccumRedBits; BYTE cAccumGreenBits; BYTE cAccumBlueBits; BYTE cAccumAlphaBits; BYTE cDepthBits; BYTE cStencilBits; BYTE cAuxBuffers; BYTE iLayerType; BYTE bReserved; DWORD dwLayerMask; DWORD dwVisibleMask; DWORD dwDamageMask; } PIXELFORMATDESCRIPTOR; 各变量的含义如下: nSize:该结构所占内存空间。 nVersion:版本号,当前为1。 dwFlags:指定像素格式属性,可选参量如表4.1所示。 表4.1 像素格式属性 标识符 解释 PFD_DRAW_TO_BITMAP 支持内存中绘制位图 PFD_DRAW_TO_WINDOW 支持屏幕绘图 PFD_DOUBLEBUFFER 支持双缓冲 PFD_CENERIC_FORMAT 指定选择GDI支持的像素格式 PFD_NEED_PALETTE 指定需要逻辑调色板 PFD_NEED_SYSTEM_PALETTE 指定需要硬件调色板 PFD_STEREO NT不支持 PFD_SUPPORT_OPENGL 支持OpenGL PFD_SUPPORT_GDI 支持GDI,此时不可使用PFD_DOUBLEBUFFER iPixelType:像素颜色模式,可选项为PFD_TYPE_RGBA或PFD_TYPE_INDEX, 分别对应于RGBA模式和颜色索引模式。 cColorBits:指定颜色的位数。 cRedBits:采用RGBA模式时,红色组分占用位数。 cRedShift:采用RGBA模式时,红色组分偏移量。 cGreenBits:采用RGBA模式时,绿色组分占用位数。 cGreenShift:采用RGBA模式时,绿色组分偏移量。 cBlueBits:采用RGBA模式时,蓝色组分占用位数。 cBlueShift: 采用RGBA模式时,蓝色组分偏移量。 cAlphaBits:采用RGBA模式时,Alpha组分占用位数。 cAlphaShift:采用RGBA模式时,Alpha组分偏移量。 cAccumBits:指定累积缓冲区表示一个像素所用位数。 cAccumRedBits:指定累积缓冲区表示红色组分占用位数。 cAccumGreenBits:指定累积缓冲区表示绿色组分占用位数。 cAccumBlueBits:指定累积缓冲区表示蓝色组分占用位数。 cAccumAlphaBits:指定累积缓冲区表示Alpha组分占用位数。 cDepthBits:指定深度缓冲区表示一个像素所用位数。 cStencilBits:指定模板缓冲区表示一个像素所用位数。 cAuxBuffers:指定辅助缓冲区,Windows9x、NT不支持。 iLayerType:Windows9x、NT下只能是PFD_MAIN_PLANE。 bReserved:,0。 dwLayerMask:指定覆盖层的屏蔽,Windows9x、NT不支持。 dwVisibleMask:Windows9x、NT不支持。 dwDamageMask:Windows9x、NT不支持。 Windows提供了四个像素格式管理函数,分别介绍如下: (1) int ChoosePixelFormat(HDC hdc,PIXELFORMATDESCRIPTOR *ppdf) 该函数比较传过来的像素格式描述和OpenGL支持的像素格式,返回一个最佳匹配的像素格式索引。该索引值可传给SetPixelFormat为DC设置像素格式。返回值为0表示失败。在比较像素格式时,匹配优先级顺序为像素格式描述子结构中的下述各域: dwFlags,>cColorBits,>cAlphaBits,>cAccumBits ,>cDepthBits,>cStencilBits,>cAuxBuffers,>iLayerType 硬件支持的像素格式优先。 (2) int DescribePixelFormat(HDC hdc, int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR *ppfd) 该函数用格式索引iPixelFormat说明的像素格式来填写由ppfd所指向的像素格式描述子结构,利用该函数可以枚举像素格式。 (3) int GetPixelFormat(HDC hdc)该函数用于获取hdc的格式索引。 (4) BOOL SetPixelFormat(HDC hdc, int iPixelFormat, LPPIXELFORMATDESCRIPTOR *ppfd)该函数用格式索引iPixelFormat来设置hdc的像素格式。在使用该函数之前应该调用ChoosePixelFormat来获取像素格式索引。另外,OpenGL窗口风格必须包含WS_CLIPCHILDREN和WS_CLIPSIBLINGS类型,否则设置失败。 应该注意的是ChoosePixelFormat函数并不一定返回一个最佳的像素格式值,可以利用DescribePixelFormat来枚举系统所支持的所有像素格式。OpenGL的通常支持24种不同的像素格式,如果系统安装了OpenGL硬件加速器,它可能会支持其它的像素格式。 设置DC的像素格式的步骤如图4.3所示。 枚举或选择像素格式 期望的像素格式描述子 ChoosePixelFormat PIXELFORMATDESCRIPTOR DescribePixelFormat 匹配 YES NO 设置像素格式 图4.3设置像素格式的一般步骤 4.3 编写本程序的基本轮廓 VC++6.0中绘制基于MFC的单文档OpenGL三维场景的方法比较简单,其大体轮廓是: 在单文档窗口的创建过程中,设置好像素格式,并按OpenGL的要求设置好窗口的属性和风格。 首先获得Windows设备描述表,然后将其与事先设置好的OpenGL绘制描述符表联系起来。 调用OpenGL命令进行绘图。 退出OpenGL窗口时,释放OpenGL绘制描述表RC和Windows设备描述表DC。 4.4编程步骤 4.4.1搭建OpenGL单文档程序框架 (1)启动VisualC++6.0,选择New菜单,在New对话框中选择project标签,选择MFC AppWizard(exe),新建一个基于单文档的工程,名称为VisualSpace。 (2)利用MFC ClassWizard为CVisualSpaceView类添加消息WM_CREATE,WM_DESTROY,WM_SIZE,和WM_TIMER。 (3) 在CVisualSpaceView.h中加入如下所示的代码: //////////////////////////////////////////////////////////////////// ////添加成员函数和成员变量 BOOL SetupPixelFormat(); void SetLogicalPalette(); BOOL InitializeOpenGL(CDC* pDC); void RenderScene(); HGLRC m_hRC; //OpenGL渲染描述表 HPALETTE m_hPalette;//OpenGL调色板 CDC *m_pDC; //OpenGL设备描述表 //////////////////////////////////////////////////////////////////// (4) 在CVisualSpaceView.cpp中部分函数中加入如下所示的代码: BOOL CVisaulSpaceView::PreCreateWindow(CREATESTRUCT& cs) { // TODO: Modify the Window class or styles here by modifying // the CREATESTRUCT cs /////////////////////////////////////// ////设置窗口类型 cs.style|=WS_CLIPCHILDREN|WS_CLIPSIBLINGS; /////////////////////////////////////// return CView::PreCreateWindow(cs); } void CVisaulSpaceView::OnDraw(CDC* pDC) { CVisaulSpaceDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here /////////////////////////////////////// ////渲染场景 RenderScene(); /////////////////////////////////////// } ///////////////////////////////////////////////////////////////////// //////// // CVisaulSpaceView message handlers int CVisaulSpaceView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here /////////////////////////////////////// ////初始化OpenGL和设置定时器 m_pDC=new CClientDC(this); SetTimer(1,20,NULL); InitializeOpenGL(m_pDC); /////////////////////////////////////// return 0; } void CVisaulSpaceView::OnDestroy() { CView::OnDestroy(); // TODO: Add your message handler code here /////////////////////////////////////// ////删除调色板,渲染上下文和定时器 wglMakeCurrent(0,0); wglDeleteContext(m_hRC); if(m_hPalette) DeleteObject(m_hPalette); if(m_pDC) DeleteObject(m_pDC); KillTimer(0); /////////////////////////////////////// } void CVisaulSpaceView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); // TODO: Add your message handler code here /////////////////////////////////////// ////添加窗口缩放时图形变换函数 glViewport(0,0,cx,cy); /////////////////////////////////////// } void CVisaulSpaceView::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default /////////////////////////////////////// ////添加图形更新函数 Invalidate(FALSE); /////////////////////////////////////// CView::OnTimer(nIDEvent); } /////////////////////////////////////// ////设置像素格式 BOOL CVisaulSpaceView::SetupPixelFormat() { 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 pixelformat; pixelformat=ChoosePixelFormat(m_pDC->GetSafeHdc(),&pfd);//选择像 素格式 SetPixelFormat(m_pDC->GetSafeHdc(),pixelformat,&pfd);//设置像素格 式 if(pfd.dwFlags&PFD_NEED_PALETTE) SetLogicalPalette();//设置逻辑调色版 return TRUE; } void CVisaulSpaceView::SetLogicalPalette() { struct { WORD Version; WORD NumberOfEntries; PALETTEENTRY aEntries[256]; } logicalPalette = { 0x300, 256 }; BYTE reds[] = {0, 36, 72, 109, 145, 182, 218, 255}; BYTE greens[] = {0, 36, 72, 109, 145, 182, 218, 255}; BYTE blues[] = {0, 85, 170, 255}; for (int colorNum=0; colorNum<256; ++colorNum) { logicalPalette.aEntries[colorNum].peRed = reds[colorNum & 0x07]; logicalPalette.aEntries[colorNum].peGreen = greens[(colorNum >> 0x03) & 0x07]; logicalPalette.aEntries[colorNum].peBlue = blues[(colorNum >> 0x06) & 0x03]; logicalPalette.aEntries[colorNum].peFlags = 0; } m_hPalette = CreatePalette ((LOGPALETTE*)&logicalPalette); } BOOL CVisaulSpaceView::InitializeOpenGL(CDC* pDC) { m_pDC=pDC; SetupPixelFormat(); //生成绘制描述表 m_hRC = wglCreateContext(m_pDC->GetSafeHdc()); //置当前绘制描述表 wglMakeCurrent(m_pDC->GetSafeHdc(), m_hRC); return TRUE; } void CVisaulSpaceView::RenderScene() { glFlush(); ::SwapBuffers(m_pDC->GetSafeHdc()); } (5) 在Stdafx.h中加入如下代码: ////////////////////////////////////////////////////////// //包含opengl的头文件 #include #include (6) 编译运行。图7是运行效果图。这是搭建好的OpenGL单文档程序框架。 一般的三维场景建模就可以在此框架上编写完善更多功能的代码了。 图4.4运行效果图 4.4.2场景绘制 (1) 首先在CVisualSpaceView.h中定义几个本程序要用到的常量: int const MAP_W=32; //地图的大小 int const MAP_SCALE=16; //地图每一个格子的大小 int const MAP=MAP_W*MAP_SCALE/2; float const PI=3.14159; //定义常数π 在RenderScene()中添加如下代码: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕及深度缓存 glLoadIdentity();//重置矩阵 glFlush();//更新窗口 ::SwapBuffers(m_pDC->GetSafeHdc());//交换缓冲区 在OnSize中添加以下代码: /////////////////////////////////////// glMatrixMode(GL_PROJECTION); // 选择投影矩阵 glLoadIdentity(); // 重置投影矩阵 // 设置视口的大小 gluPerspective(45.0f,(GLfloat)cx/(GLfloat)cy,0.1f,3000.0f); glMatrixMode(GL_MODELVIEW); // 选择模型观察矩阵 glLoadIdentity(); (2) 地形绘制 地形绘制原理是使用顶点数组函数进行绘制。以下是使用顶点数组函数所需要的数组: float m_terrain[MAP_W*MAP_W][3];//保存顶点坐标的数组 int m_index[MAP_W*MAP_W*2];//画地图时的顶点编号数组 float m_texcoord[MAP_W*MAP_W][2];//构成曲面的片元的纹理坐标 在CVisualSpaceView的构造函数中初始化各个数组: int Index=0; int Vertex; for(int z=0;zsizeX,//图像宽 pImage->sizeY,//图像高 0,//纹理边界必须为0或者1 GL_RGB,//像素数据的格式 GL_UNSIGNED_BYTE,//数据类型 pImage->data);//数据来源,最后整个函数指定一个二维的纹理贴图 free(pImage->data);//释放图像纹理数据 free(pImage);//释放图像结构 } return TRUE; } 纹理类新建后,为了进行贴图就要在程序中使用它了。在CVisualSpaceView.h 定义几个与纹理贴图有关的变量和函数: GLuint m_textures[6];//定义六个纹理 Texture *m_texture;//声明一个纹理对象指针 void SetTexture(UINT textur);//设置贴图滤波 在CVisualSpaceView类的构造函数中加入如下代码: m_texture=new Texture();//产生一个新的纹理对象指针 在InitializeOpenGL函数中加入加载纹理贴图的代码: m_texture->LoadTexture("Ground.BMP",m_textures[0]);//读入底面的贴图 m_texture->LoadTexture("cliffup.bmp",m_textures[1]);//读入天空的贴图 m_texture->LoadTexture("clifflf.bmp",m_textures[2]); m_texture->LoadTexture("cliffrt.bmp",m_textures[3]); m_texture->LoadTexture("cliffbk.bmp",m_textures[4]); m_texture->LoadTexture("cliffft.bmp",m_textures[5]); glEnable(GL_TEXTURE_2D);//启用2维纹理 完善设置纹理滤波的函数SetTexture(): void CVisaulSpaceView::SetTexture(UINT textur)//设置贴图滤波 { glBindTexture (GL_TEXTURE_2D, textur);// 设置贴图 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//放大采用线性滤波 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); } (5) 漫游的实现 在OpenGL中观察虚拟世界的主要函数gluLookAt(„),它的主要作用是可 以改变我们在OpenGL场景的观察点,这个观察点就好像是我们的眼睛,也好像 是我们手中的摄像机。我们在一个场景中行走时,看到前面的物体越来越近,两 边的物体向后退,这是我们观察点在场景中的位置改变的结果。 gluLookAt(视点,目标点,视点方向)。其中视点(或者说是观察点)是一个三 维坐标量(x,y,z)。x的变化是我们在场景中左右移动,y的变化是我们上下移动, z的变化是我们前后移动。目标点和视点方向也分别是三维坐标。目标点实际上 是我们不动时手中的相机上下左右移动的结果。视点方向是表示我们是竖直还是 倒立以及倾斜等状态。 有了上述基本原理,我们就可以建立漫游了。首先我们需要定义一些漫游相 关的数据和函数,在CVisualSpaceView.h中定义几个本程序要用到的数据和函 数: //////////////////////////////////////////////////////////////////// float m_eye[3];//视点 float m_look[3];//目标点 float rad_xz;//角度弧度值 float m_angle;//方位角 float m_elev;//俯仰角 float speed;//步长 float GetHeight(float x,float z);//获取地面高度 BOOL Camera();//视点 //////////////////////////////////////////////////////////////////// 在CVisualSpaceView构造函数中初始化: //////////////////////////////////////////////////////////////////// m_eye[0]=MAP;//视点的x分量初值 m_eye[2]=MAP;//视点的y分量初值 m_angle=0;//方位角初值 m_elev=0;//俯仰角初值 speed=0.5;//步长初值 //////////////////////////////////////////////////////////////////// 获取底摄像机距离地面的高度,也即是视点的y坐标的值。实现原理:在获得目标点的x,z坐标以后先计算目标点在地形的哪一块,获得该块的四角的坐标值中的y的大小以及目标点在块内的偏移量,然后利用双向性插值计算出目标点的高度y。 float CVisaulSpaceView::GetHeight(float x,float z) { float CameraX=x/MAP_SCALE;//计算给定的坐标值落在哪一块 float CameraZ=z/MAP_SCALE;//计算给定的坐标值落在哪一块 int Col0=int(CameraX);//块的列号 int Row0=int(CameraZ);//块的行号 int Col1=Col0+1;//相邻列 int Row1=Row0+1;//相邻行 if(Col1>MAP_W)Col1=0;//相邻列大于地块数,取首列 if(Row1>MAP_W)Row1=0;//相邻行大于地块数,取首行 float h00=m_terrain[Col0+Row0*MAP_W][1];//获取块的四角的高度 float h01=m_terrain[Col1+Row0*MAP_W][1]; float h11=m_terrain[Col1+Row1*MAP_W][1]; float h10=m_terrain[Col0+Row1*MAP_W][1]; float tx=CameraX-int(CameraX);//计算块内X的偏移位置 float tz=CameraZ-int(CameraZ);//计算块内z的偏移位置 float txtz=tx*tz; return h00*(1.0f-tz-tx+txtz)+h01*(tx-txtz)+h11*txtz+h10*(tz-txtz); //返回插值计算值,为所求点的高度 } 漫游由鼠标和键盘控制,故还需要添加如下的消息函数: void CVisaulSpaceView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { // TODO: Add your message handler code here and/or call default switch(nChar) { case VK_SHIFT: speed*=2;//按SHIFT时的加速 break; case VK_LEFT: m_angle-=speed*2;//左转,方位角- rad_xz = PI* m_angle/180.0f;//计算左右旋转角度的弧度值 break; case VK_RIGHT: m_angle+=speed*2;//右转,方位角+ rad_xz = PI* m_angle/180.0f;//计算左右旋转角度的弧度值 break; case VK_PRIOR: m_elev +=speed; //Page UP 键 if (m_elev<-360)m_elev =-360; //仰俯角 if (m_elev> 360)m_elev = 360; //仰俯角 break; case VK_NEXT: m_elev -=speed; //Page Down键 if (m_elev<-360)m_elev =-360; //仰俯角 if (m_elev> 360)m_elev = 360; //仰俯角 break; case VK_UP://前进 m_eye[2]+=(float)sin(rad_xz)*speed; //视点的x分量 m_eye[0]+=(float)cos(rad_xz)*speed; //视点的Z分量 break; case VK_DOWN://后退 m_eye[2]-=(float)sin(rad_xz)*speed; //视点的x分量 m_eye[0]-=(float)cos(rad_xz)*speed; //视点的Z分量 break; } CView::OnKeyDown(nChar, nRepCnt, nFlags); } void CVisaulSpaceView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default Lastx=point.x; Lasty=point.y; CView::OnLButtonDown(nFlags, point); } void CVisaulSpaceView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CRect rect; GetClientRect(&rect); float l=rect.right-rect.left; // Newx=point.x; Newy=point.y; float dist=sqrt((Newx-Lastx)*(Newx-Lastx)+((Newx-Lastx))*(Newy-Lasty)); if((Newx-Lastx)<0||(Newx-Lastx)<0) dist=-dist; dist=dist/l; m_angle+=dist*45.0; rad_xz = PI* m_angle/180.0f;//计算左右旋转角度的弧度值 CView::OnLButtonUp(nFlags, point); } 关于鼠标滑动转动视角原理:首先鼠标左键按下时,记下鼠标在屏幕的坐标,Lastx=point.x;Lasty=point.y;鼠标左键抬起时,记下此时屏幕的坐标,然后用两点的距离除以客服区屏幕的宽度,然后乘以45,以获取要旋转的角度。 然后在RenderScene()中绘制天空和地形代码的前面添加如下设置摄像机的代码: Camera(); //摆放摄像机 (6)场景显示模式 示场景,通俗的将就是进行贴图还是不贴图。涉及到这主要是以什么方式显 的函数为:void glPolygonMode(GLenum face,GLenum mode),此函数是用来选择多边形的光栅化模式,它带有两个参数,其中参数face是指定模式要应用的多边形,可以取值为GL_FRONT, GL_BACK 和GL_FRONT_AND_BACK。参数mode表示多边形要被光栅化的方式,可以取值为GL_LINE,GL_FILL和GL_POINT。 为程序添加子菜单,子菜单名为场景,在子菜单中添加菜单项框架,ID为IDR_BUTTON_FRAME,填充ID为IDR_BUTTON_FILLED,分别添加消息OnButtonFrame()和OnButtonFilled(),其实现代码如下: void CVisaulSpaceView::OnButtonFrame() { // TODO: Add your command handler code here glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } void CVisaulSpaceView::OnButtonFilled() { // TODO: Add your command handler code here glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } 在为程序添加两个按钮,ID和上述菜单项一样分别为:框架按钮的ID 为IDR_BUTTON_FRAME,填充按钮的ID为IDR_BUTTON_FILLED。这样这两个按钮对应的消息响应函数与上述菜单项的消息响应函数一样。 (7) 自动漫游和播放声音 在场景子菜单下添加两个菜单项自动漫游和播放声音,其ID分别为:IDR_BUTTON_AUTOPLAY和IDR_BUTTON_SOUND,实现自动漫游和声音播放。为实现自动漫游还需要为CVisaulSpaceView类添加一个标记变量m_AutoPlay,作为是否进行自动漫游的标记,一个声音播放控制变量m_PlaySound用来控制声音的播放。在CVisaulSpaceView构造函数中初始化为: m_AutoPlay=FALSE; m_PlaySound=FALSE; 这两个菜单项的消息响应函数分别为: void CVisaulSpaceView::OnButtonAutoplay() { // TODO: Add your command handler code here m_AutoPlay=~m_AutoPlay;// m_AutoPlay来控制自动漫游 } void CVisaulSpaceView::OnButtonSound() { // TODO: Add your command handler code here if(!m_PlaySound)// m_PlaySound来控制声音播放 { PlaySound("atmospheres.wav",0, SND_ASYNC|SND_NODEFAULT|SND_LOOP ); m_PlaySound=~m_PlaySound; } else { PlaySound(0,0,0); m_PlaySound=~m_PlaySound; } } 在OnTimer中加入如下代码,以实现自动漫游: if(m_AutoPlay) { m_eye[2]+=(float)sin(rad_xz)*speed; //视点的x分量 m_eye[0]+=(float)cos(rad_xz)*speed; //视点的Z分量 } (8) 文字显示 本程序是利用VC的TextOut简单的进行文字输出。为视图类添加一个成员函数: void CVisaulSpaceView::TextLine(CDC *pDC) { long x,y; RECT rect; CString str; CFont *pNewFont,*pOldFont;//新旧字体对象指针 CSize cz; str.Format("方位角=%03d X=%3.1f Y=%3.1f 高=%2.1f 俯仰角=%02.1f 漫游速率=%2.1f", int(m_angle)%360,m_eye[0],m_eye[2],m_eye[1],m_elev,speed);// 格式化输出 cz=pDC->GetTextExtent(str);//获取字符串的大小 GetClientRect(&rect);//获取矩形客户区 x=rect.right;//将矩形客户区的长赋给x y=rect.bottom;//将矩形客户区的宽赋给y pNewFont = new CFont; //声明一个新的字体对象 pNewFont->CreateFont(12, // 字体高度 6, // 字体宽度 0, // nEscapement 0, // nOrientation FW_THIN, // nWeight FALSE, // bItalic FALSE, // bUnderline 0, // cStrikeOut ANSI_CHARSET, // nCharSet OUT_DEFAULT_PRECIS, // nOutPrecision CLIP_DEFAULT_PRECIS, // nClipPrecision DEFAULT_QUALITY, // nQuality DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily _T("宋体")); // lpszFac pOldFont = pDC->SelectObject(pNewFont); //返回旧的对象并保存 pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(RGB(0,255,0)); pDC->TextOut(x-cz.cx+50,y-cz.cy,str);//输出字符串 pDC->SelectObject(pOldFont); //还原旧的对象 } 在OnDraw()函数中加入代码: TextLine(pDC);//输出文字 (9)操作说明 在帮助子菜单下添加一个名为操作说明的菜单项,其ID为:IDR_HELP同样在工具栏上添加一个按钮,其ID也为:IDR_HELP。添加一个对话框资源,其ID为:IDD_HELP,作为显示操作说明的对话框。其上面的布局如图4.6所示: 图4.6操作说明图 为程序添加一个名字为Dlghelp的对话框类,为操作说明菜单项添加消息响应函数: void CVisaulSpaceApp::OnHelp() { CDlghelp dlg; dlg.DoModal(); } 4.4.3程序的最终运行结果 程序的最终运行结果如图4.7所示: 图4.7程序运行结果 5 总结与展望 5.1 总结 本篇论文主要围绕三维游戏的开发,介绍了在VC++环境下利用OpenGL库制作三维动画物件的技术,并实现了游戏基本的设置。 OpenGL是开放的三维图形软件包,独立于窗口系统和操纵系统,在交互式三维图形建模能力和编程方面具有无可比拟的优越性。OpenGL灵活方便地实现了二维和三维的高级图形技术,在性能上表现得异常优越。它具有建模、变换、光照处理、色彩处理、动画及更先进的能力,如:纹理映射、物体运动模糊效果和雾化效果等。OpenGL为实现逼真的三维绘制效果和建立交互的三维场景提供了高效率的函数库,以OpenGL为基础开发的应用程序可以十分方便地在各种平台间移植。OpenGL可以与VC++紧密接口,便于实现有关计算和图形算法,可保证算法的正确性和可靠性。OpenGL使用简便、效率较高。 值得一提的是,由于Microsoft公司在win95以后推出的windows操作系统中提供OpenGL图形标准,尤其是OpenGL三维图形加速卡和微机图形工作站的推出,人们可以更方便地使用OpenGL建立自己的三维图形世界。 本文通过对基于OpenGL的三维游戏的开发,对虚拟环境建模方法进行初步探索。相信本技术如能够更进一步,将极大促进虚拟现实和网络三维游戏的发展。 在整个游戏的设计过程当中,我们遇到了不少的困难,并查阅了大量的资料,随着知识的一步一步完善,我们终于完成了这次的毕业设计。在以后的学习工作中,我们将继续发扬这种刻苦钻研的精神,把学问进行到底~ 致 谢 本学位论文是在导师 老师的悉心指导下完成的, 老师严谨的治学态度和敬业精神让我钦佩不已。在毕业设计期间, 老师不仅经常抽出他宝贵的时间精心的指导我如何进行研究和如何完成论文,而且在生活上也给了我无微不至的关怀和鼓励。在论文完成之际,谨向 老师表示衷心的感谢并致以崇高的敬意~ 也要感谢我的家人给我的鼓励~感谢我的同学给我的帮助和支持~感谢所有关心、鼓励、支持和帮助过我的人~ 最后在此也对在百忙之中抽出时间来评审本论文和参加答辩的各位教授和老师表示衷心的感谢~ 作者: 2008年5月 参考文献 [1] OpenGL 编程指南 / [D.施赖纳]Dave Shreiner...[等]著; 邓郑祥译. - 北京: 人民邮电出版社, 2005 [2] OpenGL高级编程与可视化系统开发?高级编程篇 / 和平鸽工作室编著. - 北京: 中国水利水电出版社, 2003.1 [3] OpenGL高级编程与可视化系统开发?系统编程篇 / 和平鸽工作室编著. - 北京: 中国水利水电出版社, 2003.1 [4] Visual C++ 6.0高级编程技术?OpenGL篇 / 费广正,乔林编著. - 北京: 中国铁道出版社, 2000.9 [5] 三维游戏设计师宝典—学OpenGL编3D游戏 / 毛伟东,唐明理编著.四川电子音像出版中心,2005.6 [6] OpenGL图形程序开发务实 / 薛惠锋,吴慧欣,解丹蕊编著.西北工业大学出版社,2005.8 [7] 计算机图形学[专著] / 孙家广,许隆文编著. - 北京: 清华大学出版社, 1986.12 [8] Computer graphics with openGL / (美) Donald Hearn, M. Pauline Baker著; 蔡士杰, 宋继强, 蔡敏译. - 北京: 电子工业出版社, 2005 - (国外计算机科学教材系列) [9] 计算机图形学,(美) [D.赫恩]Donald Hearn, [M.贝克]M. Pauline Baker著;蔡士杰, 宋继强, 蔡敏译,2005,TP391.41-43/H329 [10] OpenGL VC/VB图形编程 / 江早 主编. - 北京: 科学出版社, 2001.2 [11] 基于OpenGL的高等数学多媒体课件实现 程飞 洛阳师范学院学报 2005/0 [12] 基于OpenGL的三维仿真系统研究与应用 王碧波 计算机工程与科学 2005/12 学位论文原创性声明 本人郑重声明:所呈交的学位论文~是本人在导师的指导下进行的研究工作所取得的成果。尽我所知~除文中已经特别注明引用的内容和致谢的地方外~本论文不包含任何其他个人或集体已经发表或撰写过的研究成果。对本文的研究做出重要贡献的个人和集体~均已在文中以明确方式注明并表示感谢。本人完全意识到本声明的法律结果由本人承担。 学位论文作者,本人签名,: 年 月 日 学位论文出版授权书 本人及导师完全同意《中国博士学位论文全文数据库出版》、《中国优秀硕士学位论文全文数据库出版章程》(以下简称“章程”)~愿意将本人的学位论文提交“中国学术期刊,光盘版,电子杂志社”在《中国博士学位论文全文数据库》、《中国优秀硕士学位论文全文数据库》中全文发表和以电子、网络形式公开出版~并同意编入CNKI《中国知识资源总库》~在《中国博硕士学位论文评价数据库》中使用和在互联网上传播~同意按“章程”规定享受相关权益。 论文密级: ?公开 ?保密(___年__月至__年__月)(保密的学位论文在解密后应遵守此协议) 作者签名:_______ 导师签名:_______ _______年_____月_____日 _______年_____月_____日 独 创 声 明 本人郑重声明:所呈交的毕业设计(论文),是本人在指导老师的指导下,独立进行研究工作所取得的成果,成果不存在知识产权争议。尽我所知,除文中已经注明引用的内容外,本设计(论文)不含任何其他个人或集体已经发表或撰写过的作品成果。对本文的研究做出重要贡献的个人和集体均已在文中以明确方式标明。 本声明的法律后果由本人承担。 作者签名: 二〇一〇年九月二十日 毕业设计(论文)使用授权声明 本人完全了解滨州学院关于收集、保存、使用毕业设计(论文)的规定。 本人愿意按照学校要求提交学位论文的印刷本和电子版,同意学校保存学位论文的印刷本和电子版,或采用影印、数字化或其它复制手段保存设计(论文);同意学校在不以营利为目的的前提下,建立目录检索与阅览服务系统,公布设计(论文)的部分或全部内容,允许他人依法合理使用。 (保密论文在解密后遵守此规定) 作者签名: 二〇一〇年九月二十日 致 谢 时间飞逝~大学的学习生活很快就要过去~在这四年的学习生活中~收获了很多~而这些成绩的取得是和一直关心帮助我的人分不开的。 首先非常感谢学校开设这个课题~为本人日后从事计算机方面的工作提供了经验~奠定了基础。本次毕业设计大概持续了半年~现在终于到结尾了。本次毕业设计是对我大学四年学习下来最好的检验。经过这次毕业设计~我的能力有了很大的提高~比如操作能力、分析问题的能力、合作精神、严谨的工作作风等方方面面都有很大的进步。这期间凝聚了很多人的心血~在此我表示由衷的感谢。没有他们的帮助~我将无法顺利完成这次设计。 首先~我要特别感谢我的知道郭谦功老师对我的悉心指导~在我的论文书写及设计过程中给了我大量的帮助和指导~为我理清了设计思路和操作方法~并对我所做的课题提出了有效的改进方案。郭谦功老师渊博的知识、严谨的作风和诲人不倦的态度给我留下了深刻的印象。从他身上~我学到了许多能受益终生的东西。再次对周巍老师表示衷心的感谢。 其次~我要感谢大学四年中所有的任课老师和辅导员在学习期间对我的严格要求~感谢他们对我学习上和生活上的帮助~使我了解了许多专业知识和为人的道理~能够在今后的生活道路上有继续奋斗的力量。 另外~我还要感谢大学四年和我一起走过的同学朋友对我的关心与支持~与他们一起学习、生活~让我在大学期间生活的很充实~给我留下了很多难忘的回忆。 最后~我要感谢我的父母对我的关系和理解~如果没有他们在我的学习生涯中的无私奉献和默默支持~我将无法顺利完成今天的学业。 四年的大学生活就快走入尾声~我们的校园生活就要划上句号~心中是无尽的难舍与眷恋。从这里走出~对我的人生来说~将是踏上一个新的征程~要把所学的知识应用到实际工作中去。 回首四年~取得了些许成绩~生活中有快乐也有艰辛。感谢老师四年来对我孜孜不倦的教诲~对我成长的关心和爱护。 学友情深~情同兄妹。四年的风风雨雨~我们一同走过~充满着关爱~给我留下了值得珍藏的最美好的记忆。 在我的十几年求学历程里~离不开父母的鼓励和支持~是他们辛勤的劳作~无私的付出~为我创造良好的学习条件~我才能顺利完成完成学业~感激他们一直以来对我的抚养与培育。 最后~我要特别感谢我的导师赵达睿老师、和研究生助教熊伟丽老师。是他们在我毕业的最后关头给了我们巨大的帮助与鼓励~给了我很多解决问题的思路~在此表示衷心的感激。老师们认真负责的工作态度~严谨的治学精神和深厚的理论水平都使我收益匪浅。他无论在理论上还是在实践中~都给与我很大的帮助~使我得到不少的提高这对于我以后的工作和学习都有一种巨大的帮助~感谢他耐心的辅导。在论文的撰写过程中老师们给予我很大的帮助~帮助解决了不少的难点~使得论文能够及时完成~这里一并表示真诚的感谢。 内部资料 仅供参考 内部资料 仅供参考 内部资料 仅供参考
/
本文档为【简单的三维场景制作毕业论文】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索