5.13 三维数字地形漫游一、概述三维数字地形系统是地理信息系统的重要组成部分,现在应用于许多领域。
可以从模拟飞行游戏、Google数字地球中体验到三维数字地形生动、形象以及具有良好互动性等特点。
三维数字地形已成为具有很强应用价值的技术,但是单纯利用Direct3D或OpenGL来实现三维地形需要大量专业知识,同时编程量很大;如果使用专业三维地形引擎则价格昂贵,这些限制了广大爱好者对三维数字地形的开发研究。
不过,随着开源运动的广泛开展,目前有许多开源三维图形游戏引擎(如Ogre,KlayGE,Nebula,Irrlicht等)可用于三维数字地形系统的开发,使三维数字地形程序变得易于开发。
其中由德国电脑游戏专家Nikolaus Gebhardt设计的Irrlicht三维图形引擎,可以应用于各种.NET语言中,在普通电脑即可运行,易于掌握,并由活跃的开发团队支持。
比较适合进行三维数字地形漫游系统的开发。
文中采用目前广泛使用的C#语言,结合Irrlicht三维图形引擎实现了三维数字地形的漫游。
由于采用三维图形引擎,免去了许多底层编程工作;同时,C#的特性又使该程序开发速度较快。
二、引擎配置以Microsoft Visual Studio .NET 2003为例介绍开发过程。
1.引擎下载从Irrlicht三维图形引擎的主页:/的Download栏目中下载Irrlicht引擎 SDK压缩包。
整个压缩文件约为16MB,解压后生成irrlicht-1.3.1目录,该目录包括Irrlicht引擎C++源代码、引擎动态链接库、使用手册以及一些三维素材。
2.建立C#工程在New Project中选择建立C#的 Console Application工程,如图1所示,将工程命名为Terrain。
3.在工程中加入Irrlicht引擎鼠标右键点击新建工程的References项,选择Add References…加入引擎的动态链接库.dll,该文件在irrlicht-1.3.1\bin\Win32-VisualStudio目录中,如图2所示。
图1 建立C#工程图2 添加引擎成功加入引擎后,在References项中会显示新子项,如图3所示。
图3 References项中显示三、地形生成方法用电脑生成三维地形的方法有许多种,其中使用最广泛的就是高度图生成法。
我们经常会看到用不同的颜色表示海拔高度的地图。
高度图生成法采用相同原理,只不过为了便于计算机处理,海拔高度值用图像中的亮度值表示。
即图像中每一点处的灰度值代表该坐标处的地形海拔高度,越接近黑色则海拔越低,反之越接近白色则海拔越高。
要使地形具有真实感,地形图像必须符合一些要求,主要是灰度数值的连续变化,两像素距离越近的点灰度值越接近。
地形图像较容易得到,我们可以利用图像处理软件Adobe Photoshop制作,利用其“Render”特效的“Clouds”项,将前景设为黑色,背景设为白色,这样得到的图像可以表现出非常真实的地形效果。
也可以从网上直接下载相关的用灰度值表示的高度图。
本文采用irrlicht引擎中多媒体素材文件夹meida中的高度图terrain-heightmap.bmp,如图4所示。
为了便于编程实现,这里将路径为irrlicht-1.3.1\media的素材文件夹拷贝到与新建C#工程Terrain相同的目录下。
图4 高度图这张高度图尺寸为256*256。
可生成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,isWirefra me);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;}接下来设置视窗和镜头。
设置窗口尺寸为640*480,并创建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()函数载入地形场景节点,用于地形场景的管理和渲染。