OGRE启动过程详解(OGRE HelloWorld程序原理解析)

本文介绍 OGRE 3D 1.9 程序的启动过程,即从程序启动到3D图形呈现,背后有哪些OGRE相关的代码被执行。会涉及的OGRE类包括:

  1. Root
  2. RenderSystem
  3. RenderWindow
  4. ResourceGroupManager
  5. LogManager
  6. Viewport
  7. SceneManager
  8. Camera
  9. SceneNode
  10. Entity
  11. Light

建议在阅读本文时参考OGRE API Reference,OGRE官方给的API Reference没有类的协作图,可以自己用Doxygen生成API文档,见:Bullet的学习资源(用Doxygen生成API文档)

关于如何安装OGRE和如何配置一个可以运行的OGRE HelloWorld程序见:OGRE 1.9 的第一个程序(OGRE HelloWorld程序)

本节所有代码如下,可以先迅速浏览,然后看后面详细解释,后面将用“启动代码”来指代这段代码:

  1 #include<OgreRoot.h>
  2 #include<OgreRenderSystem.h>
  3 #include<OgreRenderWindow.h>
  4 #include<OgreConfigFile.h>
  5 #include<OgreResourceGroupManager.h>
  6 #include<OgreLogManager.h>
  7 #include<OgreViewport.h>
  8 #include<OgreSceneManager.h>
  9 #include<OgreCamera.h>
 10 #include<OgreLight.h>
 11 #include<OgreEntity.h>
 12
 13 int main(int argc, char *argv[])
 14 {
 15     Ogre::Root* mRoot;
 16     Ogre::RenderWindow* mWindow;
 17     Ogre::SceneManager* mSceneMgr;
 18     Ogre::Camera* mCamera;
 19
 20 // 创建Root,在调用OGRE任何功能之前必须已经创建了Root
 21     mRoot = new Ogre::Root("plugins.cfg","ogre.cfg","Ogre.log");
 22
 23 // 设定 RenderSystem
 24     Ogre::RenderSystem *rs =
 25         mRoot->getRenderSystemByName("OpenGL Rendering Subsystem");
 26     mRoot->setRenderSystem(rs);
 27     rs->setConfigOption("Full Screen", "No");
 28     rs->setConfigOption("Video Mode", "800x600 @ 32-bit colour");
 29     // 另一种方法是: if(!mRoot->showConfigDialog()) return false;
 30
 31 // 初始化 RenderSystem
 32     mRoot->initialise(false);
 33
 34 // 创建 RenderWindow
 35     int hWnd = 0;
 36     Ogre::NameValuePairList misc;
 37     misc["externalWindowHandle"] = Ogre::StringConverter::toString((int)hWnd);
 38     mWindow = mRoot->createRenderWindow("Win Ogre", 800, 600, false, &misc);
 39     // 上2步的另一种实现是: mWindow = mRoot->initialise(true, "Win Ogre");
 40
 41 // 创建SceneManager,将渲染目标绑定到RenderWindow
 42     mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);
 43     // Create one camera
 44     mCamera = mSceneMgr->createCamera("PlayerCam");
 45     mCamera->setNearClipDistance(5);
 46     // Create one viewport, entire window
 47     Ogre::Viewport* vp = mWindow->addViewport(mCamera);
 48     vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
 49     // Alter the camera aspect ratio to match the viewport
 50     mCamera->setAspectRatio(
 51         Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));
 52
 53 // 加载资源,该歩不能早于RenderSystem的初始化和RenderWindow的创建
 54     // 如果使用OverlaySystem,该歩也不能早于OverlaySystem的创建
 55     Ogre::ConfigFile cf; cf.load("resources.cfg");
 56     Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
 57     Ogre::String secName, typeName, archName;
 58     while( seci.hasMoreElements() ){
 59         secName = seci.peekNextKey();
 60         Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
 61         Ogre::ConfigFile::SettingsMultiMap::iterator i;
 62         for( i=settings->begin(); i!=settings->end(); ++i ){
 63             typeName = i->first;
 64             archName = i->second;
 65             Ogre::ResourceGroupManager::getSingleton().
 66                 addResourceLocation(archName, typeName, secName);
 67         }
 68     }
 69     Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
 70
 71 // 构造及设置场景
 72     mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
 73     mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f));
 74
 75     Ogre::Entity* entNinja = mSceneMgr->createEntity("entNinja", "ninja.mesh");
 76     Ogre::SceneNode* nodeNinja = mSceneMgr->createSceneNode("nodeNinja");
 77     mSceneMgr->getRootSceneNode()->addChild(nodeNinja);
 78     nodeNinja->attachObject(entNinja);
 79     Ogre::Entity* entSphere = mSceneMgr->createEntity("entSphere", "sphere.mesh");
 80     Ogre::SceneNode* nodeSphere = mSceneMgr->createSceneNode("nodeSphere");
 81     mSceneMgr->getRootSceneNode()->addChild(nodeSphere);
 82     nodeSphere->attachObject(entSphere);
 83     nodeNinja->setPosition(-50,-100,0);
 84     nodeSphere->translate(50,0,100);
 85     Ogre::Light* pointLight1 = mSceneMgr->createLight("pointLight1");
 86     pointLight1->setType(Ogre::Light::LT_POINT);
 87     pointLight1->setDiffuseColour(Ogre::ColourValue::White);
 88     pointLight1->setSpecularColour(Ogre::ColourValue::White);
 89     pointLight1->setPosition(-400,200,-200);
 90
 91     mCamera->setPosition(Ogre::Vector3(0,0,-250));
 92     mCamera->lookAt(Ogre::Vector3(0,0,0));
 93
 94 // 渲染循环
 95     Ogre::LogManager::getSingleton().logMessage(">>Rendering");
 96     mRoot->startRendering();
 97
 98 // 释放资源,目前只需释放Root
 99     delete mRoot;
