翻一下老黄历,大学毕设写的基于Ogre的Editor
[翻一下老黄历,大学毕设写的基于Ogre的Editor]
工作,说长也不长,说短也不短,
以前终日的所想,已经变成现如今的日复一日,
当理想与现实碰撞,痛苦是在所难免的,
锐气,梦想已被消磨了多少?
自觉还算有份勉强及格的答卷。
Renderware,GB,U3D,CE3,UE3也都一一接触下来了,
遇到了各种各样的问题,
还有,各种各样的人和事,
作为一个开发人员,码农,
常常自问,但除技术之外,我还真正成长了多少?
我变世俗了么?我还浮躁么?我还相信真爱么?
何时才能达到从心所欲,风轻云淡?
似乎很难,当下,
似乎只有让自己忙得来不及思考才能安抚害怕改变的心,
人生际遇,遇见选择,百感交集,
真的很害怕自己浮躁,害怕抓不住真爱,
不禁念起大四时初生牛犊不怕虎,
独立完成特殊原因只能记在脑子里的项目Framework,
在图书馆前的湖畔吹了一夜冷风想出的超大地形解决方案…
有时候,我们真的不需要想太多,
路慢慢走,别怕是否能够到达,
听从自己内心的声音就可以了,
附上跌跌撞撞完成的毕业论文,
纪念那段无知者无畏的时光,
纪念Ogre,UE开发者们十年一剑的精神,
长风破浪会有时,直挂云帆济沧海!
NANCHANG UNIVERSITY
学 士 学 位 论 文
THESIS OF BACHELOR
(2007—2011年)
题 目 基于Ogre引擎的三维场景渲染
学 院: 信息工程学院 系 计算机
基于Ogre引擎的三维场景渲染
摘要
三维虚拟现实技术,随着计算机软件,硬件的不断进步发展,已经从早期的纸上理论中解放走出,开始发展于各行各业之中,广泛地应用于科研教学,军事模拟,医学,大型影视特效,及娱乐游戏之中,极大地方便了各种前瞻应用的拓展,并开始创出造巨大的经济效益。因此,三维虚拟现实技术开始越来越受到开发者们的关注,目前,通过上层高度抽象封装的引擎系统,调用底层渲染驱动API,组织资源,并对I/O,UI进行完善的响应,基于GPU运算产生实时场景的三维实时场景模拟程序,正成为其中一个热门的开发方向。
本文主要描述了基本的三维虚拟现实技术及相应的API、引擎,并依据相应理论实现了一个完整的三维场景实时模拟程序。使用了微软公司提供的DirextX底层3DAPI系统,及研究国际上知名的开源图形引擎“OGRE”,通过对一系列三维资源,二维资源的组织,完成了完善较,具有可实时编辑功能的的三维实时场景模拟程序。
关键词:三维虚拟现实技术;DirextX;图形引擎;OGRE;场景实时模拟;
3D-Scene Rendering based on OGRE Engine
Abstract
The 3D emulation technique has been keep a high evoluton whith developing of the software and hardware,and take advantage in a lot of industry,usedd for education,military affirs,medical science,film spcial effects, entertainment, games and etc. The 3D emulation technique'evoluton expands a new area,and create a great econmic benefit.So,it attact more and more developer's attention.Nowadays,real-time 3d scene emulation progamming by high-level abstracted and encapslutioned engine system,call on driver's API.control the resources, I/O and UI input, based on GPU's calcultion,has become a hot area.
The thesis introduces the basic theory of 3D emulation technique,and the relative API,engine,and build a complete real-time 3d emulation programe.It uses Microsoft'DirectX API ,and well known open-source graphic engine "Ogre(Object Orient Graphic Engine)",with a numer of 2D,and 3D resources,it comes with a complete real-time 3d emulation programe with edible function.
Keywords:3DEmulatiionTechnique;DirectX;GraphicEngine;RealTimeScneEmulation;OGRE;
目 录
第一章 绪论
1.1 课题研究背景
随着计算机硬件技术的不断进步发展,渲染技术的发展完善,三维虚拟现实技术不断快速,已经从早期的纸上理论中彻底解放走出,开始广泛地应用于到各行各业之中,并开始创出造巨大的经济效益。因此,三维虚拟现实技术也开始越来越受到开发者们的关注[1]。目前,通过上层高度抽象封装调用底层驱动API,组织资源,并对I/O,UI进行完善的响应,并基于GPU运算产生实时场景的3D程序,正成为其中一个热门的开发方向[2]。
其中,3D游戏开发正是这一方面应用的典型代表。随着计算机和网络的不断普及,从不断节节攀升的市场规模[15]可以看出网络游戏正越来越成为人们的一种日常休闲娱乐方式。
图1.1 中国网游市场规模财报
《中国网络游戏市场趋势预测2009-2012》[13]数据显示,从2004年到2012年网络游戏市场规模的年均复合增长率为45.71%,到2012年网络游戏用户规模将达到2.272亿,中国网络游戏市场规模将达到731.3亿元。
图1.2 中国网游市场规模预测
可以看出在未来较长年内,中国网络游戏市场仍将保持较强的增长态势,最主要原因在于网络游戏在中国网民中渗透率的提升该增长受网络环境优化、网游技术发展、网游产品内容的丰富等因素影响。
但同时,随着复杂度的不断增加,游戏己不再简单的读入输出少量资源,常常需要庞大的资源来表现游戏,例如光影效果、音乐视屏等等,都已成为游戏所不可或缺的重要部分,游戏品质的游戏与否,这些直观的感观表现手法的效果好坏直接决定着此游戏是否吸引玩家。现在的三维网络游戏开发已越来越为复杂系统工程,从建模、动画到光影、粒子特效,从物理系统、碰撞检测到文件管理、网络特性,及专业的编辑工具,都是开发过程中的重要环节。
但是,对比欧美日韩等发达国家的游戏娱乐产业,如韩国甚至以游戏产出作为一项国家事业来做,我国仍出于相对落后的位置,起步晚,技术力量上的薄弱,国内的游戏厂商正面临着自主研发上的窘境,一方面市场热火朝天,但真正属于国人的游戏却少之又少,国内玩家普遍倾向于日韩欧美的游戏。有关国家部门也对此相当地关注,通过政策,资金,教育甚至863计划来大力扶植国内三维渲染技术力量的发展。
1.2课题的研究内容
正是基于当前这样的大环境,经过一系列的调研,探讨,决定研究使用国际上知名且有众多成功开发前例的开源图形引擎“OGRE(Object-oriented Graphics Rendering Engine,面向对象的图形渲染引擎)”, 作为开发环境,对游戏最为核心的内容,同时也是玩家体验的主要环境——游戏运行的基本层“三维场景模拟”进行课题研究。
图1.3 基于Ogre开发的三维程序
在国内,《天龙八部》就是一款为数不多自主研发的获得很大成功三维网络游戏。它基于Ogre引擎开发,采用全3D人物和场景制作。由北京搜狐畅游公司投入大量人力物力,历时三年精心自主研发的武侠网游大作,故事背景取材自武侠泰斗金庸先生的同名小说,2007年正式开启公测,其优秀的游戏体验,优良的画面品质,吸引了大量玩家,截止2009年3月同时在线人数已突破80万。
图1.4 天龙八部游戏截图
通过NasdaQ财报数据,可以看出搜狐畅游公司凭借《天龙八部》获得了巨大的收益,在2010年美国全球科技股利润率最高的十家公司排在第6名:搜狐畅游 利润率52.12%。搜狐畅游公司其仅在2011第一季度在线游戏总收入为9,490万美元,国内的开发者对此趋之若鹜,也纷纷加入效仿。
本课题研究从基本的三维虚拟现实技术及Ogre引擎开始,借鉴《天龙八部》大量的完善美术资源,通过对一系列三维资源,二维资源的组织、操作及一系列的完善的架构与界面接口,最终旨在完成一个高效,高画质,且具有可实时编辑功能的三维实时场景模拟程序。
1.3 论文的组织结构
本文的内容按如下方式组织:
第一章,概要介绍了三维虚拟现实技术的应用、市场及前景,并描述了本文的主要核心技术,实现目标以及论文的组织结构。
第二章,对的开发平台Ogre的概述,及对主要技术进行了探讨。
第三章,对相应的需求分析做了详细的分析,并做了整体的系统架构设计。
第四章,详细的实现过程,技术细节。
第五章,对本文的研究工作进行了深入总结,并作了前景展望。
第二章 Ogre引擎综述
2.1 开发平台
开发环境:
首先选择了Microsoft Visual Studio 2008作为开发环境。
图2.1 VisualStudio开发环境
图形SDK配置:
Ogre属于上层的抽象引擎,具体的实现还需要底层的支持,本文程序的底层渲染基于DirectXSDK,具体配置如下:
安装DirectX SDK(June 2008)和Microsoft Visual Studio 2005,设置DirectX和Visual Studio 2008关联:
图2.2 DirectX在VS中配置
直接在项目属性中加入,或者直接使用代码加入库文件引用。
Ogre 配置[3]:
1. 首先下载ogre 1.7.2的:http://www.ogre3d.org/download/sdk
SDK中下载的为稳定版,开发者维护的版本需要使用项目管理工具TortoiseHg,通过SourceForge上的地址http://bitbucket.org/sinbad/ogre/(注:Ogre组织不再使用SVN),下载完整的源代码;
图2.3 HgClone界面
2. 下载ogre 1.7.2的依赖库的:
https://sourceforge.net/projects/ogre/files/ogre-dependencies-vc%2B%2B/1.7/OgreDependencies_MSVC_20101231.zip/download
2. 解压 OgreSDK至较高层级目录,方便使用;
图2.4 解压OgreSDK
3. 将第二步下载到的依赖库解压到OgreSDK的根目录中;
4. 依赖库找到VS08工程文件
6. 编译出相应debug和release版本Lib,DLL,作为Ogre的第三方插件使用
图2.5 编译后完整的SDK目录结构及对应的第三方库
7. CMake 是个跨平台的自动化建构系统,使用了组态档控制建构过程(build process)的高效方式,跨平台、工程庞大的Ogre就是使用CMake作为构建方式的,下载最新版本的cmake:http://www.cmake.org/
8. 安装,并运行cmake-gui
图2.6 CMake
9. 在"Where is the source code"和"Where to build the binaries"中分别设置为第三步中设置的OgreSDK目录,为节约空间,这里可以设置为同一个目录;
图2.7 CMake界面
10. 点击configure,选择VS08,可根据实际需要选择生成项目,这里暂时采用默认;
12. 进入OgreSDK的根目录,打开项目OGRE.sln
13. 准备好足够的时间,编译出Ogre的debug和release
13. 在编译完成后,将OgreSDK的目录添加入系统环境变量,这样可以防止绝对变量,可以很方面地进行以后的项目移植[10];
图2.8 环境变量设置
2.2 引擎架构
花上一段时间的编译、配置后,就可以开始深入地接触这一款大名鼎鼎的图像引擎了。OGRE,正如绪论中所述,使用C++开发,面向对象且使用灵活的3D引擎,由英国的Steve ‘sinbad’ Streeting在2000年开发,超过十年的积淀,并一直在开源社区下维护至今。它的目的是让开发者能更方便和直接地开发基于3D硬件设备的应用程序或游戏。引擎中的类库对更底层的系统库(如:Direct3D和OpenGL)的全部使用细节进行了抽象,并提供了基于现实世界对象的接口和其它类。
OGRE系统主要包括:Render系统和Render插件、Material系统,文件系统。
图2.9 Ogre架构体系[16]
首先介绍核心的Root对象,它包含了 SceneManager、Render System等其它对象的引用,Root是所有对象的组织者。
其负责的对象类族从功能上可分成以下四种:
1,场景管理类,负责管理场景中的内容:如何组织,如何排列,及如何使用Camera决定渲染队列等;
2,资源管理类,渲染都是基于都需要资源,如顶点网格、纹理、字体等等。资源管理模块负责相应资源的装载、管理、卸载等;
3,渲染类,负责具体对底层的渲染管线API,提交真正的渲染batch,将场景管理提交的渲染队列提交到GPU处理。
4,插件类,负责OGRE的扩展功能。OGRE中的很多类具有可扩展的插件化设计,如可引入一个自定义场景管理插件、增加一个新的渲染系统实现,添加新型的资源管理模块等。
OGRE 的整体类图如下所示:
图2.10 OGRE 类图[16]
Root 类包含一个 SceneManager 对象和一个 enumerator 对象,前者是当前的 SceneManager,后者负责装载其它类型的场景图形;
RenderSystem为抽象类,它将 Root 跟具体的底层图形库(OpenGL 或者 Direct3D)分离;
ResourceManager为抽象类,很多处理资源 (纹理、材质、字体等) 的管理器继承自它;
类图中除了RenderSystem,SceneManager和RenderWindow之外,其它的类都使用了C++模板实现单件模式,以保证每个类只有一个实例。
可以看一下Ogre的单帧渲染过程,以一窥豹斑:
图2.11 绘制一帧图形的序列图[16]
2.3光照计算
光源[5]:在程序中有三类光源需要考虑,分别是点光源、聚光光源和方向性光源。每类光源分别描述了日常生活中见到的不同类型的光。点光源可以是某个距离上向所有方向发光的任何光源;聚光光源通过圆锥发光;而方向光则是从特定的方向,通常代表太阳光。
反射:计算机图形学中有许多不同的反射模型,在实际程序中使用环境光反射模型、漫反射模型和镜面反射模型。
光照算法:大部分算法都可以归为两类,顶点级光照和像素级光照。顶点级光照计算每个顶点的光照方程,而像素级光照则是计算屏幕上每个像素的光照方程。这是在实时光照计算时经常遇到的问题。顶点级光照是Direct3D和OpenGL中图形API使用的一种照明技术。而通常像素级光照使用Shader实现。
预处理技术:预处理技术的一个例子就是所谓的“光度图”,阴在场景模拟中使用了预计算的光度图作为地形阴影。
2.4 Shader着色语言
Shader Model,在3D图形领域常被简称SM,通常Shader(着色语言)是指一段能够针对3D对象进行操作、并被GPU所执行的程序,语法风格类C语言。通过这些程序,程序员就能够从固定渲染管线中走出,随心所欲地操作GPU流程,获得绝大部分想要的3D图形效果。在一个3D场景中,一般包含多个Shader。这些Shader中有的负责对3D对象表面进行处理,有的负责对3D对象的纹理进行处理。最早在DirectX 8时,Shader Model的概念就出现在其中了,并根据操作对象的不同被分为对顶点进行各种操作的Vertex Shader(顶点渲染引擎)和对像素进行各种操作的Pixel Shader(像素渲染引擎)。
在Shader Model发展史上,从SM 1.0进化到SM 2.0称得上是真正意义上的技术革命,后者赋予了显示芯片强大的能力,人们在游戏中也领略到前所未有的视觉体验,例如水面光影和雾化等特效的出现使游戏场景更真实,在本文中就大量地使用到了Shader程序,用于渲染真实水体,实时材质修改等。
2.5 DLL 封装
动态链接库,DLL,是Dynamic Link Library 的缩写形式。动态链接库提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。DLL 还有助于共享数据和资源。多个中单个DLL 副本的内容。DLL 是一个包含可由多个程序同时使用的代码和数据的库。
动态链接库可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。在本文所实现的场景模拟程序中,大量地使用了DLL技术,将各个层次的模块,由API模块,引擎,地形编辑层,UI层,主程序,依次分离,形成了较高的扩展性。
2.6 Ogre场景图系统
下面先具体分析Ogre中的场景图系统[4],由于使用插件机制来实现场景管理功能,ROOT核心对象所产生并管理的ScenceManager只是接口,它可以有很多具体的实现,具体的实现由相应的Plugin。场景管理主要工作包括以下几点:
1.可移动、不可移动和可渲染物体的创建删除。
2.渲染队列。
场景对象创建:
场景中的所有对象,包括可移动与不可移动的:Camera、Light、SceneNode、Entity、ManualObject、BillboardChain、RibbonTrail、ParticleSystem、BillboardSet、Animation、AnimationState、StaticGeometry、MovableObject等13种物体的create、get、has、destroy方法都由场景管理器来创建。场景中的所有实体都由场景管理器来管理。任何通过场景管理器得到的东西,都必须由场景管理器来销毁。用户不能delete通过由场景管理器得到的指针。
场景实体主要的相同点是它们与相机保持一个常量的距离。它们可以在场景中其它对象之前或是之后渲染。它们使用普通的ogre material,因此纹理动画与其他纹理没有什么不同。它们可以被场景管理器打开或关闭,与相机的距离也可以设置。
场景图渲染过程:
OGRE通过WinMain或main调用go,初始化Root系统,再通过Root调用startRendering进行主循环,然后调用renderOneFrame,通过RenderSystem的_updateAllRenderTargets方法,更新所有的RenderTarget。RenderTarget通过update方法更新与之关联的Viewport并产生FPS统计信息。而Viewport则调用与之关联的Camera的_renderScene方法进行渲染,Camera将进入SceneManager的renderScene成员函数中,在经过计算后,把需要渲染的场景送给RenderSystem去做真正的渲染,这时真正到达熟悉的_breginFrame和_endFrame。 一直下去经渲染队列RenderQueue,RenderQueueGroup,RenderPriorityGroup,QueuedRenderable Collection再通过访问者到达QueuedRenderableVisitor的子类SceneMgrQueued RenderableVisitor,最终又回到SceneManager,由SceneManager再到RenderSystem完成整个渲染过程。
RenderQueueListener的作用就是在SceneManager开始渲染和结束渲染时改变render state和别的操作,比如检查本次RenderQueue是否skip,如果skip就直接break本次render。
可以看出render queue是Renderable的集合。其实Ogre整体的核心——场景树和渲染队列都是对Renderable进行分类的上层组织,只是分类的标准不同,场景树主要是从空间结构对Renderable进行分类,而渲染队列则是对Renderable从material以及blend上进行分类,下面再对Ogre的渲染队列做具体分析。
2.7 Ogre渲染队列
Ogre抽象出了一条完善的渲染队列系统,主要起两个作用:1.确保自动且正确的绘制顺序。2. 尽可能减少渲染状态的切换,提高渲染效率。这也是整个渲染系统的真正核心,首先可以看出,真正的渲染对象被封装了多层:
图2.12 RenderQueue 继承关系图
1. RenderQueue的组成
RenderQueue由Ogre::RenderQueueGroup组成的,RenderQueue中有一个RenderQueueGroup的Map的数据成员。RenderQueueGroupMap 的key为RenderQueueGroupID,代表Objects的渲染先后顺序。RenderQueueGroupID是一个枚举量,根据场景内物体的渲染顺序由先及后定义。
RenderQueue通过成员函数addRenderable 添加物体到渲染队列中,在RenderQueue的 getQueueGroup成员负责RenderQueueGroup的查找创建。RenderQueueGroup的生命周期由RenderQueue来控制。
2. RenderQueueGroup的组成
RenderQueueGroup中有一个RenderPriorityGroup的Map的数据成员: PriorityMap的key为一个ushort,它代表着RenderPriorityGroup渲染的优先级。对同一优先级的Objects,RenderQueueGroup会通过成员函数addRenderable 将它加入相同的RenderPriorityGroup中,RenderPriorityGroup的生命周期是由 RenderQueueGroup管理的。
3. RenderPriorityGroup的组成
RenderPriorityGroup中是存放需要渲染的Objects的最终场所。需要渲染的Objects——Renderable,RenderPriorityGroup组织将其组织为RenderableList,然后把RenderableList组织成SolidRenderablePassMap:
综上所述,需渲染的物体分别经过RenderPriorityGroup、RenderQueueGroup分类后,由RenderQueue统一管理。
3. QueuedRenderableCollection
RenderPriorityGroup有5个成员变量mSolidsBasicm、SolidsDiffuseSpecular、mSolidsDecal,mSolidsNoShadowReceive,mTransparents都是QueuedRenderable Collection.
QueuedRenderableCollection是存储Renderable和Pass的最终场所。通过多种排序实现Renderable和Pass的有序化。排序包括小于排序、深度递减排序和基数排序。
QueuedRenderableCollection组织Renderable和Pass有三种,分别是按Pass分组、按与camera的距离升序和按与camera的距离减序。
4.QueuedRenderableVisitor
QueuedRenderableVisitor是按访问者模式设计的抽象接口,最终在SceneMgr中被调用完成渲染。
6. RenderTarget
RenderTarget用来接收渲染操作的结果,它可以是屏幕上的窗口、离屏面(如texture)等。FPS信息的统计也是由RenderTarget完成的。在RenderTarget每次更新完成后,将会更新统计信息(封装于FrameStats中), 除了负责统计帧的信息外,RenderTarget还负责创建维护Viewport(视口)。
每个Viewport都对应一个Camera和一个RenderTarget。当创建一个Viewport后,它会自动建立与Camera的联系。可以把Camera看作是图像的来源,而RenderTarget是图像渲染的目的地。一个Viewport只能对应一个Camera和一个RenderTarget,而一个Camera也只能对应一个Viewport,但RenderTarget却可以拥有几个Viewport。
简要说Ogre渲染主流程分三步使用渲染队列:清空、构造和访问,这个过程在每一帧的绘制过程中重复执行。
Ogre在每一帧渲染前都会先清空渲染队列。熟悉Ogre渲染流程的很容易看到在SceneManager::_renderScene() 这个方法中调用prepareRenderQueue()方法。这个方法的实现很简单,就是清空现在的渲染队列并且初始化渲染队列的一些配置参数。渲染队列的清空函数是RenderQueue::clear(),该函数继续调用内部其它对象的清理函数。
2.8 Ogre可见性剔除
由于实时场景中会存在大量的可移动物体,所以考虑到效率问题,场景节点的可见性剔除就成了一个很大的问题,具体分析在前面渲染队列中提到的SceneManager::prepare RenderQueue()方法,在被调用之后,Ogre会继续调用场景管理器的_findVisibleObjects() 方法,寻找可见对象的。
通过取出场景管理器中的根节点,调用根节点的_findVisibleObjects() 方法完成寻找可见对象和渲染队列填充功能。这个函数是个递归函数,从根节点出发递归调用所有子节点的这个函数。将场景管理器传进这个函数意味着由每个node对象负责渲染队列的填充。在节点的_findVisible Objects方法中获取了绑定到node上的所有的MovableObject对象,并调用这些MovableObject对象的_updateRenderQueue方法。Ogre中有很多继承自MovableObject的对象,这里我们从最熟悉的Entity对象来继续我们的分析。Entity是一种ovableObject对象,所以如果该节点上绑定的对象是Entity对象,那么节点上调用_updateRenderQueue方法其实就是调用Entity对象的_updateRenderQueue方法。
Entity对象中包含SubEntity对象,而只有SubEntity对象才是可以显示的对象,所以在Entity的_updateRenderQueue方法中获得所有可以显示的SubEntity对象,并将其放入作为参数传入的渲染队列中。
这里是最终对渲染队列的添加操作,调用RenderQueue的addRenderable函数。addRenderable函数有几个重载的版本,根据需要可以选用合适的函数。
2.9大规模场景四叉树
Ogre中另一个重要的插件,另一个继承于SceneManager类,Ogre以插件形式提供了多种场景管理器,如 BSP、Terrain、Octree,使用它们SceneManager负责挂接的SceneNode根据自身的特性进行管理[12].
可见这个操作利用SceneManager的空间管理算法来对所有的SceneNode进行了可见性判断,如果可能可见,则加入到RenderQueue中.四叉树 (QuadTrees)可以说是2叉树的扩展形式。如果在程序中通过遍历查找对象,那是相当消耗资源的,而且会随着数目的增加而呈正比例消耗;但4叉树则不同,他通过预先建树的过程把对象整理到一个完整的树状结构中去。查询的时候只要根据要求的范围,通过遍历树节点的方法即可得到想要的.
依照空间区域将场景划分成4等分,然后以这4个区间分支点把在各自区间的对象添加到对应支点的哈希表里边;然后再分别对这4个区间各自进行4等分,重复上边的步骤,(黄线区间),然后在如此类推,一般说只要进行5-6个层级,这个过程称之为建树;建树完毕接下来就是用树;在场景中使用的是19300*19300的场景,有近百万面的数量,通过四叉树6建树,但可以快速、高效地完成搜索。
2.10 Ogre资源组织
图2.13 资源管理系统组织[16]
OGRE文件系统的核心是Archive和DataStream。其中:Archive泛指文件容器,可以是文件加,也可以是压缩包,甚至可以为远程位置(Ogre1.06还未发现有此功能)。DataStream完成与std::basic_istream相同的功能,它既可以读取普通的文件,压缩文件,以及内存文件等[7]。
Archive:
为完成Archive的功能(如列举目录下的文件,获取文件信息,查找文件等)OGRE提供了一个统一的接口(抽象基类)Ogre::Archive,具体的实现分别由Ogre::FileSystemArchive和Ogre::ZipArchive完成。由名称就可以看出它们的功能。Data Stream:
Data Stream完成与std::basic_istream相同的功能,但Data Stream的数据来源多种多样,包括与STL stream接口不兼容的lib(如zlib)。为满足各种情况,OGRE为Data Stream提供如下的继承体系,从名称可以看出各自的功能:
Archive和DataStream:
Archive可以看作是文件夹(包括压缩的),里面包含有文件,而DataStream是对文件的操作(读取、修改等)。读取文件夹是为了读取其中的文件,因此,一个程序若提供了一个ArchiveFactory,也应该提供一个DataStream以从Archive中读取出数据。除非现有的DataStream实现能够满足需求。
Ogre通过ResourceManager对Archive及DataStream实行管理,依次有“未载入,申明,解析,载入”若干状态,从而完成自动化的资源管理功能。
资源状态:
图2.14 资源状态变化[16]
未定义:
游戏程序启动时所有资源的默认状态,资源未被索引,所以OGRE不知道它们的存在。
已声明:
资源已被索引,OGRE已经知道这些资源的存在,但它们还没被初始化。
(平时我们读取了资源配置文件之后,资源就进入了这种状态。)
未载入:
资源已被初始化——被初始化的资源都生成了一个分别对它们的引用(这些引用会占用小部分内存),但资源还没被载入到内存。
(调用“ResourceGroupManager::getSingleton().initialiseResourceGroup ("General");”函数后,之前被索引的资源就进入未载入状态。)
已载入:
资源被激活,资源数据存在于内存中,可以被游戏程序直接调用了。(如果游戏程序调用未载入的资源,并且资源管理器的内存配额没有超出,则OGRE会自动帮你载入这些资源。)
2.11 Ogre材质
Ogre对具体的模型采用material材质文件决定其所有的渲染特性,文件资源组管理器初始化完毕后,就可以装载材质脚本,OGRE会自动的在组相关的资源位置查找".materal”扩展名的文件,并对这些脚本进行语法解析[13]。
每一个material X代表是一个材质单元。其中,每一个technique代表一个材质渲染手法,pass是每个渲染通路模式, texture_unit则是一个纹理单元。
一个材质脚本允许有多个渲染手法技术,一个渲染手法技术中允许有多个渲染通路模式,一个渲染通道允许有多个纹理单元。
方案技术scheme:因为我们对不同的显卡标准或根据某中不同的需求,设计出不同的技术,每种技术所适用的环境方案需要我们指出。
渲染技术Techinique:一个“渲染技术”就是一个单独的渲染物体的方法。多个技术的存在原因是为适用不同的显卡以及根据远近关系对一个物体进行不同的渲染。
在Ogre中,Overlay、Entity、Particle等都会用到材质。 Ogre中的材质脚本只不过是指定程序该如何去使用纹理(Texture),具体关于材质脚本的编写及参数可参考ogre中文手册。
材质做为一种资源,它也有它自己的资源管理器(MaterialManager)。 一般管理器都是使用了单件模式,在程序中都只有一个对像。 要获取一个材质在程序中就可以这样MaterialPtr mat= MaterialManager::getSingleton().getByName (strName),其中strName就是材质脚本中定义材质的名字了。 这样我们就可以通过mat(可以是在当指针的方式用)在程序动态修改我们的材质了。
第三章 系统分析与设计
3.1 综合目标
运行平台目标:Win32平台为主,同时可以利用Ogre的跨平台特性,在Unix,iOS等平台上做相应的扩展。
界面目标:使用高效的MyGUI界面,定义完善的快捷键系统,使操作者可以快速的完成设计工作。
速度目标:运行时画面流畅,操作无明显延迟感。
快捷目标:完善的快捷键,明确的UI信息提示。
3.2 具体需求
要实现三维场景的实时模拟,并加入完整的可实时编辑功能,需要考虑到很多问题,在完成基本的场景显示后,具体的编辑功能需求如下:
(1)地形变换功能:能够实现地形高度的实时编辑;实现光影的实时更新及优化;
(2)地表纹理编辑功能:可以实时地编辑地表纹理;可以选择,并在UI上现实当前选择的纹理图块;
(3)景物添加功能:可以实时地添加物体进入场景;在UI上高效地树状显示所有物体,并渲染至UI预览;
(4)景物编辑功能:高亮,精确到mesh的点击选择;物体实时地移动,旋转,缩放,地表高度匹配;
3.3 整体架构
Ogre中大量的优雅的插件话设计令人折服,同时出于对效率,性能,及本身将来扩展需求的考虑,对程序的设计从一开始就站在模块化的角度出发。几经重构,最后程序的架构如下:
图 3.1 具体框架组织
图 3.2 整体程序框架
这样的体系很好集成了Ogre引擎自身的优点,不仅方便了问题的分步化解决,在后面的拓展工作中,可充分体现到了模块化的设计的可扩展性优点。下面具体介绍单独的模块.
在具体组织架构中,将项目解决方案分为四个独立的工程,其中主工程BloodRain通过DLL方式将其余代码调用。
主核类:
图 3.3 App类图
核心的App主类继承自Ogre的ExampleApplication,它负责完成整个系统的初始化。
主线程监听器:
图 3.4 主流程处理类
在初始化完成后,系统的主循环便转移到CProcedure,利用监听器模式,接受从OIS传入的鼠标,键盘,Win32等各种信息。
系统UI设计:
图 3.5 GUI控制类
系统UI控制类PanelView负责对UI的处理响应,嵌入于CProcedure内部。
3.4核心场景渲染模块
核心的场景渲染包括了整个场景地形,地表纹理,景物的加载,由ETM(EditbleTerrainManager)模块负责,
3.5地形变换模块设计
图 3.7 地形变换模块设计
地形的分辨率为19300*19300,由193*193个顶点构成,拥有三层贴图,Terrain Info决定了所有顶点的数据,载入TerrainManager中的TerrainImpl负责管理,如果作为整体同时渲染,显卡难以承受,所以在实现中,地形细分为32块地块,由Tile分别负责地块的管理,接受地表高度的调整,这样Ogre可以高效地实行剔除,提升效能。
3.6地表纹理编辑模块设计
图 3.8 地形纹理编辑模块设计
在前面提到的地表,是由三层纹理构成,由Shade负责值的混合,其中两层为地表纹理,第三层为自动生成的光度图,在程序中,可由Panel决定地表纹理编辑模式后,选择纹理图,决定使用的Brush(地表纹理刷子),传入Procedure,再具体
3.7景物添加模块设计
图 3.9 景物添加模块设计
首先要确保在resource.dfg中配置使资源管理器中添加对资源目录的引用,完成资源的定义及材质脚本的解析,再在MainPanel中,使用MyGUI的扩展插件完成景物列表的显示,最后,在Procedure中,如在SceneAdd状态下,对鼠标与地形实时碰撞检测,得到景物的放置位置,在左键按下是加入场景图。
3.8景物编辑模块设计
由于编辑器的多种状态转换,要快速的进行编辑就显得很麻烦了,在实现中采用了一个枚举变量来标志不同的编辑状态,并采用F1~F4的快捷键分别对应核心场景编辑模块。
图 3.10 编辑状态切换
景物编辑模块在实际中应是经常被调用的一个模块,为了能高效地处理,直接将逻辑内嵌于Procedure之中,对下列消息进行响应处理,具体过程,在下一章实现中讨论。
第四章 系统实现
4.1资源准备
首先,程序所需的资源大体可以分为以下三大类:
核心资源[9]:
·Media:Ogre引擎自身所包含的一些基本资源,包含一些Shader脚本,Overlay脚本,及一些可以提供测试之用的模型,材质文件
·Hydrax:水体引擎所包含的水面反射法线贴图,及相关的材质处理脚本
·ET:地形编辑的基本资源库,其中包含:基本的笔刷,基本的地表纹理,基本的地表材质
界面资源:
·Ani:基本的动态鼠标指针
·Layout:MyGUI使用的UI组织分布
·MyGUI_Media:包括一些列的资源配置,界面方案配置,及使用的贴图,纹理,字体
场景资源:
·Brush:地表纹理资源
·Mesh:场景模型资源
·Sketelon:场景动态模型骨骼动画
·Material:场景模型材质
·DDS:场景模型贴图
·ini:场景全局的相关配置
·Heightmap:场景地形使用的高度图文件
·lightmap:场景地形使用的光度图文件
·GridInfo:场景地形使用的网格信息文件
·Terrain:场景地形配置文件
·Scene:场景模型组织配置文件
4.2核心场景渲染模块的实现
场景载入:
场景的地形高度信息全部保存在一幅二位图像中,以图像的RGB值换算成实际高度。高度图可以使用PS等二维工具生成,也可使用3DMax或者专业的地貌软件生成,或者直接在编辑器内修改。
图 4.1 高度图示例
有了地形高度,场景还只是一片空白,这里就先借读取天龙八部的资源,其地形资源由xml文件配置,在程序中通过TinyXML对相应场景文件的加载解析进行的。
过程如下:
1,加载场景名的Terrain文件;
2,根据Terrain配置文件,加载所有要用的地形贴图;
3,读取光度图,配置场景阴影图;
4,读取场景大小(<tileSize>) 地形大小(xsize, ysize),缩放值(<scale>),地图中心坐标(<center>);
4,读取.gridinfo 文件,地形贴图;
5, 加载Heightmap,载入实际高度图信息;
6,根据以上数据,载入ETM地形模块,创建实际三维场景;
7,读取Scene文件,加载场景中使用的模型,并插入到场景Root中。
读取信息完成后,将数据导入到ETM[8]中。其中,地表由很多个格子(Title)组成,每个格子四个点,两个三角形GridInfo文件决定了每个点的位置,UV,法线,材质。
其文件格式如下:
DWORD nVersion 版本号
int nWidth 地表宽度(横向格子数)
int nHeight 地表高度(纵向格子数)
BYTE nFirstLayerOp 对nFirstLayer的操作,可能取值如下:
0 不变,1 水平翻转,2 垂直翻转,4 向左旋转90度,8 对角线镜像
BYTE nSecondLayer该值为pixelmap[6]的索引
地表最多可以两层融合,说白了就是每个点里有两层UV,这里为第二层pixelmap的索引BYTE nSecondLayerOp对nSecondLayer的操作,取值同nFirstLayerOp BYTE IndexOrder对格子的三角形的操作,可能取值如下
0正常三角形索引
1不同于正常的三角形索引
如下图,该值主要用在水池啊一类的地方,如果三角形索引不变的话,水池四个角中的两个角就不对了。
图 4.2 索引示例
实时地形阴影:
光度图:与高度图类似,以预计算的信息表示位置的阴影。
图 4.3 光度图示例
地形是可编辑的,后面将会提到的ETM中有createTerrainLightmap函数,可根据灯光的方向计算出地形中每个顶点的阴影,在Procedure只需将计算的结果拷贝到地表贴图的第三张动态纹理,也就是光度图上,从而实现实时的地形阴影。
在具体的实现中具体做了一定的控频处理,及在逻辑上判定正在调解高度时不予更新光度图,以降低系统消耗。
实时物体阴影渲染:
在Ogre中,可以自动化地实现景物的阴影,并可以处理AplhaTest,实现树荫斑驳的效果。
//设置阴影
mSceneMgr->setShadowTechnique(SHADOWTYPE_TEXTURE_MODULATIVE);
mSceneMgr->setShadowTextureSize(2048);
mSceneMgr->setShadowColour(ColourValue(0.6, 0.6, 0.6));
mSceneMgr->setShadowTextureCount(2);
mSceneMgr->setShowDebugShadows(false);
mSceneMgr->setShadowFarDistance(3000);
完成阴影类型,阴影贴图尺寸、数量(决定可产生投影的最大灯光数),阴影颜色,最大阴影渲染范围(节约资源)后,Ogre会自动在渲染队列中加入对阴影图的产生及处理。
真实水体渲染:
水体效果在实现中具体使用了两套方案,首先是OgreAddon上开源的HydraX,实现了完整的折射,反射,蚀刻,阳光等效果,但是硬件要求过高,这里先引入一种简易的水体渲染:动态纹理+深度图。
一般情况下,水面TerrainLiquid要实现比较好的效果主要依靠两种方法
1,利用种子坐标填充整个水面
2,利用深度图纹理控制水面透明图
种子填充可以利用现有的地形信息快速完成,接下来可以根据得到的水面深度控制实际的透明度:
图 4.4 深度图纹理
在每个顶点生成的时候设置纹理坐标。第一层纹理是动态纹理,第二层是一维的深度图纹理。第一层纹理坐标的设定要根据 TerrainLiquid 中 texture_scale值来确定。由于Ogre默认的纹理映射方式是wrap,就是说Any value beyond 1.0 wraps back to 0.0. Texture is repeated (引用自Ogre官网的Manual). 我们不需要考虑纹理坐标大于1或者小于0的状况,它自己会映射到正确的位置,所以我们只要将(x,y)处点的纹理坐标设为( x*texture_scale, y*texture_scale )就OK了。
第二层纹理是一张宽度为256,高度为1的一维纹理,有4通道,每个通道的值都是从0-255递增。映射方式为clamp,所以纹理坐标在在大于1.0时,会映射到1.0.所以( depth texture layer.height scale*水面深度)求出的就是第二层的纹理坐标。水面深度=种子坐标高度-当前点的地形高度。
其次,是完整地利用实时的反射,折射,蚀刻,阳光反射实现的真实水体渲染:
图 4.5 HydraX水体渲染效果
HydraX已有很良好的封转,在程序结构上做了第二次封装,将其独立于主线程之外,如需开启,只要在App中载入ROOT对象监听器中载入HydraXListener即可。
在HydraXListener中,首先初始化其管理类mHydrax = new Hydrax:: Hydrax(mSceneMgr, mCamera, mWindow->getViewport(0)); 然后加载水面mesh及相关配置即可。
在资源类介绍里提到的HydraX.hdx为其配置文件,通过#Sun parameters(阳关反射率),#Foam parameters(水面泡沫), #Caustics parameters(水下蚀刻),#Module options(波浪频率高度),#Noise options(水面波纹)等参数可以调配出真实可信的水面效果。
图 4.6 移植效果
在国外主流的次时代游戏程序中,往往使用一种SpeedTree的中间件,从而实现大规模的植被渲染,Ogre的AddOn上同样类似的一个“PageGeometory”的实现,通过区块的管理,及将距离远的树木渲染成平面,以BillBoard显示,可以很好地实现普通程序需要百万面才能实现的效果。
在测试项目得到了很好的显示,但由于美术资源上的限制,在程序中植被的实现仍是由简单的场景剔除和Mesh的LOD实现的,这里可作为以后的一个扩展方向。
图 4.7 大规模森林渲染
4.3地形变换模块的实现
地形编辑基于ETM,负责Tile信息的管理,由鼠标点击的位置转换为
// brush的强度
float brushIntens = evt.timeSinceLastFrame * 0.4 * (mLMouseDown? 1 : -1);
// 获得位置
Vector3 deformPos = mPointer->getPosition();
int x = mTerrainInfo->posToVertexX(deformPos.x);
int z = mTerrainInfo->posToVertexZ(deformPos.z);
// 传入ETM修改Tile
mTerrainMgr->deform(x, z, mEditBrush, brushIntensity);
地形海拔高度的变化程度由鼠标按压的时间决定,按下左键提升地面,按下右键沉降地面。
图 4.8 地形高度改变
4.4地表纹理编辑模块的实现
当前场景所有的地表纹理均在4.2中所示的scene文件中配置,正确载入后,同样为了减少Batch(显卡的提交批次),在实现中使用了一种Atlas(大型纹理集合的技术),将所有的地表纹理统一整合到一张1024*2048的大型贴图上,同时将altlas做一定比例的压缩,存放成一张 1024*128的贴图,用于后面的UI显示。
图 4.9 Atlas图
同时,由于两层地表纹理的存在,在实现中,将Tab键设置为纹理层的切换,通常,可将第一层作为场景的地表基本纹理,将带有Alpha颜色的半透明贴图作为第二层贴图以丰富整体表现力,示例如下图:
图 4.10 地表纹理改变
4.5景物添加模块的实现
在Procedure模块中,使用了Delegate设计模式,类似于回调注册函数,将PanelView中的消息直接挂载到上一层处理:
mMainPanel->mpResourcesTree->eventTreeNodeSelected=MyGUI::newDelegate(this,&CProcedure::OnGUITreeItemSelected);
mMainPanel->eventAction = MyGUI::newDelegate(this, &CProcedure::notifyEventAction);
分别处理了在UI面板中定义的若干消息,及树状控件点击的消息(即景物点击添加的信息),只要MyGUI处理到有根树节点收到了点击消息,就调用:
String EntityName="PointerSceneEdit"+ObjectName;
if(mSceneMgr->hasEntity(EntityName))
newPoint=mSceneMgr->getEntity(EntityName);
else
newPoint=mSceneMgr->createEntity(EntityName,ObjectName);
mPointer->attachObject(newPoint);
mMainPanel->mRenderBoxScene.injectObject(ObjectName);
首先确定所选择的Entity是否存在,已决定是否创建新的实体,然后,将其挂接到鼠标指针上,实现在场景可预览的拖放效果,最后,同时将模型名通知于UI面板的RendrBox模块,使模型在UI面板中渲染,方便观察一些大的模型。
图 4.11 景物添加
4.6景物编辑模块的实现
精确碰撞检测:
利用场景查询,加上Entity的mask(防止选到地形所使用的Tile),得到具体Entity所在的SceneNode,然后根据具体的操作执行相应的矩阵变换。
mRaySceneQuery->setRay(ray);
mRaySceneQue->setQueryTypeMask(Ogre::SceneManager::ENTITY_TYPE_MASK);
mRaySceneQuery->setSortByDistance(true);
在实际中,由于场景内实体众多,SceneQuery所执行的是包围盒检测,所以还要在得到的Entity列表中做精确到三角形面的检测,void GetMeshInformationEx()具体不再展开。
实时模型材质修改:
在material应用shader程序,可以实现许多特殊效果,例如在点选物体时的高亮效果,就是为材质动态添加了一个pass,缩放物体法线,赋予半透明颜色产生。
图 4.12 物体点击高亮效果
为了实现被点击物体的高亮效果,需要实时地修改材质,必须考虑到只有被点击的Entity产生高亮效果,且在完成操作后能够回复到正常状态显示。
首先,如果一个Entity被点击,检测是否有上一个被点击,如果有,则遍历其SubMesh,将存在数组里的原始材质恢复。
for(unsigned int i = 0;i < m_pSelectedEntity->getNumSubEntities();++i)
{
//将先前选择的物体效果移除
m_pSelectedEntity->getSubEntity(i)->setMaterialName(m_OldMaterialVec[i]);
}
再获取Entity,显示其包围框,所有SubMesh的材质,为每一个材质都增加一个渲染过程Pass,
物体移动:
为了实现精确的位置调整拖放位置,设置为判断Shift,Ctrl,Alt点击后,拖按左键执行,响应OIS传入的鼠标相对位移值,分别在XYZ轴向上移动,当Caps键按下时,则直接通过当前鼠标点击的位置的地表高度决定位置:
if(mLShiftDown)
pos.y+=arg.state.X.rel * m_fMoveSpeed-arg.state.Y.rel * m_fMoveSpeed;
else if(mLAltDown)
pos.x+=arg.state.X.rel * m_fMoveSpeed+arg.state.Y.rel * m_fMoveSpeed;
else if(mLCtrlDown)
pos.z+=arg.state.X.rel * m_fMoveSpeed+arg.state.Y.rel * m_fMoveSpeed;
else if(mLCaptial)
pos=mPointer->getPosition();
m_pSelectedSceneNode->setPosition(pos);
物体放缩/旋转:
类似于物体移动的实现,在Shift,Ctrl,Alt按下的情况下,分别响应:
m_pSelectedSceneNode->roll(Degree(-arg.state.X.rel*mRotateSpeed)+Degree(-arg.state.Y.rel * mRotateSpeed) );->pitch();->yaw();
缩放直接判断arg.state.Z.rel,调用ScneNode的->scale(0.9f,0.9f,0.9f);
->scale(1.1f,1.1f,1.1f);执行缩小,放大。
4.7系统UI的实现
与主线程的交互:
图 4.13 Procedure成员及方法
在Procedure中绝大多数的方法为标志的开关,点击位置的信息,鼠标键盘消息的缓冲,及相关地形编辑的接口,PanelView嵌于此主线程中,即可很完善地完成与使用者的交互。
树状图及渲染到纹理技术:
树状图为MyGUI中的UnitTest项目,经过对中文的支持改造,可以使用Ogre的资源管理系统中Archive对资源目录进行枚举,完成所有景物的列出显示。
另外,使用MyGUI的RenderBox,在Ogre中建立一个SceneMgr,将模型渲染至景物选择面板。
图 4.14 渲染到纹理
4.8 其他
帧率测试:
调用Ogre中的mTrayMgr,可以将每一帧统计出的三角形面数,渲染批次数。可以分析出tile所覆盖的区域是否合适,在实现中将F6,F8,分别设定为显示渲染统计, 线框/实体渲染方式切换。
图 4.15 帧率,线图显示
性能分析:
用微软DX开发组提供的官方调试工具PIX可以分析出具体渲染调用,
图 4.16 PIX渲染批次分析
第五章 总结与展望
5.1 总结
在决定开发目标后,工程下日志的文件从去年的12月一直写到现在,也坚持将体会心得写在博客上(cnblogs.com/hmxp8),其间遇到了很多问题,经验的缺乏,对Ogre的不熟悉,技术上的困惑,时间上的窘境都让自己卡住多次,特别是几次因为最初设计上的不足导致几次大的重构,过程是非常痛苦的。
还好坚持了下来,如今,看来一切都是值得的,做出了一个模样还算过得去的东西,但明白,要做的还有很多,最佳的代码永远是下一行,相信自己还会一直写下去的,
5.2 展望
种种限制,导致了许多想法并为实现,程序可以做的还有很多,简单的陈列如下:
(1)物理系统的加入,碰撞检测功能的加入;
(2)节点动画系统;
(3)粒子系统;
(4)动态天空系统;
(5)Ogre引擎的ScreenSpace的ShadowMap改造;
(6)植被,草体系统的实时编辑;
(7)QT重构编辑界面
(8)LOD优化,巨型场景,室内外场景结合;
(9)物件脚本,AI系统;
(10)网络交互模块;
参考文献
[1] 铁男.浅谈虚拟现实技术的研究现状及发展趋势[OL].
http://blog.sina.com.cn/s/blog_5ddbd1060100ec6r.html.
[2] 王健美、张旭、王勇、赵蕴华.美国虚拟现实技术发展现状、政策及对我国的启示.科技管理研究2010,30(14).
[3] Oiramario.手把手教你如何配置和编译ogre 1.7.0 + cegui 0.7.1 [OL].
http://www.cnblogs.com/oiramario/archive/2010/03/03/1677461.html.
[4] wangwang123654. ogre的主要渲染流程[OL].
http://blog.csdn.net/wangwang123654/archive/2009/12/10/4980088.aspx.
[5] shareany3.Direct3D光照导论 [OL].
http://hi.baidu.com/shareany3/blog/item/2951fb273458a8388744f91d.html.
[6] 飞天翔.Ogre《天龙八部》GridInfo文件格式说明[OL] http://www.mobilegamebase.com/blog/article.asp?id=17.
[7] 清源游民.Pro Ogre 3D Programming》读书笔记之第七章资源管理[OL].
http://www.cppblog.com/yuanyajie/archive/2007/03/14/19827.html
[8] KingMars' blog .修改ETM,用Ogre实现《天龙八部》地形与部分场景详解 [OL].http://www.cnblogs.com/syqking/archive/2009/10/20/1586993.html.
[9] 傅志阳.让Ogre支持中文路径与中文文件名[OL].
http://blog.csdn.net/zyfu0000/archive/2009/12/07/4958731.aspx
[10] shongbee2.OGERE源代码编译超详细步骤》[OL].
http://hi.baidu.com/shongbee2/blog/item/82426f2b8d6b44f6e6cd40b7.html.
[11] 张民.基于HLA的OGRE引擎的实现及应用研究[J].电子科技大学学报,2007,36(1):112.115.
[12] Felix Kerger .Ogre 3D 1.7 Beginner's Guide[M]. Packt Publishing Ltd.November 2010.
[13] Gregory Junker.Pro OGRE 3D Programming[M]. Apress,September 26, 2006) .
[14] 佚名. 2011年中国网游市场规模[OL].
http://www.mingren365.com/html/View_78190.html.
[15] 国新办网. 2009年中国网络游戏市场白皮书[OL].
http://www.scio.gov.cn/wlcb/hzlhb/201002/t553901.htm,2010-02-25.
[16] Ogre开发组.OgreWiki [OL]
http://www.ogre3d.org/tikiwiki/tiki-index.php
致谢
十分感谢刘老师的赏识,不仅仅是课业上的教授,书本之外从刘老师的开放前进的思想,宽阔的视野更使我受益良多,没有刘老师的指导,立意,真的很难想象自己能从一个许多人当成应付作业的毕业设计中学到那么多知识,感谢他的指导。
同时,众所周知,国内在三维图形方面上起步较晚,特别是三维场景实时编辑方面的资料更是匮乏,非常感谢网上无私分享经验的高手们,当然,更得感谢开源项目的维护者们,正是他们公平的信仰才使在地球另一个小角落的平凡大学生接触到了世界上最为杰出的代码设计.
感谢家人对我爱好的理解和支持,没有父母的循循善诱,或许我的兴趣或许还是一直停留在玩游戏,而非写游戏之上。最后,感谢大学四年中可爱的老师,同学们,一路走好,一切顺利!