在一款完整的3D游戏或是三维动画中,大地和天空都是构建场景时不可缺少的元素,另外,伴随着游戏中的角色形象上变得越来越饱满,越来越细节化,游戏中的地形系统也随着时间的推移变得越来越真实,越来越复杂,我们可以想象一副具有真实感的地形会给玩家带来多大的震撼效果,因此,在游戏引擎中地形系统的重要性就不言而喻了。
目前,游戏中有很多种方法来制作地形系统,当然,每一种方法都有它的优势和弱项。它可以是你的任务看起来很简单,也可以是你的工作量大的惊人。地形制作的方法多种多样,本节我们会通过相应的示例展示给大家Ogre3D中一种实现地形的方法。当然有时候我们也需要在一望无际的天空中或是在辽阔无垠的大地上增加一点雾气朦胧的感觉,这就需要用到Ogre的雾化效果,通过学习这个章节我们可以知道在Ogre中使用雾化效果是一件很简单的事情。不过,Ogre中使用地形和天空等元素,就不像以前章节中,在场景中添加一个Ninja模型那么简单了。好,下面我们就进入Ogre中地形和天空的世界,看看Ogre中是如何使用这些元素的。
8.1 地形
在开始学习“地形”的概念之前,我们先来了解一下“高度图”的概念。
高度图是一张简单的2D图片,由黑色、白色和之间的254种渐变灰度所生成,如下图所示:
这种方法的原理是每一种级别的灰度表示了不同的高度。色彩越亮的地方,地形就越高。
用高度图来制作地形是比较简单的,而且它可以生成一些最为真实的地形结构。在游戏中,我们通常可以在图形编辑器中,比如Photoshop中来制作高度图。用高度图的方式生产地形的优势是:制作简单,可以方便快速地做出大的变动,比较真实。但是这种方式同样也有它的劣势:很难做出小的修改,为了恢复正常可能会花费相当多的时间等。
Ogre3D过去在地形上的支持很少,过去只是让你导入一个高度图,然后在上面放一层基本纹理和一细节纹理,在1.7版本之后,Ogre在里面单独加入了terrain的模块,在地形的支持上已经非常强大。
笔者注: 在1.7之前的Ogre版本中,如果我们想要在一个3D场景中创建并使用一个地形,就必须要用到Terrain Scene Manager这个类;但是在Ogre 1.7之后的版本中,我们就可以使用Ogre Terrain System这个类来取代Terrain Scene Manager了,事实上Ogre Terrain System使用起来要方便的多。 |
下面我们同样使用在前面章节保存的模板代码,通过一个示例程序逐步给大家讲解Ogre中的地形系统。
第一步,修改createScene函数中的内容如下:
void createScene() { mCamera->setPosition(Ogre::Vector3(1683, 50, 2116)); mCamera->lookAt(Ogre::Vector3(1963, 50, 1660)); mCamera->setNearClipDistance(0.1); mCamera->setFarClipDistance(50000); if(mRoot->getRenderSystem()->getCapabilities()->hasCapability(Ogre::RSC_INFINITE_FAR_PLANE)) { mCamera->setFarClipDistance(0); //如果相机距离支持无限大就设置为无线距离 } Ogre::MaterialManager::getSingleton().setDefaultTextureFiltering(Ogre::TFO_ANISOTROPIC); Ogre::MaterialManager::getSingleton().setDefaultAnisotropy(7); } |
这里我们修改的相机的位置和方向,调整了相机的远近裁剪距离,这是由于通常情况下,地形会相当的大,我们想要相机能看到很远的距离。最后几行的代码是设置材质的一些属性,这里是设置默认的各向异性值。
第二步,设置光源。
Ogre::Vector3lightdir(0.55, -0.3, 0.75); lightdir.normalise(); Ogre::Light*light =mSceneMgr->createLight("tstLight"); light->setType(Ogre::Light::LT_DIRECTIONAL); light->setDirection(lightdir); light->setDiffuseColour(Ogre::ColourValue::White); light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4)); mSceneMgr->setAmbientLight(Ogre::ColourValue(0.2, 0.2, 0.2)); |
这里我们的地形组件使用定向光去计算地形光路图,同时我们增加了一个环境光源,用来平滑整个环境。
第三步,引入地形相关的头文件,并在VS2008中依次在菜单栏中点击:“项目——属性——配置属性——链接器——输入”,在“附加依赖项”处添加属性页中附加OgreTerrain_d.lib。然后在“项目——属性——配置属性——C/C++——附加包含目录”中添加地形相关的包含目录(你的Ogre根目录)\Components\Terrain\include)。
#include <OgreTerrain.h> #include <OgreTerrainGroup.h> |
第四步,为我们的类添加如下几个private类型的类成员变量及成员函数:
Ogre::TerrainGlobalOptions*mTerrainGlobals; Ogre::TerrainGroup*mTerrainGroup; bool mTerrainsImported;
void defineTerrain(long x, long y); void initBlendMaps(Ogre::Terrain*terrain); void configureTerrainDefaults(Ogre::Light*light); |
在构造函数中初始化成员变量,在析构函数中释放它们:
Example1() { mTerrainGroup = NULL; mTerrainGlobals = NULL; } ~Example1() { if (mTerrainGroup) { OGRE_DELETE mTerrainGroup; } if (mTerrainGlobals) { OGRE_DELETE mTerrainGlobals; }
} |
第五步,我们需要设置TerrainGlobalOptions,这是一个表示地形的总体特性的类,它可以设置地形的一些特征,首先你需要new出来一个,因此继续在createScene函数中添加如下代码:
mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions(); |
第六步,我们构造一个TerrainGroup对象,用来帮助管理地形网格,继续添加如下代码。
mTerrainGroup = OGRE_NEW Ogre::TerrainGroup(mSceneMgr,Ogre::Terrain::ALIGN_X_Z, 513, 12000.0f); mTerrainGroup->setFilenameConvention(Ogre::String("Example1Terrain"),Ogre::String("dat")); mTerrainGroup->setOrigin(Ogre::Vector3::ZERO); configureTerrainDefaults(light); |
这里TerrainGroup类的构造函数需要一个SceneManager类的实例,一个地形对齐选项,一个地形大小和一个地形世界大小作为参数。然后我们使用setFilenameConvention函数告诉TerrainGroup当我们想要保存地形的时候的名字前缀和后缀名。接着,我们设置起点的位置。最后一行,我们调用configureTerrainDefaults函数,这个函数的实现代码稍后会实现。
第七步,定义地形并加载所有:
for (longx = 0;x <= 0; ++x) for (longy = 0;y <= 0; ++y) defineTerrain(x,y); // sync load since we want everything in place when we start mTerrain |