100
101     return 0;
102 }

运行结果截图:

1. 启动过程概览

我们概要地看OGRE的启动,OGRE WIKI Basic Tutorial 6: The Ogre Startup Sequence中摘出下面这段,注意它和上面的代码(“启动代码”)是有差别的,各步骤的顺序不同:

The basic Ogre life cycle looks like this:

  1. Create the Root object.
  2. Define the resources that Ogre will use.
  3. Choose and set up the RenderSystem (that is, DirectX, OpenGL, etc).
  4. Create the RenderWindow (the window which Ogre resides in).
  5. Initialise the resources that you are going to use.
  6. Create a scene using those resources.
  7. Set up any third party libraries and plugins.
  8. Create any number of frame listeners.
  9. Start the render loop.

总的来说,先是初始化,最后启动渲染循环。我将所有这些类的关系总结如下图(不是什么UML图,就大致理解吧):

看完后面的详细解释后可以回过头来看这段,那时你就会对OGRE的启动有个大致印象。

2. 创建Root

在调用OGRE任何功能之前,首先要实例化一个Root类,该Root实例将直接或间接指向所有其他类的实例。一个OGRE程序有且只有一个Root对象,因此Root类使用Singleton设计模式(单例模式,继承自Singleton<Root>)。说到Singleton,OGRE的很多类都是Singleton,后面还会讲的。

Root类的构造函数原型如下:

Root (const String &pluginFileName="plugins"OGRE_BUILD_SUFFIX".cfg",
    const String &configFileName="ogre.cfg", const String &logFileName="Ogre.log")

其中OGRE_BUILD_SUFFIX宏在Release下定义为空,Debug下定义为"_d"。三个参数是三个文件名。

pluginFileName是插件配置文件,该文件指示OGRE要加载哪些插件,一个plugins.cfg文件的例子如下,其中#表示注释

# Defines plugins to load
# Define plugin folder
PluginFolder=.
# Define plugins
# Plugin=RenderSystem_Direct3D9
 Plugin=RenderSystem_GL
 Plugin=Plugin_ParticleFX
 Plugin=Plugin_BSPSceneManager
 Plugin=Plugin_CgProgramManager
 Plugin=Plugin_PCZSceneManager
 Plugin=Plugin_OctreeZone
 Plugin=Plugin_OctreeSceneManager

configFileName文件设置渲染系统(OpenGL或者Direct3D)及其参数,如抗锯齿采样数(FSAA),一个针对OpenGL驱动的配置文件ogre.cfg例子如下:

Render System=OpenGL Rendering Subsystem
[OpenGL Rendering Subsystem]
Colour Depth=32
Display Frequency=N/A
FSAA=8
Fixed Pipeline Enabled=Yes
Full Screen=No
RTT Preferred Mode=FBO
VSync=No
VSync Interval=1
Video Mode=1024 x 768
sRGB Gamma Conversion=No

