Ogre的分頁大地形場景管理器PLSM2使用手記
By MulinB
最近專案需要模擬一個3D場景飛機飛行的簡單演示,主要功能就是提供一個比較大的地形高程圖和一個飛機飛行路線,能在三維下顯示演示飛機飛行。感覺以前自己見過不少類似的遊戲,這無非是第一個人稱射擊類或者模擬飛行類的遊戲的簡化版,所以從避免重複製造輪子的角度決定查一下開源遊戲引擎,看看有沒有類似的可以很快開發出第一人稱射擊類遊戲的引擎。經過查看了一些網上的評論和對比文章決定使用三維圖形渲染引擎OGRE來開發。
OGRE的演示Demo裡有個Terrain的演示,直接是一個由地形高程圖生成三維地形場景的例子,其使用的是OGRE內建的TerrainSceneManager(
到網上查了一下關於支援大地形的場景管理器的資料,發現OGRE的一個AddOn外掛PLSM2(Paging Landscape SceneManager)
環境: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特有的引數了,程式碼片段如下:
上面的程式碼中的變數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');
<完>