馬省軒 任麗娜
摘 要:本文采用C#編程語言,利用Irrlicht三維圖形引擎實現了三維數字地形的漫游。為三維數字地形顯示提供了較易實現的解決方案。
關鍵詞:C# 高度圖 Irrlicht引擎 三維地形 場景漫游
一、概述
三維數字地形系統是地理信息系統的重要組成部分,現在被應用於許多領域。我們可以從模擬飛行游戲、Google數字地球中體驗到三維數字地形生動、形象以及具有良好互動性等特點。三維數字地形已成為具有很強應用價值的技術,但是單純利用Direct3D或OpenGL來實現三維地形需要大量專業知識,同時編程量巨大;如果使用專業三維地形引擎則價格昂貴,這些限制了廣大愛好者對三維數字地形的開發研究。不過,隨着開源運動的廣泛開展,目前有許多開源三維圖形游戲引擎如Ogre,KlayGE,Nebula,Irrlicht等可用於三維數字地形系統的開發,使三維數字地形程序變得易於開發。其中由德國電腦游戲專家Nikolaus Gebhardt設計Irrlicht三維圖形引擎,可以應用於各種.NET語言中,在普通電腦即可運行,易於掌握,並由活躍的開發團隊支持。比較適合進行三維數字地形漫游系統的開發。
文中采用目前廣泛使用的C#語言,結合Irrlicht三維圖形引擎實現了三維數字地形的漫游。由於采用三維圖形引擎,免去了許多底層編程工作;同時,C#的特性又使該程序開發速度較快。所以本文采用的方法具有較強的實用性。
二、引擎配置
這里以Microsoft Visual Studio .NET 2003為例介紹開發過程。
1、 引擎下載,從Irrlicht三維圖形引擎的主頁:http://irrlicht.sourceforge.net/ 的Download欄目中下載Irrlicht引擎 SDK壓縮包,目前該引擎的最新版本是Irrlicht-1.3.1。整個壓縮文件約為16MB,解壓后生成irrlicht-1.3.1目錄,該目錄包括Irrlicht引擎C++源代碼、引擎動態鏈接庫、使用手冊以及一些三維素材。
2、 建立C#工程。
在New Project中選擇建立C#的 Console Application工程,如圖1所示,這里將新工程命名為Terrain。
3、在工程中加入Irrlicht引擎
鼠標右鍵點擊新建工程的References項,選擇Add References…加入引擎的動態鏈接庫Irrlicht.NET.dll,該文件在irrlicht-1.3.1\bin\Win32-VisualStudio目錄中。如圖2所示。
圖1 建立C#工程
圖2 添加irrlicht.NET引擎
成功加入引擎后,在References項中會顯示新子項Irrlicht.NET,如圖3所示。
圖3 References項中顯示Irrlicht.NET
三、地形生成方法
用電腦生成三維地形的方法有許多種,其中使用最廣泛的就是高度圖生成法。我們經常會看到用不同的顏色表示海拔高度的地圖。高度圖生成法與采用相同原理,只不過為了便於計算機處理,海拔高度值用圖像中的亮度值表示。即圖像中每一點處的灰度值代表該坐標處的地形海拔高度,越接近黑色則海拔越低,反之越接近白色則海拔越高。要使地形具有真實感,地形圖像必須符合一些要求,主要是灰度數值的連續變化,兩像素距離越近的點灰度值越接近。地形圖像較容易得到,我們可以利用圖像處理軟件Adobe Photoshop制作,利用其“Render”特效的“Clouds”項,將前景設為黑色,背景設為白色,這樣得到的圖像可以表現出非常真實的地形效果。也可以從網上直接下載相關的用灰度值表示的高度圖。本文采用irrlicht引擎中多媒體素材文件夾meida中的高度圖terrain-heightmap.bmp,如圖4所示。為了便於編程實現,這里將路徑為irrlicht-1.3.1\media的素材文件夾拷貝到與新建C#工程Terrain相同的目錄下。
圖4 高度圖
這張高度圖尺寸為256X256。可生成216個三維網格頂點,這是普通電腦系統一次所能繪制的最多頂點數。如果要繪制更大的三維地形,則需要分批多次繪制。通過高度圖生成三維圖形后,可在Irrlicht引擎的支持下增加控制功能以及碰撞檢測用於實現三維數字地形漫游。具體實現過程如下。
四、地形漫游程序
三維地形漫游程序采用console風格,程序的整體框架如下:
using System;
using System.Text;
using System.IO;
using Irrlicht;
using Irrlicht.Video;
using Irrlicht.Core;
using Irrlicht.Scene;
using Irrlicht.GUI;
namespace TerrainRoam //命名空間指定為為地形漫游
{
class Program: IEventReceiver
{
//高度圖及紋理載入路徑
string path="../../../../media/";
//定義地形場景節點
ITerrainSceneNode terrain;
static void Main(string[] args)…
public bool OnEvent(Event p_event) …
public void run()…
}
}
其中主類Program從irrlicht事件接收器類IEventReceiver繼承。該類成員函數包括程序進入點Main(),引擎事件接收器類成員函數的重載函數OnEvent(),地形載入及主循環函數run()。另外在程序開始通過使用using指令指定包含文件和相應的命名空間。包括Irrlicht的視頻驅動、引擎核心、場景管理以及用戶圖形界面的命名空間。用於實現console風格的編程。下面,分別介紹Program類的成員函數。
1.程序進入點函數
首先用[STAThread]指示應用程序的默認線程模型是單線程單元,然后用new從堆上創建類Program的對象,並利用成員函數run()載入地形並進入地形場景渲染循環。
[STAThread]
static void Main(string[] args)
{
Program prog = new Program();
prog.run();
}
2. 事件響應函數
OnEvent()是irrlicht事件接收器類成員函數的重載,用於響應用戶自定義的事件。這里使用該函數實現三維地形網格模式和紋理顯示模式的實時轉換。用戶可通過”w”鍵實現這一切換。
public bool OnEvent(Event p_event)
{
// 檢測用戶是否按下W鍵
if (p_event.Type == EventType.KeyInput &&
!p_event.KeyPressedDown)
{
switch(p_event.Key)
{
case KeyCode.KEY_KEY_W:
isWireframe=!isWireframe;
terrain.SetMaterialFlag(MaterialFlag.WIREFRAME,isWireframe);
break;
}
}
return false;
}
3. 地形載入及主循環函數
該函數的作用包括圖形程序庫的選擇、設置視窗和鏡頭、載入高度圖、碰撞檢測的實現以及建立程序主循環。
其中可供選擇的圖形程序庫包括Driect3D 9.0、Driect3D 8.0、OpenGL,涵蓋了普通PC機上使用的主流圖形驅動庫。另外如果用戶電腦上沒裝上述程序庫的話,也可選擇irrlicht軟圖形驅動,但是顯示效果會差一些。
public void run()
{
DriverType driverType;
// 詢問用戶選擇什么圖形庫
StringBuilder sb = new StringBuilder();
sb.Append("Please select the driver you want for this example:\n");
sb.Append("\n(a) Direct3D 9.0c\n(b) Direct3D 8.1\n(c) OpenGL 1.5");
sb.Append("\n(d) Software Renderer\n (otherKey) exit \n\n");
// 獲取用戶輸入
TextReader tIn = Console.In;
TextWriter tOut = Console.Out;
tOut.Write(sb.ToString());
string input = tIn.ReadLine();
// 根據用戶輸入確定相應的圖形庫
switch (input)
{
case "a":
driverType = DriverType.DIRECT3D9;
break;
case "b":
driverType = DriverType.DIRECT3D8;
break;
case "c":
driverType = DriverType.OPENGL;
break;
case "d":
driverType = DriverType.SOFTWARE;
break;
default:
return;
}
接下來設置視窗和鏡頭。設置窗口尺寸為640X480,並創建Irrlicht設備device用於調用引擎核心支持,實例化引擎的場景管理對象、視頻驅動對象、圖形界面接口對象,用於場景管理、圖形程序庫調用以及圖形界面的管理。設置鏡頭類型為FPS型(第一人稱),用戶可以從第一人稱視點觀察三維地形,並可控通過移動鼠標實現抬頭、低頭功能,通過鍵盤上的“↑、↓、→、←”進行鏡頭前、后、左、右移動。
// 創建Irrlicht設備,如果創建失敗則退出程序
IrrlichtDevice device = new IrrlichtDevice(
driverType, new Dimension2D(640, 480), 32, false, true, true);
if (device == null)
{
tOut.Write("Device creation failed.");
return;
}
//引擎響應當前類的事件
device.EventReceiver=this;
ISceneManager smgr=device.SceneManager;
IVideoDriver driver=device.VideoDriver;
IGUIEnvironment env= device.GUIEnvironment;
// 加入FPS鏡頭,並設置鏡頭位置
ICameraSceneNode camera = smgr.AddCameraSceneNodeFPS(null,100.0f,1200.0f,-1);
camera.Position=new Vector3D(1900*2,255*2,3700*2);
camera.Target= new Vector3D(2397*2,343*2,2700*2);
camera.FarValue=12000.0f;
// 設置鼠標箭頭不可見
device.CursorControl.Visible=false;
下一步導入高度圖,這里利用addTerrainSceneNode()函數載入地形場景節點,用於地形場景的管理和渲染。在該函數中設置需要載入高度圖的路徑,場景管理器會自動載入高度圖並根據其創建三維地形。為了使地形場景看起來更大一些,設置比例尺度向量為(40, 4.4, 40)。
terrain = smgr.AddTerrainSceneNode(
path+"terrain-heightmap.bmp",null,-1,
new Vector3D(),new Vector3D(40, 4.4f, 40), new Color(255,255,255,255));
碰撞檢測的加入可防止漫游時鏡頭穿透地形表面。為了使地形漫游有碰撞的功能,加入三角形選擇器,檢測三角形面片。同時將碰撞檢測同鏡頭關聯並設置碰撞動作,這樣當有山峰擋在鏡頭前面時,鏡頭不會從山體中間穿過,而是依地形起伏運動。
//創建地形節點三角形選擇器
ITriangleSelector selector =
smgr.CreateTerrainTriangleSelector(terrain, 0);
//創建碰撞反應動作並與鏡頭關聯
ISceneNodeAnimator anim = smgr.CreateCollisionResponseAnimator(
selector, camera, new Vector3D(60,100,60),
new Vector3D(0,0,0),
new Vector3D(0,50,0),0.0005f);
camera.AddAnimator(anim);
最后建立程序主循環。當Irrlicht設備運行狀態device.Run為真時,繪制出場景中所有的物體,其中整型變量lastFPS用於記錄每秒鍾渲染幀數並將結果顯示在窗口標題欄上。當主循環結束后,調用GC.Collect()回收資源。
int lastFPS = -1;
while (device.Run())
{
if (device.WindowActive)
{
device.VideoDriver.BeginScene(true, true, new Color(0, 200, 200, 200));
device.SceneManager.DrawAll();
device.VideoDriver.EndScene();
int fps = device.VideoDriver.FPS;
if (lastFPS != fps)
{
device.WindowCaption = " Terrain [" +
device.VideoDriver.Name + "] FPS:" + fps.ToString();
lastFPS = fps;
}
}
}
GC.Collect();
}
通過上述程序,可實現三維地形顯示的基本功能。
五、顯示結果
對上述工程進行編譯並運行。程序運行后首先會彈出console界面,請用戶選擇圖形程序庫。這里使用Driect3D 9.0,如圖。
圖5 選擇圖形程序庫
選擇完后,系統會自動加載高度圖並建立三維地形,結果如圖5。這是用網格線表示的地形。用戶可通過鍵盤和鼠標控制實現第一人稱三維場景漫游。
圖6 選擇圖形程序庫
為了使地形效果更生動,在導入高度圖函數AddTerrainSceneNode()后加入如下代碼。
//設置紋理放大比例
terrain.ScaleTexture(1.0f, 20.0f);
terrain.SetMaterialFlag(MaterialFlag.LIGHTING, false);
//多細節紋理映射
terrain.SetMaterialType(MaterialType.DETAIL_MAP);
terrain.SetMaterialTexture(0, driver.GetTexture(path+"terrain-texture.jpg"));
terrain.SetMaterialTexture(1, driver.GetTexture(path+"detailmap3.jpg"));
//加入天空盒
smgr.AddSkyBoxSceneNode(
driver.GetTexture(path+"irrlicht2_up.jpg"),
driver.GetTexture(path+"irrlicht2_dn.jpg"),
driver.GetTexture(path+"irrlicht2_lf.jpg"),
driver.GetTexture(path+"irrlicht2_rt.jpg"),
driver.GetTexture(path+"irrlicht2_ft.jpg"),
driver.GetTexture(path+"irrlicht2_bk.jpg"),null,0);
//使用mipmap效果
driver.SetTextureCreationFlag(TextureCreationFlag.CREATE_MIP_MAPS, true);
這里通過SetMaterialFlag()函數設置場景光照效果為全亮顯示,同時利用GetTexture()函數導入低、高細節層次紋理貼圖,利用引擎支持的mipmap技術根據鏡頭離場景遠近進行動態映射。離鏡頭較遠的場景只使用低細節層次紋理。鏡頭附近的場景則同時使用兩種紋理的混合,這樣即可以生動表示地形場景,也可減少系統資源消耗。如圖7所示。
圖7 高、低細節層次紋理
為了使三維地形更生動,加入天空盒,在場景上、下、左、右、前、后都導入貼圖,形成遠景,具有透視感。這里的低細節層次紋理采用irrlicht素材文件夾media中的靜態陰影貼圖,使地形具有真實感。最終顯示結果如圖8所示。
圖8 顯示結果
五、結語
文中提供了一種實現三維數字地形漫游的解決方案。在Irricht三維圖形引擎的支持下,利用C#可較容易地實現三維數字地形的漫游。本文提供的程序框架是可以擴充的,讀者可參考Irrlicht引擎使用手冊,加入更多功能,從而編寫出更好的三維地形漫游程序。
參考文獻:
1.Nikolaus Gebhardt. Irrlicht.NET Documentation. http://irrlicht.sourceforge.net,2007
2.麥中凡,陸永寧編著.C#編程語言.北京航空航天大學出版社 2001
原文鏈接:C#實現三維數字地形漫游
Irrlicht .NET三維引擎庫鏈接:Irrlicht .NET CP