logFileName文件是OGRE程序的日志文件,在OGRE程序可以插入写日志的代码,日志文件方便对OGRE程序的调试。向日志文件写入信息的代码的一个例子如下:

Ogre::LogManager::getSingleton().logMessage(">>Rendering");

这里的LogManager是另一个使用Singleton设计模式的类,这种类使用静态方法getSingleton获取全局唯一的类实例。

“启动代码”中创建Root对象的代码在第21行:

mRoot = new Ogre::Root("plugins.cfg","ogre.cfg","Ogre.log");

3. 设定RenderSystem,初始化

RenderSystem类对渲染系统(底层的OpenGL或Direct3D)进行抽象,它相当于是执行渲染的设备。给 Root 添加一个RenderSystem实例的最简单方式是调用Ogre::Root:: showConfigDialog方法,运行时系统将弹出如下对话框,让用户选择要使用的图形驱动,以及相应的参数:

if(!mRoot->showConfigDialog()) return false;

我们在这个对话框所做的设置被记录在ogre.cfg文件中(见上面第2节)。也可以不用对话框,而在程序中设置,也就是说在程序中设置我们在对话框所选的项:

Ogre::RenderSystem *rs = mRoot->getRenderSystemByName("OpenGL Rendering Subsystem");
mRoot->setRenderSystem(rs);
rs->setConfigOption("Full Screen", "No");
rs->setConfigOption("Video Mode", "800x600 @ 32-bit colour");

“启动代码”使用的是后者,代码在第24-27行。

如果不想每次都弹出对话框选择渲染系统,可以用如下代码:

if( !(mRoot->restoreConfig() || mRoot->showConfigDialog()) )
    return false;

restoreConfig方法读入ogre.cfg文件来代替对话框设置,还记得C/C++逻辑运算表达式求值的短路性质吧,如果mRoot->restoreConfig()返回true(存在ogre.cfg文件),mRoot->showConfigDialog()将不被执行。

RenderSystem对象创建后需要初始化,Ogre::Root::initialise(bool, const String, const String)方法就是初始化root的RenderSystem的,如果第一个bool参数为true,将自动创建窗口,“启动代码”没有这样做,在第31行:

mRoot->initialise(false);

另外还要说的是,OGRE作为一个跨平台的高层3D图形库,对图形系统进行了高度抽象,这种抽象使用户不需要关心底层技术(如OpenGL或Direct3D、win32或Xwindow),但程序的执行必然会用到底层功能(如具体渲染任务必然是OpenGL或Direct3D执行)。OGRE(或者其他很多开源程序库)是这样做到这一点的:用户使用基类(如RenderSystem和RenderWindow)接口和OGRE进行交互,代码执行时程序自动根据系统配置调用相应子类的实现来执行命令(这得益于面向对象的继承性和多态性)。RenderSystem类的继承图如下:

对于我们,使用的是OpenGL图形驱动,所以到程序执行时,实际使用的是GLRenderSystem的实现。其实RenderSystem压根就是个抽象类,不能被实例化。

4. 创建 RenderWindow

RenderWindow是对窗口的抽象,该窗口用来显示渲染结果(对于离线渲染或渲染到纹理则不需要窗口)。创建窗口最简单的方法是在调用Ogre::Root::initialise方法时传入true作为第一个参数:

mWindow = mRoot->initialise(true, "Win Ogre");

但“启动代码”为了代码的清晰,使用了手动创建RenderWindow的方法:

int hWnd = 0;
Ogre::NameValuePairList misc;
misc["externalWindowHandle"] = Ogre::StringConverter::toString((int)hWnd);
mWindow = mRoot->createRenderWindow("Win Ogre", 800, 600, false, &misc);

注意上面使用的NameValuePairList类是用来构造参数的,你可能发现了,OGRE的很多参数都使用string数据类型。

5. 创建SceneManager,将渲染目标绑定到RenderWindow

SceneManager类管理OGRE的场景图形(Scene Graph),《Ogre 3D 1.7 Beginner‘s Guide》的Chapter 6中将SceneManager的功能总结为两个方面:

  1. 管理Camera, SceneNode, Entity, Light等场景中的对象,作为Factory提供create方法如createEntity(), createLight()(也负责释放它们);
  2. 管理场景图形,包括维护场景树的可用性,计算节点的Transform信息,隐藏面剔除(Culling)。

