Ogre(Object-oriented Graphics Rendering Engine)是一款优秀的C++开源图形渲染引擎。OGRE主要提供渲染引擎,但是在系统API,文件管理以及范例中都提供的非常丰富的接口和范例,在设计模式上也有很多考究,代码书写的也比较规范,无论是作为使用还是学习,都有着非常不错的价值。
OGRE整体不算小,目前支持Windows、Linux、IPHONE、MAC、SYMBIAN五个平台,作为一个学习三维的新手来说,个人认为难度还算不小。当你运行范例框架时,在惊诧其界面绚丽之余,多少很有一点无从下手。而且OGRE也借助了不少第三方库,其范例界面俨然就是一个典型的Windows游戏界面, 对于没有接触过的人们来说,和在.Net下的界面开发有很大的不同,这也加大的初学的难度。如果仅看一个一个的局部案例而缺乏对整体框架的理解,又难免有管中窥豹之遗憾。
本文主要是针对范例框架部分的介绍,涵盖范例框架启动、显示和其后的UI界面操作部分。针对范例框架,分析了OGRE在初始化、创建窗口、鼠标键盘事件响应、资源加载以及范例加载、渲染显示这几个模块的技术实现,另外对这几块所用到的框架和设计模式进行了总结。算是对所学知识的总结,也方便接下来对案例所涉及的三维细节技术的研究。
备注:OGRE下载编译参考
Ogre范例写的很简单,main函数中仅有如下两行
OgreBites::SampleBrowser sb; sb.go();
可见,范例完全封装在SampleBrowser类中,只需要调用一下go函数即可。但这两句话的效果却非常丰富,展现给我们一个交互式的范例展现界面,期间经过了root的初始化、窗口的创建、鼠标键盘事件的绑定和监听,系统资源的加载、范例程序的加载等,旅途可谓丰富多彩,期间无论是框架还是技术细节实现,都值得学习和推荐。不妨现在就开启此次旅途第一站。
createRoot
Ogre::Root* mRoot是SanmpleBrowser最重要的成员变量,和三维相关的渲染引擎、窗口、以及事件监听都是有其实现的,并完成整个过程的驱动,数据更新和渲染。
SanmpleBrowse启动的第一步,就是Root类的构造,Root相当于OGRE的地基,渲染场景和引擎、以及各种文件系统的管理和加载都是在Root构造函数完成的,代码相对简单明了,如果对UGC插件机制了解的话,插件加载等方式也基本一致,相对难度不大。
Root构造函数主要进行了:
1. Manager初始化(DynLibManager插件管理、ArchiveManager文件管理、
ResourceGroupManager资源文件管理、OverlayManager管理)
2. 具体文件系统的加载(普通文件、zip)
3. 窗口的初始化(mAutoWindow = 0)
4. 插件加载(loadPlugins(pluginFileName),渲染引擎的加载)
其后会根据配置文件设置Root的属性,最关键的是设置渲染引擎类型(OpenGL、D3D)
在Root构造之后,便开始窗口的初始化创建过程,其本质是调用Root函数的initialise()函数,返回RenderWindow的窗口指针。
Initialize函数调用非常方便,但内部处理还是非常复杂,关键成员对象如下:
l Rendersystem(GLRenderSystem)
l GLSupport(Win32GLSupport)
l RenderWindow(Win32Window)
l Win32Context
其中,Rendersystem就是GL的渲染引擎,Win32GLSupport负责屏幕相关的大小,位深,像素等信息的传递,而Win32Window则负责调用Win系统函数来实现窗口的创建,以及与OpenGL建立关联,而Win32Context则用来存储创建窗口后的句柄等信息,函数大致过程如下:
mRoot->initialise(true, "OGRE Sample Browser");-> Rendersystem _initialise->
mGLSupport->createWindow(Win32GLSupport)-> Win32GLSupport::newWindow->
Win32Window.create-> mContext = new Win32Context(mHDC, mGlrc);
Win32Window.create函数中,在CreateWindowEx创建窗口后,通过wglCreateContext和wglMakeCurrent,将OpenGL与窗口的HDC建立关联,而Win32Context用来存储此句柄
如上,一个空白的Windows窗口创建完成。
窗口创建完毕后,下一步就是鼠标键盘事件的处理了,在OGRE中采用了OIS开源库来负责打理相关事务,OIS库专门用来处理外部设备(键盘、鼠标、操纵杆、触摸屏)和程序之间的,而且支持跨平台,在游戏程序中还是非常实用的。
另外分析一下SampleContext类,继承自FrameListener,WindowEventListener,KeyListener,MouseListener四个类,FrameListener用来监听OGRE三维渲染中的事件响应,WindowEventListener则用来监听Windows消息事件,而后两者则处理程序内部事件响应。
OIS主要文件有三类:
l OISInputManager
统一处理外部设备的管理类
l OISKeyboard
负责键盘事件的处理类
l OISMouse
负责鼠标事件的处理类
使用时,调用OIS::InputManager::createInputSystem返回OIS::InputManager管理,核心函数为DirectInput8Create函数,虽然在msdn中都没有此注释,可不要小看这个函数,用于创建和Win下外部设备交互的管理对象,封装成OIS的InputManager返回给用户来管理。由InputManager创建出对应的鼠标键盘对象。
在使用中,可大致分为三个模块来实现外部设备的响应:
l 外部事件的响应WindowEventUtilities::messagePump(),用来接收系统事件,比如激活当前窗口,关闭,窗口变化等等,处理程序之间的消息事件
l 在OGRE渲染过程中,之前将SampleContext类添加到Root中进行监听
(mRoot->addFrameListener(this)),实时渲染中renderOneFrame会调用监听类的
captureInputDevices,分别处理键盘和鼠标类的响应(mKeyboard->capture(),mMouse->capture();)
l 在鼠标和键盘监听中,会在capture中分析当前的行为(鼠标移动,按键操作等),最终在SampleBrowser中实现响应操作
如上,通过监听类和OIS开源库,采用回调函数,实现了外部设备和程序的交互操作
之后是加载范例中所用到的资源文件,也是是程序加载过程中显示进度条的过程。此时主要是由SdkTrayManager、OverlayManager和ResourceGroupManager来处理,前者用来负责界面进度条更新,OverlayManager用来负责界面显示部分,后者用来加载资源。首先构造进度条,进度条和其描述内容都是通过构建OverlayElement完成。
OverlayElement是一个非常有用的界面显示元素,有点类似Dreamweaver中的层的作用,一些在屏幕中界面元素,比如游戏视角中的驾驶舱,指南针等相对影像不变的部分,都可以以OverlayElement的形式来体现。OverlayElement本身有一些固定模板,也可以通过配置文件的形式来构建,可见,OverlayElement在游戏中是一个非常常用的对象。
SdkTrayManager构建完界面元素对象后,则加入到ResourceGroupManager中进行监听,而在ResourceGroupManager中会加载resources_d.cfg中包含的资源文件,分为二进制和zip压缩文件两种,每次解析文件时通过监听机制,对SdkTrayManager进行数值更新,而SdkTrayManager则驱动RenderWindow更新(renderwindow->update)
Root中Render渲染过程:分为Update和Render两部分,但均在一个线程下完成,在一系列Update后,最终调用GLRenderSystem的Render,完成此帧的渲染,其顺序大致如下:renderwindow->update-> rendertarget update-> SceneManager Update RenderQueue-> SceneManager::renderSingleObject-> GLRenderSystem::_render(const RenderOperation& op),详细的就不多说了,这也是今后研究的重点了。
最后,资源加载完,设置进度条对应了Overlay为不可见,则资源加载过程完毕。
资源加载完毕后,则开始导入具体范例,生成Sample对象来统一管理范例。
setupWidgets()则负责完成范例界面相关的部署
OGRE对范例也定义了一系列规范采用插件的方式来加载范例,最终由mLoadedSamples容积保存。mThumbs负责存储范例的缩略图容器,本身也是Overlay的容器集合。
在范例加载完成后,OGRE的界面显示过程宣告结束,这时用户通过键盘鼠标的外部输入来进行范例的选择。同样通过OIS来进行维护,判断键盘的上下移动,更新显示内容。
OGRE上述过程只是冰山一角,但就复杂度来说也不低,得益于OGRE的框架设计,OGRE在使用中设计的非常优良,在实现最初就做过很多好的设计模式,而且定义的很规范,这些程序员对C++编码经验可谓非常丰富。在如上的学习中,个人大概总结了三个模块:
1.三维渲染框架
一个while(true)类似的循环,则开始了OGRE中每一帧的渲染。OGRE中渲染框架分为Update和Render两部分,均在一个线程中完成,这在游戏中,数据量不大的情况下感觉没有问题。通过Root驱动,最终,构造出RenderOperation来让RenderSystem进行渲染。其渲染思路和我们的可以说完全一致,而我们则是在此思路基础上用两个线程来分别管理数据和渲染,在实际应用中则有更好的应用范围。
三维渲染将渲染的核心代码独立,则用户在上次则专注于业务层,最终完成RO后调用渲染引擎完成即可,降低了理解的复杂度。
这一部分在本章里面不做过多描述,相信今后会有很多关于这最长的一帧的描述。
2.设计模式(单例(fun dllStartPlugin)、插件、封装第三方库、观察者模式)
OGRE中的设计模式应用很老练,让各种复杂的管理变得错落有致。
n 单例模式
class Singleton
OGRE为了保证所有管理类的唯一性,都继承自单例模板类,所有的Manager并不以全局变量的方式在程序启动前加载,而是作为Root的成员,在Root初始化时构建,单例类的实现也比较简单,但还是比较实用的,其实,最关键的一句个人认为是:ms_Singleton = static_cast< T* >( this );获取派生类的指针,进行上行转换获取管理类的唯一指针
n 插件机制
这个和我们的技术实现细节完全一致,但提供Plugin基类,方便插件的开发
n 封装
和我们类似,对第三方库的使用,OGRE封装了一个中间层管理,避免第三方库和本身的过多的交涉。比如zip压缩文件的读取。
n 观察者模式(监听)
在消息传递中,OGRE采用监听的方式,比如提供FrameListener类来处理消息事件,每次消息传递中,对关注的听众都进行传递。这种观察者模式的技术实现不难,却可以很灵活的解决很多棘手的问题。
在我们的类库中,其实也有必要有一个监听的基类,在需要时可以灵活解决问题。比如在传统二维显示中,都是在显示的框架中,驱动读取数据,然后绘制,显示到界面的线形顺序,而在读取网络数据时这样的用户效果则非常差了,一般需要以数据驱动显示,下载完一部分数据则驱动显示更新,由于框架依赖关系和已有代码的限制,数据层无法直接联系到显示层,通过观察者模式,则可以在每次下载完后对听众发送消息,驱动更新,则非常方便的处理这种使用场景。
观察者模式,在框架复杂的情况下,可以很好的降低耦合度,提供框架灵活性。
3.资源管理框架(配置文件、插件、cfg、文件系统)
OGRE的配置文件非常多,不论是系统级别的(日志、插件资源、显示效果)还是资源级别的(Overlay、zip、图片),这都减少了不必要接口的添加,方便用户的使用,毕竟,在游戏中,一些显示效果,装备等都可以在资源文件中打包,无需在程序中进行设置,点到为止吧,不多写了,有点写烦了,呵呵。
l 游戏完整性
纵观OGRE的范例,采用插件方式集成案例,整个界面都以Overlay作为元素展现,借助OGRE的实时渲染引擎,达到了一个非常精致的显示效果,典型的游戏画面,另外,也很好的考虑了和外部设备的交互,一个基础的游戏框架在OGRE中很快捷的搭建完成。
l 设计模式优雅
OGRE不仅仅实现了3D引擎的渲染功能,也包含了很多其他功能,同时借助了很多开源库,也满足使用人员的扩展,这些能够糅合在一起,难度非常大,能将这些缤纷的功能和模块有序的整合在一起,对开发人员的设计模式有不小的考验,何时使用合适的设计模式达到适度的效果,这需要很好的把握,太粗犷或精致的考虑都有可能导致不适用和易用性的考验。
OGRE也是一款非常悠久的开源库,采用插件的定制化方式,随着新技术和新的设计模式的开展,也是不断演化,本身能够有如此丰富的扩充性,也是非常难得的,这体现了很多管理类的设计的成熟和优雅。
l 其他
OGRE有一个不错的地方就是将所做的一切百分百的完成,很多工作尽量以扩展插件的方式完成。比如管理类,都有单例来进行控制。插件机制有了,但有基类来提供规范,方便用户理解和扩展。配置文件也定义的很清晰。
OGRE的扩展机制也很灵活,不会出现那种要么全要要不什么都没有的选择,整合许多优秀的元素(第三方库、物理引擎、影音套件),借助整合更好的计划你的方案,而不是被不喜欢的拖累着走。
诚然,OGRE本身还是有一定难度的,如果没有丰富的编程经验和技能,阅读起来肯定还是有不少难度的,这也体现了OGRE开发者的能力和活力。“我們非常小心地挑選我們的團隊成員,程式技巧是最重要的能力,可以跟別人好好溝通,對於討論持著開放得心胸,可以與目前的團隊成員在設計和撰寫程式方面保持一致得概念並且可以獨立工作,這些都是必需的。”这是OGRE招募开发者的标准,而核心团队会控制在5人以下,这也很好的保证了OGRE库的功能,设计和品质。而作为开源代码,很多都是在工作之外的创作,这种发自自身的动力也是非常钦佩。
这是一个精彩的世界,无论是C、C++还有Java,还是一些新型语言Python、Silverlight、XNA,或者Go等还没有普及的语言,无论是传统的Windows,还是移动或者各种嵌入式终端和机器人,各种技术和领域都充斥着人类的生活和工作领域,多少有点乱花渐欲迷人眼的味道,想学的有很多,在这样一个浮躁的世界里,会有很多急于求成的人和事情,希望会有更多的人能够深入研究各自领域的知识,让自身获取更宝贵的财富和经验。
无论你关注底层技术还是前台应用,无论你希望成为管理者还是所谓的码农,如果因为外界而有所动摇,只能证明你对成功没有足够的渴望,记住,最宝贵的东西都流淌在你的血液中,不会因为这些称谓而改变。
http://blog.csdn.net/pizi0475/article/details/7795804