10.1 Ogre场景管理总览及场景结构体系
10.1.1 场景管理总览
在前面的章节,我们已经多次提到过场景管理器(SceneManager)的概念,它是Ogre中相当重要的概念。当我们进行Ogre程序开发的时候,场景管理器也是最经常用到的对象。场景管理器控制着场景中的所有内容,它还负责组织创建管理所有的摄像机(Camera)、可移动的实体(Entity)、光(Light)和材质(Material),并且载入和布置世界地图等。
那么,到底什么是场景呢?在三维游戏、动画或应用程序中,场景就像是一个大的容器,它用来盛放我们需要在这个三维“虚拟世界”中要显示的物体。这些物体既包括非活动物体,即一旦放入场景就不能被移动的物体,也包括一些活动物体,例如一些模型、建筑物,人物,树,椅子以及灯光和摄像机等等。另外,场景包括很多不同的类型,例如有室内场景和室外场景。在一幅室内场景中,可能有楼梯、走廊,也可能有沙发、家具和墙壁等等;室外场景可能包括高山、流水、草地,也可能包括蔚蓝的天空和飘在上面的白云等等。
为了管理这些场景,Ogre为我们提供了很多不同类型的场景管理器,不同的场景管理器用来管理不同类型的场景。场景管理器主要负责以下事情:1 创建放置在场景中的可移动对象,例如模型、灯光、摄像机等等,并可以有效地访问这些对象。2 加载世界地图,这个世界地图是不可移动的,而且它通常很大。3 场景查询,知道场景中都包含哪些对象。4 将不可见对象卸载,把可见对象放入渲染队列进行渲染。5 根据当前和渲染物体的透视图,按由近到远的顺序对无方向的光源(Nondirectional Light)进行组织和排序。6 设置并渲染场景中的所有阴影。7 设置并渲染场景中的其他对象(如背景,天空盒)。8 将组织好的内容传递到渲染系统进行渲染。
总之,Ogre场景管理器功能丰富,它可以做很多事情,它有许多函数都以create,destroy,get,set开头。例如:setScale()、setposition()函数等。其中,场景管理器最常用的功能就是用来管理场景中的对象,这些对象可以是摄像机(Camera)、可移动的实体(Entity)、光(Light)等。例如,当我想在场景中添加一台摄像机、或是删除某个光源的时候,都得经由场景管理器。
笔者注: 1.当我们需要绘制场景时,场景管理器会将场景送到渲染系统(RenderSystem)对象中去,而且,我们无需调用SceneManager::_ renderScene函数来操作这个绘制过程,场景管理器会帮助我们完成函数的调用工作,这样也就大大减少了程序的编写量,提高了程序员的编程效率。 2.场景管理器不仅仅管理场景中的对象,而且它负责对整个场景进行管理。例如计算场景中对象的具体位置、对整个场景进行优化等。 |
10.1.2 场景结构体系
Ogre中的场景管理器设计独特,它实现了场景节点和场景内容的分离。Ogre中有一个实体的概念,我们可以把场景中的3D模型都看做实体(例如,我们前几章添加进createScene函数里的OgreHead.mesh和Ninja.mesh等)。但是,实体不能直接被放置到场景中,它需要与场景节点绑定后放入场景,这样,这个实体才能够被渲染;同样,一个场景节点也不能单独的在屏幕上显示出来,只有与一个实体绑定后才能在屏幕上显示,场景节点记录了绑定在上面的实体的信息。
正如上图所示,根节点下绑定场景节点,场景节点上可以再绑定场景节点,实体绑定在场景节点上,而且,一个场景节点上可以绑定多个实体。在场景中,被渲染的对象是实体,而不是场景节点,场景节点中只包含了绑定在上面的实体的方位等信息。例如,当我们移动节点2,实体3就跟着移动;当我们移动节点1,实体1和2就跟着移动。这就好比场景节点是实体的“代言人”,对实体的移动、旋转、缩放等操作,其实是通过实体绑定着的场景节点完成的。另外,实体是场景中被渲染的对象,它包含了纹理、材质等信息,而场景节点却不必包含这些信息。这也就意味着,Ogre实现了场景节点与场景内容(即实体)的分离。
10.1.3 场景管理器的类型
首先,我们要向大家介绍一个createSceneManager()函数,因为Ogre正是使用了createSceneManager()函数来创建场景管理器,代码如下:
SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC, “MyOgreSManagemer"); |
通过这一句代码,我们创建了一个ST_GENERIC类型(它的具体含义将在下文介绍)的场景管理器,它的名字是“MyOgreSManagemer”。 createSceneManager()函数的第一个参数用来设置场景管理器的“类型”,第二个参数用来设置我们所创建的场景管理器的名称。其中,第一个参数所有取值有如下几种:
(1)ST_GENERIC:这是最简单的场景管理器的类型,这个类型的场景管理器没有对场景内容和结构做任何优化。如果一个程序的场景结构非常简单,我们才会考虑使用这个类型的场景管理器。
(2)ST_INTERIOR:这种类型的场景管理器,优化了室内近距离的渲染,比较适合高密度的场景。
(3)ST_EXTERIOR_CLOSE:这种场景管理器,优化了室外场景里面的中近距离的渲染,比较适合用一个简单模型或者高度场的场景地图。
(4)ST_EXTERIOR_FAR:在目前的Ogre版本中,这种类型的场景管理器现在已经不再使用了。在需要的时候会用ST_EXTERIOR_CLOSE和ST_EXTERIOR_REAL_FAR来代替这个选项。
(5)ST_EXTERIOR_REAL_FAR:这种类型的场景管理器特别适合那种需要动态加载内容的场景。需要动态加载的场景通常都非常大,可能要加载的内容也非常大,在这种情况下,我们一般会使用这种ST_EXTERIOR_REAL_FAR类型的场景管理器。
笔者注: 在创建场景管理器的时候可以指定上述参数值的一种或是几种,即我们创建的场景管理器可以具备上述特性的一种或几种。默认情况下,系统会选择具有ST_GENERIC特性的场景管理器。 |
除了这种用参数值来选择场景管理器类型的方法,我们还可以通过场景管理器的名字来选择场景管理器。Ogre官方一共提供了四种可供选择的场景管理器:
(1)GenericSceneManager(默认类型)
(2)OctreeSceneManager
(3)BspSceneManager
(4)Portal Connected Zone Scene Manager (PCZ)SceneManager
其中,GenericSceneManager 类型的场景管理器具有ST_GENERIC 特性;OctreeSceneManager和PCZSceneManager 具有全部的5个特性(ST_GENERIC、ST_INTERIOR、ST_EXTERIOR_CLOSE、ST_EXTERIOR_FAR、ST_EXTERIOR_REAL_FAR),BspSceneManager管理器具有ST_INTERIOR特性。
笔者注: 如果我们用第二种“名字”的方式来选择场景管理器,那么我们必须要将我们要选择的场景管理器的名字写入plugins.cfg文件中,如果我们在plugins.cfg文件中,依次写入了三个场景管理器的名字,它们是:BSP, PCZ and OctreeSceneManager,那么最终只有最后一个OctreeSceneManager会被系统选中作为我们的场景管理器。事实上,默认情况下plugins.cfg文件中的场景管理器的名称的顺序就是上述的BSP, PCZ and OctreeSceneManager ,所以OctreeSceneManager就成为了系统默认的场景管理器。 |
1. Generic Scene Manager
Generic Scene Manager是默认的场景管理系统,但是它的功能非常的简单而且缺点非常多,我们一般不使用它。
2. Octree Scene Manager
Octree Scene Manager简单、通用,并使用了八叉树(Octree)的场景结构,这种类型的场景管理器是目前使用的最多的。在前面的章节中,我们使用的例子,一直使用的场景管理类型是八叉树场景管理(OctreeSceneManager),这个场景管理器使用八叉树的方式来存储场景,为了让大家更好的理解,我们首先来了解一下什么是八叉树。
八叉树(Octree)
正如其名字表示的那样,八叉树也是一种树,它有根,每一个节点也有父(根节点除外)。它和其它树的区别在于每个节点最大可以有八个孩子,下面的图表展示了八叉树的结构:
Ogre3D使用八叉树存储3D场景是由于八叉树具有一些存储3D场景非常有用的属性,其中一个就是它能达到八个孩子。例如,如果我们有两个对象在一个3D场景中,我们能用一个立方体来装入整个这个场景。
如果我们用这立方体的一半长宽高,我们就得到了八个新的立方体,每一个就可以装入八分之一的场景。这八个立方体可以认为是最初的那个立方体的八个孩子。
现在场景中的两个对象就存在于右上角的那个立方体内,其它的七个立方体是空的,因此是叶子节点。我们现在只需再划分包含两个对象的那个立方体。
现在,每个立方体或者有一个对象,或者没有对象已经是叶子节点了。八叉树的这一属性使得“剔除”实现的很容易并且很快速,当我们渲染一个场景的时候,我们相机的视锥体就会在场景中,我们需要决定哪些对象需要被渲染,哪些不需要渲染,因此,我们从八叉树的根节点开始,如果视锥体和这个立方体相交,我们就继续,通常情况一开始都会是这样,因为这个八叉树在第0层次装入了整个的场景,我们然后继续查看八叉树的第1层,也就是根节点的孩子,我们再次用视锥体和每个孩子节点测试,如果他们相交,我们就继续测试这个立方体的孩子,但是如果这个立方体完全的在视锥体内,我们就不需要再进行深层次的测试,因为我们已经知道了这个立方体的所有孩子也都在这个视锥体内了,我们一直这样继续做,直到我们到达了叶子节点,然后我们再继续测试其它的孩子,直到我们仅仅只剩下叶子节点了,没有其它的可以测试的立方体了。
通过这个算法,我们在每一步丢弃了大部分的场景,一般情况,仅仅只需几步,我们就对场景剔除到只剩下空叶子节点了或者含有对象的叶子节点了,这样我们就会知道哪些对象是可见的,是需要渲染的,而哪些是不需要渲染的。这种方法和二叉树很类似,不同的是它用八个孩子代替了二叉树的两个孩子。
3. BSP Scene Manager
BSP(binary-space partitioning) Scene Manager(二叉空间分割)场景管理器比较适合用于室内场景。
笔者注: BSP(binary-space partitioning)二叉空间分割类型的场景管理器也是一个比较常用的场景管理类型,但是对于现在的应用程序而言,更倾向于使用BSP做空间快速碰撞检测而不是几何体分割。Ogre中为我们提供了一个简单得BSP支持,这里我们只是为了让大家多了解一种场景管理类型,实际上这里的这种场景管理类型以及很少使用了,大家可以了解即可。 |
4. Portal Connected Zone Scene Manager (PCZSM)
Portal Connected Zone Scene Manager场景管理器也非常适用于室内的场景,而且它兼容大量的第三方工具。
10.1.4 通过实例了解Ogre场景管理
实例一:
首先,我们还是使用我们最初定义的模板代码,在此基础上添加新的内容,模板代码如下:
#include <windows.h> #include "ExampleApplication.h" class Example1 : public ExampleApplication { public: void createScene() { } protected: private: }; INT WINAPI WinMa |