SceneManager不是Singleton,可以从Root创建一个(或多个)SceneManager,“启动代码”的第41行创建了一个普通类型的SceneManager:

mSceneMgr = mRoot->createSceneManager(Ogre::ST_GENERIC);

有了SceneManager,就可以从这个Factory创建场景的对象了,主要是Camera, SceneNode, Entity, Light,构建场景(构建场景树)留到后面说,这里说Camera和RenderWindow的对应关系。

Camera是场景到窗口输出的媒介,负责将3D场景映射到2D窗口,这个映射涉及到另一个类Viewport,Viewport将Camera对场景的“拍摄”结果“贴”到窗口的全部可绘制区域的一个矩形部分。一个Root可以有多个SceneManager,一个SceneManager中也可以有多个Camera,但每个Camera都需要一个Viewport对应到窗口的一个矩形区域。现在你应该知道怎样把一个场景的不同视角,或者多个场景绘制到一个窗口的不同区域了吧。

“启动代码”中创建Camera的代码在第43行:

mCamera = mSceneMgr->createCamera("PlayerCam");
mCamera->setNearClipDistance(5);

其中也设置了Camera的近裁剪面。“启动代码”中创建建Viewport的代码在随后的第46行:

// Create one viewport, entire window
Ogre::Viewport* vp = mWindow->addViewport(mCamera);
vp->setBackgroundColour(Ogre::ColourValue(0,0,0));
// Alter the camera aspect ratio to match the viewport
mCamera->setAspectRatio(
    Ogre::Real(vp->getActualWidth()) / Ogre::Real(vp->getActualHeight()));

其中也设置了Viewport背景颜色和Camera长宽比例,addViewport是RenderWindow的方法,并以Camera为参数,这样就把RenderWindow和Camera联系起来了,正如我们所分析的。另外addViewport方法还有其他参数用来指定Viewport在窗口的哪一区域,上述代码使用了缺省参数,即将Viewport对应到整个窗口。

同第4节最后说的,RenderWindow是抽象类,具体的和窗口相关的功能实际是由子类实现的,在windows上,这个子类是Win32Window。

6. 加载资源

OGRE的资源文件是OGRE的一个特色,最常见的资源文件莫过于Mesh(.mesh)和Material(.material)了,注意Mesh是一个可渲染物体,而不仅仅是一个网格,Material定义可渲染物体除几何信息外的其他所有属性,可以是而不限于颜色、纹理、着色器什么的。

资源文件的一个好处就是当修改了物体的外观等信息之后,不需要重新编译程序,如果将物体的顶点数据什么的写在代码里那就当然要重新编译啦。资源文件的缺点,程序在启动时要对资源文件进行解析(分析脚本),这增加了程序启动时间,这也是HelloWorld程序需要好几秒之后才能看见图形的原因。另一个缺点,对于初学者来说,最初可能就是想画一个长方体,但在OGRE里,你就需要创建Mesh资源。当然啦,OGRE作为面向而不限于3D游戏的3D引擎,强大的资源管理能力可以大大提高开发效率,应当说,正是资源文件的庞杂换来了程序代码的简洁。

有关OGRE对资源文件处理的细节见:Resources and ResourceManagers,要使用一个程序外定义(即脚本定义)的资源,需要:

  1. 用ResourceGroupManager::addResourceLocation方法添加资源文件所在目录;
  2. 用ResourceGroupManager::declareResource方法声明(declare)资源,可选的;
  3. 用ResourceGroupManager::initialiseResourceGroup或ResourceGroupManager::initialiseAllResourceGroups方法初始化所添加目录中的资源文件脚本;
  4. 默认下,资源文件的数据直到该资源使用时才被加载,如一个纹理的图片并不是在纹理定义时加载,而是在纹理被首次使用时加载至内存,也可以手动调用ResourceGroupManager::loadResourceGroup加载。

上面的第一步在“启动代码”中对应代码如下,位于第54-67行:

Ogre::ConfigFile cf; cf.load("resources.cfg");
Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();
Ogre::String secName, typeName, archName;
while( seci.hasMoreElements() ){
    secName = seci.peekNextKey();
    Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
    Ogre::ConfigFile::SettingsMultiMap::iterator i;
    for( i=settings->begin(); i!=settings->end(); ++i ){
        typeName = i->first;
        archName = i->second;
        Ogre::ResourceGroupManager::getSingleton().
            addResourceLocation(archName, typeName, secName);
    }
}

