本文内容主要参考于页面
http://www.ogre3d.org/tikiwiki/tiki-index.php?page=Ogre+Wiki+Tutorial+Framework
Ogre是一个非常好的开源面向对象的3D引擎,架构合理清晰,源代码总体来说(相对于这么大的项目而言)相当工整,逻辑几乎可算一目了然。很值得学习 3D图形学的同学研究。
作为初入手,应该抓住主要矛盾,对Ogre的框架有一个把握。因此,抛却旁枝,这里只讨论Ogre运行所需要的最小最精炼的步骤。这里将我的学习心得记录如下:
Ogre中必需的类
1 Ogre::Root,这个类是一切的起源。因此,一个Ogre程序,第一件要做的事就应该是new一个Root,并将其保存下来。
创建者: 无,直接new
作用:
Ogre的主干,渲染流程所在的类。但是它并不做具体的事情,它只是定义了抽象的渲染流程。负责调度framelistener。它负责加载所有插件。Ogre中具体的事情都由插件做,诸如场景管理,渲染器等等。而这些散落在各处的具体工作执行者,就由Ogre::Root来创建和组织。
创建之前的条件:
无任何条件
2 Ogre::RenderWindow,这个类代表了被渲染的窗口
创建者:Ogre::Root
创建方式:
Ogre::Root::initialize(true,..);或者Ogre::Root::createRenderWindow
作用:
还用说么?没它你看个蔡国庆。
创建之前的条件:
必须调用了Ogre::Root::initialize,这个函数初始化了渲染器,没渲染器也就没法创建渲染窗口
3 Ogre::SceneManager,这个类代表了场景管理器
创建者:Ogre::Root
创建方式:Ogre::Root::createSceneManager
作用:
通过组织一系列的Ogre::Node,组成了场景,Node中挂着各种物体,从而让3D世界构造起来。
同时,场景中的物体,也通过它来创建。作为生产者,它和Root的不同在于Root创建更底层的类,它则创建鲜活的对象类
4 Ogre::Camera,这个类代表了摄像机
创建者:Ogre::SceneManager
创建方式:Ogre::SceneManager::createCamera
作用:
3D世界的视图矩阵和投影矩阵,有了它,鲜活的3D世界才可以转变成屏幕坐标从而才能显示到屏幕上。
5 Ogre::Viewport,视口,这个类代表游戏窗口中的一个区域,这个区域用来显示渲染结果
创建者:Ogre::RenderWindow
创建方式:Ogre::RenderWindow::addViewport
作用:
定义屏幕上一块区域用于显示某个摄像机的投影和裁剪结果。因此,创建函数中应该传入一个摄像机。
有了这些,Ogre世界将运转起来。
但是仅仅是展现还不够,我们还需要输入进行用户交互。于是我们必须初始化OIS输入设备库。
1 OIS::InputManager OIS的基础类,必须第一个创建,具体的输入设备则由其来创建管理
创建者:无,直接通过类的createInputSystem静态函数创建。创建时需要填写ParamList,ParamList中最重要的是"WINDOW"值,这个值只要把窗口句柄(以Windows为例是HWND,对Ogre,我们可以通过对RenderWindow调用getCustomAttribute方法取得名为"WINDOW"的值,这个值类型为size_t,从而达到跨平台解决)通过%d格式化到一个字符串里即可。这样OIS就和该窗口绑定起来。
创建方式:OIS::InputManager::createInputSystem
作用:
OIS的基础
2 OIS::Keyboard, OIS::Mouse等各种输入输出设备
创建者:OIS::InputManager
创建方式:OIS::InputManager::createInputObject
作用:
具体输入对象的代理,创建以后,即可以向其注册listener来侦听事件
创建了以后还不算完事,必须在游戏大循环的合适位置进行事件分发才行,这个步骤通过在合适的位置调用输入输出设备OIS::Keyboard或OIS::Mouse等的capture方法完成。对Ogre而言,最合适的是放在frameListener的frameRenderingQueued中最合适。
Ogre程序所必须的Listener。
Ogre和OIS输入设备都有其固定的流程,因此我们必须实现它们的listener,从而把我们的处理逻辑插入到它们的固定逻辑中。最小的Ogre程序,应当有如下Listener
1 Ogre::FrameListener
身份:在帧循环的特定时候被调用
推荐重写的函数:
frameRenderingQueued。可以把这个函数看成游戏中的tick,这个函数在cpu向gpu发送了绘图指令以后被调用,此时gpu处于全速运行状态,cpu执行这个函数能够达到cpu和gpu并行工作,提高工作效率的效果。这个函数由于在渲染之后调用,它执行的结果会体现在下一帧,这一般不会对游戏构成影响。
2 Ogre::WindowEventListener
身份:侦听游戏窗口的事件,如Resize move之类
推荐重写的函数:
windowResized。这个函数当窗口Resize时调用,在这里一定要做一件事,设置OIS中Mouse的裁剪区域.
unsigned int width, height, depth; int left, top; window->getMetrics(width, height, depth, left, top); const OIS::MouseState& ms = mouse->getMouseState(); ms.width = width; ms.height = height;
windowClosed。这个函数中销毁OIS对象。
if (window == w) { if (inputManager) { inputManager->destroyInputObject(keyboard); inputManager->destroyInputObject(mouse); } OIS::InputManager::destroyInputSystem(inputManager); }
3 OIS::MouseListener OIS::KeyListener
这两个就没什么好说了,按需编写
最后,总结一下最小ogre程序的流程:
1 创建Ogre::Root
2 用Ogre::Root加载插件,必须载入的是场景管理器和渲染器
3 调用Ogre::ResourceGroupManager::getSingleton().addResourceLocation来设置资源搜索路径(Ogre的资源管理很智能,不需要自己指定路径,只需要给它一个搜索路径,然后所有material等脚本和资源文件按名字会被载入到ResourceManager中(但只对资源来说只是存了一个引用,并没有实际的载入),就可以通过名字来访问。
4 创建渲染窗口,这里我们经常这么写
if (root->showConfigDialog()) { root->initialise(false); window = root->createRenderWindow("Window", 800, 600, false); } else { return false; }
这样一来会弹一个Ogre的配置窗口,然后还会把配置保存到文件中,配置文件名在创建Root的时候给出,如果不给出,会用默认的名字ogre.cfg。
也可以先来一句root->restoreConfig从配置文件中读取配置,如果返回true表示读取成功,就不需要下一步了。
5 创建场景管理器,顺便设置场景环境光
6 创建摄像机
7 创建视口
8 初始化OIS
9 调用root->startRendering();进入游戏大循环。这个时候游戏进入了这个函数就再也不会出来了,直到窗口销毁。
Ogre的销毁
如何销毁Ogre?非常非常简单,只要一句话
delete root;
所有的工作都在Ogre::Root析构函数中做了。果然省事!
具体代码就不贴了,顶部的链接是ogre官方的,里面有一切,而且这个链接应该永远不会失效。