OGRE除了我们已经见过的普通场景以外还支持大型复杂的场景,如:宽阔的野外地形和复杂的大楼和迷宫。3D引擎支持大型复杂场景的主要难度在于对场景的组织和裁减。一个大型场景的三角型数量极其庞大,如果没有有效的场景组织和裁减方法,计算机在渲染的时候效率会很低。解决大办法就是用BSP或八叉树等数据结构将场景组织起来,利用与这些数据结构相关的快速检索算法将摄象机看到的内容拣选出来,再送到渲染器进行渲染,这样图形处理器的负载才会减小。
在OGRE中场景管理器的类型有四种:
ST_GENERIC, 普通场景
ST_EXTERIOR_CLOSE, 室外封闭场景
ST_EXTERIOR_FAR, 室外无限场景
ST_INTERIOR 室内场景
除了第一种普通场景外,其它几种都是处理复杂场景的专用场景管理器。OGRE引擎中有一个SceneManagerEnumerator类,它的作用是管理已实现的场景管理器。在OGRE的Root类里就就聚合了一个SceneManagerEnumerator对象,已供选择需要的场景管理器。在OGRE FrameWork的ExampleApplication类中可以看到这样的代码:
view plaincopy to clipboardprint?
virtual void chooseSceneManager(void)
{
// Get the SceneManager, in this case a generic one
mSceneMgr = mRoot->getSceneManager(ST_GENERIC);
}
virtual void chooseSceneManager(void)
{
// Get the SceneManager, in this case a generic one
mSceneMgr = mRoot->getSceneManager(ST_GENERIC);
}
从以上代码可知,在普通情况下使用的是普通场景管理器。如果要使用特殊的场景管理器,通过mRoot->getSceneManager()函数做出选择就可以了。
在OGRE的SceneManager类中有这样的一个函数setWorldGeometry,它的作用是读入世界信息(复杂场景中的大楼、野外地形等不变场景内容,区别于程序员手工加入的可以控制的场景元素),并将其管理起来。对于普通场景管理器来讲,没有“World Surface(Terrain,Room...)”这个概念,调用这个函数将只会抛出一个 "World geometry is not supported by the generic SceneManager." 的异常。
而OGRE在其引擎提供的Plugin_BSPSceneManager.dll中提供了通过BSP算法实现的ST_INTERIOR 室内场景管理器,它重新实现了setWorldGeometry函数,使其可以读入一个.bsp的室内场景文件并管理之。.bsp是QUAKE和CS等室内游戏使用的场景文件类型。OGRE还在其引擎提供的Plugin_OctreeSceneManager.dll中提供了通过八叉树算法实现的ST_EXTERIOR_CLOSE室外封闭场景管理器,它同样重新实现了setWorldGeometry函数,使其可以读入一个.cfg室外场景配置文件,并载入以灰度图形式表达的地形高程图。
需要注意的是特殊场景管理器不仅可以管理复杂场景中那些属于“World Surface”(复杂场景中的大楼、野外地形等不变场景内容)的场景内容,它也具有普通场景管理器的全部功能。所以在应用时,通过setWorldGeometry函数载入“不变世界”之后依然可以向场景中加入SceneNode和Entity等其它场景元素,依然可以通过FrameLisener来控制这些普通场景元素的运动。只不过它们将在一个具有楼房或山坡的场景中运动了。
室内场景
Demo_BSP工程是OGRE引擎带的室内场景例子。打开Demo_BSP工程查看代码。
OGRE在其引擎提供的Plugin_BSPSceneManager.dll插件中提供了通过BSP算法实现的ST_INTERIOR室内场景管理器。OGRE引擎在初始化的时候会载入Plugins.cfg中指定的全部插件,只要在Plugins.cfg中包含Plugin_BSPSceneManager.dll插件就可以使用了。
由于OGRE目前支持的是QUAKE3的地图,所以必须有一个quake3settings.cfg来指定QUAKE3的地图包(. pk3文件,其实是一个ZIP文件)和其中的地图文件名(.bsp)。QUAKE3是一个商业游戏软件,其中的全部地图的知识产权都属于ID公司,所以OGRE引擎的DEMO程序并没有附带任何QUAKE3地图,你必须在自己的计算机上安装QUAKE3的地图包,并将路径设置到quake3settings.cfg中去,Demo_BSP才能够正确运行。
在BSP.h文件中,定义了BspApplication类,该类的构造函数首先读入quake3settings.cfg,并解析出其中的地图包文件名和地图文件名。代码如下:
view plaincopy to clipboardprint?
BspApplication()
{
// Load Quake3 locations from a file
ConfigFile cf;
cf.load("quake3settings.cfg");
mQuakePk3 = cf.getSetting("Pak0Location");
mQuakeLevel = cf.getSetting("Map");
}
BspApplication()
{
// Load Quake3 locations from a file
ConfigFile cf;
cf.load("quake3settings.cfg");
mQuakePk3 = cf.getSetting("Pak0Location");
mQuakeLevel = cf.getSetting("Map");
}
地图包文件实际上是一个ZIP文件,该ZIP文件中包含OGRE真正需要的实际扩展名为.bsp的地图文件,所以还必须将地图包ZIP文件加入到OGRE的资源搜索路径中去。代码如下:
view plaincopy to clipboardprint?
void setupResources(void)
{
ExampleApplication::setupResources();
ResourceManager::addCommonArchiveEx(mQuakePk3, "Zip");
}
void setupResources(void)
{
ExampleApplication::setupResources();
ResourceManager::addCommonArchiveEx(mQuakePk3, "Zip");
}
由于采用了特殊的室内场景管理器,所以必须重新实现chooseSceneManager函数,以选择正确的场景管理器。代码如下:
view plaincopy to clipboardprint?
void chooseSceneManager(void)
{
mSceneMgr = mRoot->getSceneManager(ST_INTERIOR);
}
void chooseSceneManager(void)
{
mSceneMgr = mRoot->getSceneManager(ST_INTERIOR);
}
最后是创建场景,代码如下:
view plaincopy to clipboardprint?
void createScene(void)
{
// Load world geometry
mSceneMgr->setWorldGeometry(mQuakeLevel);
// modify camera for close work
mCamera->setNearClipDistance(4);
mCamera->setFarClipDistance(4000);
// Also change position, and set Quake-type orientation
// Get random player start point
ViewPoint vp = mSceneMgr->getSuggestedViewpoint(true);
mCamera->setPosition(vp.position);
mCamera->pitch(90); // Quake uses X/Y horizon, Z up
mCamera->rotate(vp.orientation);
// Don't yaw along variable axis, causes leaning
mCamera->setFixedYawAxis(true, Vector3::UNIT_Z);
}
void createScene(void)
{
// Load world geometry
mSceneMgr->setWorldGeometry(mQuakeLevel);
// modify camera for close work
mCamera->setNearClipDistance(4);
mCamera->setFarClipDistance(4000);
// Also change position, and set Quake-type orientation
// Get random player start point
ViewPoint vp = mSceneMgr->getSuggestedViewpoint(true);
mCamera->setPosition(vp.position);
mCamera->pitch(90); // Quake uses X/Y horizon, Z up
mCamera->rotate(vp.orientation);
// Don't yaw along variable axis, causes leaning
mCamera->setFixedYawAxis(true, Vector3::UNIT_Z);
}
在创建场景的过程中,首先通过场景管理器的setWorldGeometry方法载入地图。
接下来是设置摄象机。QUAKE地图中都有预先设置好的起始点和起始方向,通过场景管理器的getSuggestedViewpoint方法可以获取到这个ViewPoint,而后再将摄象机定位到这个点上。由于QUAKE是以XY轴构成的平面为水平面,Z轴为上方向,所以必须将摄象机用mCamera->pitch(90)语句调整90度。mCamera->rotate(vp.orientation);一句的意思是将摄象机调整到起始方向。最后将摄象机的旋转轴固定,因为人只能头向上走路,不能象飞机那样360度全空间翻滚。因为在QUAKE中Z轴向上,所以通过mCamera->setFixedYawAxis(true, Vector3::UNIT_Z);语句实现。
室外场景
Demo_Terrain工程是OGRE自带的室外场景的例子,打开该工程查看代码。
OGRE在其引擎提供的Plugin_OctreeSceneManager.dll插件中提供了通过八叉树算法实现的ST_EXTERIOR_CLOSE室外封闭场景管理器。OGRE引擎在初始化的时候会载入Plugins.cfg中指定的全部插件,只要在Plugins.cfg中包含Plugin_OctreeSceneManager.dll插件就可以使用了。
室外场景的主要内容是起伏的地形。表现地形起伏的一般方法是通过一个灰度图来表达场景中每一块土地的高度,颜色浅为高,颜色深为低。引擎读取这个灰度图,并根据每个像素的颜色值画出高低起伏的地形网格。再用另外的彩色图作为地表纹理铺在地形网格上,就实现了室外场景。
除此之外,为了防止地表纹理放大造成的失真,还需要一个代表地面细节的可拼接的细节纹理图。地形显示的时候应该还可以设置缩放因子。OGRE将这些地形显示需要的图片和属性都放在terrain.cfg文件中设置。请查看terrain.cfg。
在程序中实现室外场景漫游很简单。首先需要选择室外场景管理器,代码如下:
view plaincopy to clipboardprint?
virtual void chooseSceneManager(void)
{
// Get the SceneManager, in this case a generic one
mSceneMgr = mRoot->getSceneManager( ST_EXTERIOR_CLOSE );
}
virtual void chooseSceneManager(void)
{
// Get the SceneManager, in this case a generic one
mSceneMgr = mRoot->getSceneManager( ST_EXTERIOR_CLOSE );
}
选择好室外场景管理器后,通过该场景管理器创建场景就可以了。
view plaincopy to clipboardprint?
void createScene(void)
{
// Set ambient light
mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
// Create a light
Light* l = mSceneMgr->createLight("MainLight");
// Accept default settings: point light, white diffuse, just set position
// NB I could attach the light to a SceneNode
// if I wanted to move automatically with
// other objects, but I don't
l->setPosition(20,80,50);
mSceneMgr -> setWorldGeometry( "terrain.cfg" );
mSceneMgr->setFog( FOG_EXP2, ColourValue::White, .008, 0, 250 );
mRoot -> showDebugOverlay( true );
}
void createScene(void)
{
// Set ambient light
mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
// Create a light
Light* l = mSceneMgr->createLight("MainLight");
// Accept default settings: point light, white diffuse, just set position
// NB I could attach the light to a SceneNode
// if I wanted to move automatically with
// other objects, but I don't
l->setPosition(20,80,50);
mSceneMgr -> setWorldGeometry( "terrain.cfg" );
mSceneMgr->setFog( FOG_EXP2, ColourValue::White, .008, 0, 250 );
mRoot -> showDebugOverlay( true );
}
以上的createScene函数首先设置环境光,又创建了一个点光源。通过mSceneMgr -> setWorldGeometry( "terrain.cfg" );语句载入terrain.cfg配置文件,解析它,同时创建地形。场景管理器的setFog函数可以设置雾化效果。
尽管本例使用的是室外场景管理器,我们依然可以用熟悉的方法向场景中加入Entity等场景元素,试着向程序中加入如下代码并查看效果。
view plaincopy to clipboardprint?
Entity *ent = mSceneMgr->createEntity("head", "ogrehead.mesh");
// Set material loaded from Example.material
ent->setMaterialName("Examples/EnvMappedRustySteel");
// Add entity to the root scene node
mSceneMgr->getRootSceneNode()->createChild()->attachObject(ent);
Entity *ent = mSceneMgr->createEntity("head", "ogrehead.mesh");
// Set material loaded from Example.material
ent->setMaterialName("Examples/EnvMappedRustySteel");
// Add entity to the root scene node
mSceneMgr->getRootSceneNode()->createChild()->attachObject(ent);
这部分代码向场景中加入了一个食人魔。从这里可以看到,场景管理器的setWorldGeometry方法会将固定不变的地形创建好,我们还可以很方便的向场景中加入其它可运动的物体(汽车、动物等),从而实现一个近乎真实的野外世界。
出处:http://blog.csdn.net/aries__lee/archive/2010/10/15/5943373.aspx