其中"resources.cfg"是资源文件名字,文件内容如下(为了简洁,删减了一些):

# Resources required by the sample browser and most samples.
[Essential]
Zip=../../media/packs/SdkTrays.zip
Zip=../../media/packs/profiler.zip
FileSystem=../../media/thumbnails

# Common sample resources needed by many of the samples.
# Rarely used resources should be separately loaded by the
# samples which require them.
[Popular]
FileSystem=../../media/fonts
FileSystem=../../media/models
Zip=../../media/packs/cubemap.zip
Zip=../../media/packs/cubemapsJS.zip

[General]
FileSystem=../../media

# Materials for visual tests
[Tests]
FileSystem=../../media/../../Tests/Media

Ogre::ConfigFile是一个资源配置文件解析的辅助类,类似于XML解析,和代码对应,Essential、Popular、Popular是secName,这是OGRE为方便对资源进行管理而分的组,每个settings的格式为:typeName= archName(参数类型=参数值)。

注意下面这句代码:

Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);

OGRE有很多xxxManager类,它们负责管理特定事物,如ResourceGroupManager提供对资源组的操作,LogManager提供写日志文件功能,SceneManager管理场景图形等等。这些Manager中的很多,比如LogManager和ResourceGroupManager使用Singleton设计模式,可以调用静态方法getSingleton获取全局唯一的实例。但SceneManager不是单例模式的,因为一个Root可以有多个场景图形(场景树)。

“启动代码”中declare资源的代码如下,在第68行:

Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

这里采取的是简单粗暴的方式,解析资源目录的所有脚本,怪不得程序启动后要等那么久了。

注意declare资源有时不能进行的太早,例如,不能早于RenderSystem的初始化和RenderWindow的创建,如果使用OverlaySystem,也不能早于OverlaySystem的创建。原因同第3节最后分析的,因为资源的解析具体是由子类实现的,在没有确定使用的是RenderSystem和RenderWindow的哪个子类前,不能确定使用哪个解析资源的子类,例如,RenderSystem的不同子类GLRenderSystem或D3D9RenderSystem,使用的纹理解析的类不同,如下图:

    截止目前你应该了解plugins.cfg、ogre.cfg、Ogre.log、resources.cfg文件的作用了吧。

7. 构造场景树

目前大多数3D图形库采用了场景图形(Scene Graph)技术,即用场景树来组织场景的所有物体,场景树的节点可分为两种:分支节点和叶节点。分支节点SceneNode(继承自Node)主要负责空间位置变化,叶节点可以为:Entity(可绘制实体),Light,Camera等。关于场景树,最需要了解的是,叶节点对象在世界坐标中的最终位置是由父节点级联决定的。一个典型的场景树如下图:

Entity3的世界坐标由Node5、Node4、Node2联合决定(世界坐标计算方式可以修改)。每个Node都有一些空间变换方法:setPosition、setOrientation、setScale、translate、rotate、scale,其中前三个是覆盖式修改,后三个是增量式修改。用Ogre::Node::addChild方法连接两个Node,用Ogre::SceneNode::attachObject方法连接Node和叶节点。上图有一个容易混淆的地方:Light1是不是只作用于Node4子树呢,答案是否定的,Light1作用于整个场景树,Camera1也是类似的。Light和Camera总是作用于整个场景树,其上级Node只起到对其世界坐标进行变换的作用。

注意,一个可用的场景树不能有循环路径,如下图的场景树,OGRE程序运行时会抛出异常:

可以调用Ogre::SceneManager::setShadowTechnique方法设置阴影,Ogre::SceneManager::setSkyBox方法设置天空,Ogre::SceneManager::setFog方法设置雾效果。

“启动代码”构建了一个简单的场景,代码在第71-91行:

mSceneMgr->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);
mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f));

