By MulinB
最近项目需要模拟一个3D场景飞机飞行的简单演示,主要功能就是提供一个比较大的地形高程图和一个飞机飞行路线,能在三维下显示演示飞机飞行。感觉以前自己见过不少类似的游戏,这无非是第一个人称射击类或者模拟飞行类的游戏的简化版,所以从避免重复制造轮子的角度决定查一下开源游戏引擎,看看有没有类似的可以很快开发出第一人称射击类游戏的引擎。经过查看了一些网上的评论和对比文章决定使用三维图形渲染引擎OGRE来开发。
OGRE的演示Demo里有个Terrain的演示,直接是一个由地形高程图生成三维地形场景的例子,其使用的是OGRE内置的TerrainSceneManager(下面简称TSM)场景管理器,《Pro OGRE 3D Programming》里面对这个场景管理器的使用介绍的相当详细,无非就是如何配置一个terrain.cfg的配置文件进行映射。更详细的介绍可以从官方wiki得到:http://www.ogre3d.org/wiki/index.php/Terrain_Scene_Manager 。用这个场景管理器载入比较小的地形高程图时效果还比较理想,不过载入比较大一点的地形高程图时,速度就明显慢了下来,打开程序时的等待时间变得很长,帧频也变得比较低。
到网上查了一下关于支持大地形的场景管理器的资料,发现OGRE的一个AddOn插件PLSM2(Paging Landscape SceneManager)可以将大的地形高程图分成小的page载入,无奈关于这个场景管理器的用法网上资料并不多,使用起来有些困难。经过一番痛苦的折磨调试,终于还算成功的在OGRE1.6版本下把PLSM2使用了起来,效果还不错,载入速度明显快了很多,只是仍然有些问题,就是在地形漫游时,地形会自己变形,郁闷……这个暂留作TODO吧,估计是有些设置没设置好,或者是这个场景管理器依然不完善。下面把使用PLSM2的过程记录一下备忘。
环境:Windows XP, MS Visual Studio 2005 + SP1。
//--------------------------------------------------------------------------
关于PLSM2的使用的官方wiki:http://www.ogre3d.org/wiki/index.php/Paging_Scene_Manager 。
其中有关于PLSM2的安装、使用、配置文件如何配置的详细说明。但是按照这些官方wiki一步一步进行并不能让Demo程序顺利跑起来,你需要根据报错的提示信息将可执行文件相对于media文件夹的路径。而且这其中比较让人疑惑的是,会有两套media文件夹,一套是mapsplitter用,另一套才是使用PLSM2的Demo程序使用。需要注意的是,配置文件的填写很重要,也很耗时。需要mapsplitter的配置文件(用于将大地形高程图分割成小块),还需要配置运行时将小块地形图映射成三维场景下的世界坐标系的参数。主要的配置文件可以参考wiki:http://www.ogre3d.org/wiki/index.php/Paging_Scene_Manager_config_files 。
关于配置文件,需要说明的是,用于纹理自动生成的效果不是太好,如果没有合适的纹理,不妨使用Matlab+photoshop将地形高程图生成伪彩图作为地形纹理,使用TextureFormat=ImagePaging将纹理也分割成小块,从实验效果来看,如果伪彩图生成的合适,那么三维场景效果还比较漂亮。还有一点是配置Terrain map specification里的Map definition参数时,width和height的值是之前分割好的小块地形图在两个方向的个数,比如如果大地形图是16385×16385的话(注意地形图的大小按PLSM2的要求应该是(2^n)+1),小块的大小是PageSize=513的话(这里的pagesize也应该是(2^n)+1),那么width和height的大小应该是(16385-1) / (513-1) = 32。还有参数ScaleX, ScaleZ是将一个小块(pagesize大小的地形图)映射到三维场景后的长和宽,而不是将整个大图映射到三维场景的长和宽,经测试,这个ScaleX=PageSize×400的时候,三维地形效果比较理想,地形比较平滑,而且地形起伏明显。ScaleY是映射到三维场景下的高度范围(0~ScaleY)。其他的很多参数可以参照wiki说明去设置。
当你把Demo程序跑起来之后,你会发现使用PLSM2场景管理器载入大地形时的时间明显比TSM缩短,不过你会发现有个眼中的问题是当你在三维场景里漫游时,地形像是有生命一样,会动,当拉近镜头时,很多山会长高,当拉远镜头时,很多山会缩回去,⊙﹏⊙b汗……这个问题我调了很多设置参数也没搞定,哪位大侠搞定希望指点迷津,不过可以肯定的是,显卡比较好的时候这个效果不是太严重。
好吧,现在需要从Demo程序跳出来,把PLSM2用到自己的工程项目中了,你会发现之前从官方wiki介绍的地址 http://tuan.kuranes.free.fr/Ogre.html 下载的编译好的Demo程序、MapSplitter程序、PLSM2 DLL和Lib文件,是用OGRE1.2编译的,而如果你用的是最新版的OGRE SDK的话,或许是像我一样使用了OGRE1.6.1,那么你最好像我一样直接从SVN下载最新代码(最好是跟你使用的SDK版本相同的代码),自己编译出一套MapSplitter程序和PLSM2 DLL和Lib文件。
然后就是使用PLSM2的三维场景下的世界坐标系的问题了。在PLSM2的世界坐标系里,原点(x,z)=(0,0)点(注意在OGRE的三维世界坐标系里,x,z是长宽两个方向,y是高度方向)是在场景的中间,对应着地形高程图的中心位置,而不是地形高程图的左上角。例如:
大地形高程图大小: 16385×16385
块大小(PageSize): 513
Width, Height: 32
ScaleX, ScaleZ: 204800 (204800=(513-1)×400)
那么,PLSM2三维世界坐标系的大小是(SizeX, SizeZ) = 32×512×400 = (6553600, 6553600)
那么,PLSM2三维世界坐标系的左上角的坐标是(MinX, MinZ) = -6553600/2 = (-3276800, -3276800)
好在这些信息其实不用自己计算,PLSM2的场景管理器有相应的接口可以得到这些参数,而在代码里需要做的是,将一个SceneManager的指针mSceneMgr转换为PagingLandScapeSceneManager指针,然后用PagingLandScapeSceneManager提供的接口获得这些参数。在写代码之前,需要将PLSM2的头文件拷贝到当前工程中,因为PLSM2里头文件相互引用,想挑出这些头文件还要一番功夫,我拷贝的头文件清单如下:
OgrePagingLandScapeData2DManager.h
OgrePagingLandScapeOcclusion.h
OgrePagingLandScapeOcclusionElement.h
OgrePagingLandScapeOcclusionQuerySet.h
OgrePagingLandScapeOcclusionTraversal.h
OgrePagingLandScapeOctree.h
OgrePagingLandScapeOctreeCamera.h
OgrePagingLandScapeOctreeNode.h
OgrePagingLandScapeOctreeSceneManager.h
OgrePagingLandScapeOptions.h
OgrePagingLandScapePoolSet.h
OgrePagingLandScapePrerequisites.h
OgrePagingLandScapeSceneManager.h
之后,就可以使用PagingLandScapeSceneManager接口获得PLSM2特有的参数了,代码片段如下:
view plaincopy to clipboardprint?
PagingLandScapeSceneManager* G_pPLSceneMgr = NULL;
Real worldSizeX, worldSizeZ;
G_pPLSceneMgr = static_cast
G_pPLSceneMgr->getWorldSize(&worldSizeX, & worldSizeZ);
PagingLandScapeOptions* pPLSM2Options = G_pPLSceneMgr->getOptions();
Real xScaleFactor = pPLSM2Options->scale.x;
//x方向放大因子, ScaleX/(PageSize-1) , 这里是400
Real zScaleFactor = pPLSM2Options->scale.z;
//z方向放大因子, ScaleZ/(PageSize-1) , 这里是400
Real maxY = pPLSM2Options->scale.y; //高度最大值, 这里是ScaleY
PagingLandScapeSceneManager* G_pPLSceneMgr = NULL;
Real worldSizeX, worldSizeZ;
G_pPLSceneMgr = static_cast
G_pPLSceneMgr->getWorldSize(&worldSizeX, & worldSizeZ);
PagingLandScapeOptions* pPLSM2Options = G_pPLSceneMgr->getOptions();
Real xScaleFactor = pPLSM2Options->scale.x;
//x方向放大因子, ScaleX/(PageSize-1) , 这里是400
Real zScaleFactor = pPLSM2Options->scale.z;
//z方向放大因子, ScaleZ/(PageSize-1) , 这里是400
Real maxY = pPLSM2Options->scale.y; //高度最大值, 这里是ScaleY
上面的代码中的变量pPLSM2Options,在调试模式观察之,其实可以观察到很多PLSM2的参数,是使用PLSM2的很好的一个帮手。
其他的按照官方wiki做应该没有什么问题。
//--------------------------------------------------------------------------
附本人做的小程序的截图一张(纹理贴图使用Matlab+Photoshop生成):
附Matlab生成伪彩图代码(之后可以采用photoshop进行拼接、调色):
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear all;close;
%读取未经过处理的地形高程图,高度值范围(0~5000),png为16位灰度(0~65535)
dxgc_data=imread('dxgct.png');
%定义用于贴图的伪彩显示的colormap,是本人经过手工调色事先用matlab调好的颜色表
dxcolor=[0 0 0.5625;0.1 0.2 0.7813;0.2 0.4 1;0.3333 0.5333 1;0.4667 0.6667 1;0.6 0.8 1;0.5 0.8 0.75;0.4 0.8 0.5;0.3 0.8 0.25;0.2 0.8 0;0.24 0.78 0;0.28 0.76 0;0.32 0.74 0;0.36 0.72 0;0.4 0.7 0;0.44 0.68 0;0.48 0.66 0;0.52 0.64 0;0.56 0.62 0;0.6 0.6 0;0.5926 0.5852 0;0.5852 0.5704 0;0.5778 0.5556 0;0.5704 0.5407 0;0.563 0.5259 0;0.5556 0.5111 0;0.5481 0.4963 0;0.5407 0.4815 0;0.5333 0.4667 0;0.5259 0.4519 0;0.5185 0.437 0;0.5111 0.4222 0;0.5037 0.4074 0;0.4963 0.3926 0;0.4889 0.3778 0;0.4815 0.363 0;0.4741 0.3481 0;0.4667 0.3333 0;0.4593 0.3185 0;0.4519 0.3037 0;0.4444 0.2889 0;0.437 0.2741 0;0.4296 0.2593 0;0.4222 0.2444 0;0.4148 0.2296 0;0.4074 0.2148 0;0.4 0.2 0;0.4 0.1882 0.01176;0.4 0.1765 0.02353;0.4 0.1647 0.03529;0.4 0.1529 0.04706;0.4 0.1412 0.05882;0.4 0.1294 0.07059;0.4 0.1176 0.08235;0.4 0.1059 0.09412;0.4 0.09412 0.1059;0.4 0.08235 0.1176;0.4 0.07059 0.1294;0.4 0.05882 0.1412;0.4 0.04706 0.1529;0.4 0.03529 0.1647;0.4 0.02353 0.1765;0.4 0.01176 0.1882;0.4 0 0.2];
%高程图转化为索引图
dxgct_ind=gray2ind(dxgc_data, 1000); %经实践,1000对应着60个索引灰度级
%索引图转化为伪彩图,本来这一行就可以生成,但是地形图太大时matlab会内存不足,所以改为分块
%dxgct_rgb=ind2rgb(dxgct_ind, colormap(dxcolor)); %使用之前调整好的colormap
%分块输出
dxgct_rgb0=ind2rgb(dxgct_ind(1:1500,1:1500), colormap(dxcolor));
dxgct_rgb1=ind2rgb(dxgct_ind(1:1500,1501:3000), colormap(dxcolor));
dxgct_rgb2=ind2rgb(dxgct_ind(1501:3000,1:1500), colormap(dxcolor));
dxgct_rgb3=ind2rgb(dxgct_ind(1501:3000,1501:3000), colormap(dxcolor));
%输出为png,matlab内存不足,改为分块
%imwrite(dxgct_rgb, 'dxgct_texture.png', 'PNG');
%分块输出
imwrite(dxgct_rgb0, 'dxgct_texture0.png', 'PNG');
imwrite(dxgct_rgb1, 'dxgct_texture1.png', 'PNG');
imwrite(dxgct_rgb2, 'dxgct_texture2.png', 'PNG');
imwrite(dxgct_rgb3, 'dxgct_texture3.png', 'PNG');
<完>
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/MulinB/archive/2009/05/14/4183339.aspx