Ogre::Entity* entNinja = mSceneMgr->createEntity("entNinja", "ninja.mesh");
Ogre::SceneNode* nodeNinja = mSceneMgr->createSceneNode("nodeNinja");
mSceneMgr->getRootSceneNode()->addChild(nodeNinja);
nodeNinja->attachObject(entNinja);
Ogre::Entity* entSphere = mSceneMgr->createEntity("entSphere", "sphere.mesh");
Ogre::SceneNode* nodeSphere = mSceneMgr->createSceneNode("nodeSphere");
mSceneMgr->getRootSceneNode()->addChild(nodeSphere);
nodeSphere->attachObject(entSphere);
nodeNinja->setPosition(-50,-100,0);
nodeSphere->translate(50,0,100);
Ogre::Light* pointLight1 = mSceneMgr->createLight("pointLight1");
pointLight1->setType(Ogre::Light::LT_POINT);
pointLight1->setDiffuseColour(Ogre::ColourValue::White);
pointLight1->setSpecularColour(Ogre::ColourValue::White);
pointLight1->setPosition(-400,200,-200);

mCamera->setPosition(Ogre::Vector3(0,0,-250));
mCamera->lookAt(Ogre::Vector3(0,0,0));

8. 渲染循环

调用Root::startRendering方法进入渲染循环,渲染结束释放Root:

Ogre::LogManager::getSingleton().logMessage(">>Rendering");
mRoot->startRendering();

// 释放资源,目前只需释放Root
delete mRoot;

其中使用LogManager这个Singleton类的功能向日志文件中写入了信息。

startRendering函数实现如下:

void Root::startRendering(void)
{
    assert(mActiveRenderer != 0);

    mActiveRenderer->_initRenderTargets();

    // Clear event times
    clearEventTimes();

    // Infinite loop, until broken out of by frame listeners
    // or break out by calling queueEndRendering()
    mQueuedEnd = false;

    while( !mQueuedEnd )
    {
        //Pump messages in all registered RenderWindow windows
        WindowEventUtilities::messagePump();

        if (!renderOneFrame())
            break;
    }
}

Root::renderOneFrame方法代码如下:

bool Root::renderOneFrame(void)
{
    if(!_fireFrameStarted())
        return false;

    if(!_updateAllRenderTargets())
        return false;

    return _fireFrameEnded();
}

也可以自行构造渲染循环,这样就可以解决“启动代码”点击关闭窗口程序也不退出的问题了:

while(true)
{
    // Pump window messages for nice behaviour
    Ogre::WindowEventUtilities::messagePump();

    if(mWindow->isClosed())
    {
        return false;
    }

    // Render a frame
    if(!mRoot->renderOneFrame()) return false;
}

9. 总结

本文要点总结如下:

  1. OGRE程序总是从创建Root实例开始;
  2. OGRE的很多xxxManager类使用了Singleton设计模式,可以调用类的静态方法getSingleton来获取全局唯一的类实例,如:ResourceGroupManager、LogManager、TextureManager、MeshManager等等,但SceneManager不是;
  3. OGRE对图形系统进行了高度抽象,用户使用基类接口和OGRE交互,程序执行时会自动根据系统配置调用特定子类的实现,如RenderSystem和RenderWindow;
  4. OGRE的场景数据用场景图形(Scene Graph)来组织,其本质是树(Tree),由SceneManager来组织和管理;
  5. 每个Camera通过一个Viewport映射到窗口的一个矩形部分(当然也可以渲染到纹理);
  6. OGRE的资源文件是其一大特色,资源需要特定程序加载到执行期间的程序;
  7. OGRE采用配置文件,本文涉及的有plugins.cfg、ogre.cfg、Ogre.log、resources.cfg文件,你应该清楚它们的作用了;
  8. 场景树的可用性要求场景树不能有循环。

好了,关于OGRE的基本启动过程你应该了解的吧,本文并没涉及WindowEventListener、FrameListener等一些事件的处理,也没有涉及鼠标键盘输入,甚至,“启动代码”运行起来后关闭窗口都不能结束程序,这些留到以后再讲吧。

时间: 2024-10-12 12:03:23

OGRE启动过程详解(OGRE HelloWorld程序原理解析)的相关文章

计算机启动过程详解

计算机启动过程详解打开电源启动机器几乎是电脑爱好者每天必做的事情,面对屏幕上出现的一幅幅启动画面,我们一点儿也不会感到陌生,但是,计算机在显示这些启动画面时都做了些什么工作呢?相信有的朋友还不是很清楚,本文就来介绍一下从打开电源到出现Windows的蓝天白云时,计算机到底都干了些什么事情.  首先让我们来了解一些基本概念.第一个是大家非常熟悉的BIOS(基本输入输出系统),BIOS是直接与硬件打交道的底层代码,它为操作系统提供了控制硬件设备的基本功能.BIOS包括有系统BIOS(即常说的主板BI

Linux(RHEL6)启动过程详解

Linux(红帽RHEL6)启动过程详解: RHEL的一个重要和强大的方面是它是开源的,并且系统的启动过程是用户可配置的.用户可以自由的配置启动过程的许多方面,包括可以指定启动时运行的程序.同样的,系统关机时所要终止的进程也是可以进行组织和配置的,即使这个过程的自定义很少被需要. 理解系统的启动和关机过程是如何实现的不仅可以允许自定义,而且也可以更容易的处理与系统的启动或者关机相关的故障.  1.启动过程  以下是启动过程的几个基本阶段:   ① 系统加载并允许boot loader.此过程的细

VxWorks启动过程详解(下)

上一节主要是从映像的分类和各种映像的大致加载流程上看VxWorks的启动过程,这一节让我们从函数级看一下VxWorks的启动过程: 1. Boot Image + Loadable Images: 下面是具体的流程图: 其中第一阶段的执行流程使用的是上图的左边的源文件中的那些函数(romInit->romStart->usrInit->sysHwinit->usrKernelinit->usrRoot);第二阶段执行流程使用的是上图中右边源文件中的那些函数(sysInit-&

VxWorks启动过程详解(上)

vxworks有三种映像: VxWorks Image的文件类型有三种 Loadable Images:由Boot-ROM引导通过网口或串口下载到RAM ROM-based Images(压缩/没有压缩):即将Image直接烧入ROM,运行时将Image拷入RAM中运行. ROM-Resident Images:Image的指令部分驻留在ROM中运行,仅将数据段部分拷入RAM. 注意这里说的三种映像都是包含真正操作系统VxWorks的映像,其中后两种可以直接启动并运行起来,但是第一种不行,它必须

solaris启动过程详解

在Sparc平台下,Solaris系统中有一个类似PC BIOS的芯片程序(EEPROM OpenBoot)负责识别分区.文 件系统和加载内核,在Solaris 2.6之后的版本中,默认的内核文件存放在/platform/`arch`/kernel/unix 位置,`arch`指令是指明系统的硬件体系,目前一般是i86pc(Intel IA32)或sun4u(Sun UntraSparc). 在Intel体系中,因为没有eeprom firmware,所以系统提供了一个模拟eeprom的引导程序

cocos2dx 启动过程详解一:渲染

今天来看一下cocos2d-x的整体启动过程: cocos2d-x 在各个平台的实现代码是一样的,只要针对不同平台做相应的配置就可以了. 一.启动前奏 现在来看一下在ios平台下的相关结构: 打开源代码自带工程,你会看到一个main文件,这里main里面有一个main函数,这是程序的入口函数.在这里他回加载AppController,进入这个类,这里有ios平台华景初始化代码,但是最先执行的如下: // cocos2d application instance static AppDelegat

cocos2dx 启动过程详解二:内存管理和回调

在上一篇的第二部分中,我们有一句代码待解释的: // Draw the Scene void CCDirector::drawScene(void) { -- //tick before glClear: issue #533 if (! m_bPaused) //暂停 { m_pScheduler->update(m_fDeltaTime);   //待会会解释这里的内容 } -- } 这里是一个update函数,经常会写像this->schedule(schedule_selector(X

Fabric网络环境启动过程详解

这篇文章对fabric的网络环境启动过程进行讲解,也就是我们上节讲到的启动测试fabric网络环境时运行network_setup.sh这个文件的执行流程 fabric网络环境启动过程详解 上一节我们讲到 fabric网络环境的启动测试,主要是使用 ./network_setup.sh up 这个命令,所以fabric网络环境启动的重点就在network_setup.sh这个文件中.接下来我们就分析一下network_setup.sh这个文件network_setup.sh其中包括两个部分,一个

转-Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)

http://blog.chinaunix.net/space.php?uid=10167808&do=blog&id=26042 1)BIOS自检2)启动Grub/Lilo3)加载内核4)执行init进程5)通过/etc/inittab文件进行初始化6)登陆Linux 1)BIOS自检   a)POST(Power On Self Test),对硬件进行检测   计算机在通电后首先由BIOS进行自检,即所谓的POST(Power On Self Test),对硬件进行检测   依据BIO