笔者学习地理大类下的专业,因为近期实习的需要,琢磨了在ArcGIS Engine环境下,利用VS窗体和Unity交互,将基于真实地形搭建的虚拟地理空间,引入到GIS应用中,实现数据可视化的漫游,取得了一定效果。此方向探究十分小众,全网文章有限。现将主要内容,结合基础的遥感地形反演手段、Unity大型场景搭建等,分享一篇原创博客,欢迎参考交流!另外,笔者并非互联网和数字媒体专业,涉及相关内容,如有表述不规范等问题,恳请批评指正。
本篇博客涉及内容不算太复杂,但涉及一定的学科交叉,如果具备一定的Unity游戏开发基础,并对地球系统科学和地图学有所了解,理解起来会更加容易。
本场景是基于新疆某国家级自然保护区地区数字高程模型与遥感影像,建立虚拟的地理环境,并用电脑实现全局漫游,整个过程也可以看作一条新的地理数据可视化途径。
(这里先上几张成果图吧hhh,感兴趣的朋友欢迎看最后的视频)
————————主体峡谷地貌———————
—————————雪中木屋————————
—————————水体特效————————
————————云杉林和云海———————
—————————悬泉瀑布————————
ArcGIS Engine是ArcGIS的一套软件开发引擎,可以让开发者创建自定义的GIS桌面程序,属于C/S架构,是本次开发的主要环境。VR即虚拟现实技术,目前,VR新兴蓬勃的发展方向主要面向Web端,因为Web端下B/S架构可以更好地实现网络交互式体验,登录浏览器,不用下载专门应用程序就可以实现VR漫游等虚拟现实操作。
在虚拟现实与游戏开发中,主要使用UE4和Unity 3D两款软件。UE4是Epic Game公司推出的一款开发软件,十分高效全能,其使用的物理引擎可以打造非常逼真的画面,物理学碰撞系统非常优秀,骨骼动画系统和AI框架甚是一绝,是目前公认效果最为出色的系统之一,用其打造的游戏有《和平精英》、《绝地求生》等,电影《星球大战外传》等也使用了它优秀的渲染功能。Unity 3D软件,初学者可能很难达到UE4做出的画面效果,但支持范围更广(包括此次开发所述的和微软VS窗体结合),为众多游戏开发、影视、美术、建筑创作者提供了方案平台,将创意变为现实,支持平台包括手机、平板电脑、游戏主机、PC、VR与AR等,《神庙逃亡》、《炉石传说》等都是用它打造的。
鉴于Unity具备极广的支持范围,实习初期笔者就想过能否将它和Visual Studio 2010的Winform窗体结合起来,这算是极为小众的一个结合方式,起初很担心只是异想天开,最终一厢情愿,但查阅Unity自带控件后,发现竟然确实可行!虽然U3D可以通过导出WebGL的方式发布到Web端,直接支持在HTML5标准浏览器中运行,但还可以利用一个叫做Unity Web Player控件,它虽然名为Web,但也支持导出程序和VS窗体的嵌入,把建好的虚拟地理空间和AE环境连接起来,在GIS应用中直接调用。
综上实现了调用嵌套,但仅仅显示画面是不够的,如果能够把两个相互独立的程序搭建通信通道,实现交互操作,才算核心!通过全网屈指可数的几篇文章发现,Unity Web Player控件下属自带一个SendMessage () 函数,可以向U3D导出包中传递信息,而这也是唯一一个可以实现控制Unity 3D空间中物体的方法,后文会对其做具体说明。
至此,整个通信技术的流程可以归纳为两大步:1.完成Unity和Winform窗体的连接嵌入;2.实现Unity和Winform的通信与交互。只要上述两步的理论验证通过了,就代表着连接与交互没有问题,之后即可以在U3D中搭建真实地理环境、完善大型场景了,这一过程主要在Unity中完成,具体操作和游戏开发十分类似。
Windows 10系统、Visual Studio 2010、ArcGIS Engine 10.2、ArcGIS软件、Unity4.5、Bigemap GIS Office、Global Mapper 23.0 、Photoshop 2017、Blender 3.0。
这里介绍两种连接交互方式:
经过前期一周多的探索,笔者尝试了利用Unity Web Player Ocx控件嵌入的方式,验证有效;在实践最后阶段,我利用另一种直接调用Unity打包出的.exe文件方式,发现则更加直接,但两种方式各有不确定性。
(关于和VS2010匹配的Unity版本:因为笔者实习环境对应VS 2010和AE10.2,无法改变,所以需要相关版本的Unity Web Player控件,因而也对应更老版本的Unity软件。版本不匹配,会导致画面加载不出来。目前Unity 2022已经发布了,全网关于Unity软件的教学大多是2017版本开始的,笔者第一次毫无思绪地尝试2015年发布的Unity 5,但其所带的Unity Web Player控件无法真正连接,运行时画面一直加载不出来,可见Unity 5还是不够“老”,匹配不了VS 2010。之后,又卸载了和Unity 5所有相关的组件,并在电脑上清除了注册表里面的信息,不给后续尝试带来影响。随后下载的是2014年发布的Unity 4.5,这可能是目前能找到最老的版本了,安装完毕后在VS里添加Unity Web Player控件,最终成功实现连接!)
首先第一种方式,Unity Web Player OCX控件形式。从宿主Windows程序中调用Unity游戏内部方法比较方便,有API接口SendMessage可以直接使用,但此函数中传递的参数仅限于int,float,string三种简单类型,方式很有限,所以如何利用这三个量实现交互,很关键,挺佩服微软公司开发2010版本时候集成如此多样,太周全了呜呜~(手动感动!!!)
相反地,Unity程序不能直接调用宿主Windows程序中的函数,Unity官方文档说这是为了安全考虑。另外,曾经流行的uLua插件还不支持Web Platform,所以无法使用lua进行编程。
第二种连接方式,是直接编写按钮方法,在VS中打开已打包.exe的方式。这种方式在实习一开始笔者并不了解,是整个项目结束后才发现的,它的好处在于,不需要在电脑上安装Unity软件,不用验证嵌套过程和交互性,只要获得别人做好的已达包的.exe文件,就能在Winform里面打开了,简单高效,但不确定性在于,在VS2010程序中,打开.exe的Unity程序是否会受Unity版本或导出格式的影响,比如用Unity 2022打包的能不能用VS 2010打开?这些有待广大爱好者继续探究;另外,不在电脑上装Unity,对.exe程序修改完善的部分,则需要原始的开发者完成。
上面所述的第二种方法和Winform打开普通.exe文件方法相同,因为该类博客较多,本文还是主要介绍第一种方法。
理论验证的第一步:连接嵌入。先在Unity4.5中搭建一个最简易的环境,在Unity 4.5游戏工程在打包时,发布Platform要选择Web Player方式。
从Unity官网下载4.5版本时,需要安装Unity Web Player插件,不知道这一步是不是必须的,因为笔者在安装Unity时,好像VS之后自动完成了。安装完Unity Web Player网页插件后,Windows系统内就自动注册了Unity Web Player OCX控件。之后,在新建的VS窗体中,菜单栏上选择工具-选择工具箱项打开COM组件界面,下拉寻找,找到Unity Web Player Control,勾选上。
此时返回窗体界面,另外再打开工具箱,工具箱里应该多出一个Unity Web Player Control,点击它,把它拖到窗体里。
单击上面添加的Unity画面控件,修改其属性。一般主要修改它的Dock与src2个属性,Dock实现Unity Web Player Control在父窗口中的停靠及填充方式,如果选择的Fill,这样Unity渲染窗口会填满整个Form,并且随着Form的缩放进行大小变化。Src属性设置,是设置之前导出发布的的.unity3d文件的路径,例如我的路径是:F:\Unity project1\build\build.unity3d(注意:这里采用的是刚刚Unity 3D平台build出的文件,文件名别忘了.unity3d的后缀格式)
如图为我的文件路径:
这一模块的最后,点击运行,如果可以看到之前在Unity中搭建的场景,说明第一步成功。
常见问题举例:
1.笔者刚开始运行的报错是,不加载场景画面,弹出了“Failed to initialize player’s 3D settings",这类问题基本上是因为版本不契合问题,最好是Unity 4系列下的版本,推荐4.5。卸载现有版本重新安装后,基本可以解决。
2.如果同样不加载场景画面,但报错“Failed to download data file",通常是路径填写错误,少些了一个文件夹名称等,需要自行确认,并填写正确的Src路径。
3.如果在VS的COM组件下发现没有可选的Unity Web Player内容,可能是控件没装上,需要到Unity web Player安装目录选择UnityWebPluginAX.ocx,我的安装目录是:C:\Users\admin\AppData\LocalLow\Unity\WebPlayer\loader。
下面介绍这种SendMessage()方法这种通信方式
这个过程采用Unity Web Player Control提供的API:SendMessage,上面的步骤中,我们通过拖拽的方式往form上添加了一个Unity Web Player Control,同时也自动生成了一个与之对应的变量:
private AxUnityWebPlayerAXLib.AxUnityWebPlayer axUnityWebPlayer1;
这个变量就是我们的Unity Web Player Control,通过其提供的函数SendMessage,就可以调用Unity脚本里的函数,这种方式与iOS Object-C代码调用Unity游戏脚本函数以及Android里Java代码调用Unity游戏脚本函数的方式相同,都是通过SendMessage。其函数原型为:
Void SendMessage(string obj, string method, object val);
第一个参数是Unity里要接收消息的obj的名称,第二个参数method是要调用的函数名,第三个参数是要传递的参数,只能是int,float,string三种之一。
在U3D中设定天空盒、搭建最简单的地形并上地表覆盖之后,我添加了一个飞机物体,在U3D中编写一个飞机移动的方法,封装好导出场景,并在VS窗体中添加一个button1按钮,利用SendMessage写一个简单代码,验证交互性:
private void button1_Click(object sender, EventArgs e)
{
axUnityWebPlayer1.SendMessage("plane", "Fly", 25);
}
这个函数中,plane就是U3D场景中飞机的物体名字,需要注意,每个物体的名字不能相同,否则无法识别到底传参给哪个物体。Fly是对应物体方法的函数名,25是传递给飞机的移动速度。点击运行后,加载出画面,每点击一次按钮,飞机从画面左端向提前设定好的右端移动25个单位值,交互验证完成!
做到这里,即可以确定连接与通信交互成功,之后就是在这个最简单的场景基础上,编写漫游控制的方法,并完成对所需区域地理环境的反演搭建,剩下的工作几乎都是以Unity为主线的开发建模工作了。
在Unity中完成地形的搭建,建立在DEM数字高程模型反演的基础上,此过程需要ArcGIS 10.2、Bigemap GIS Office、Global Mapper 23.0 、Photoshop 2017,最后在Unity4.5完成反演。此过程网上也可以找到其他方式,但此方法十分简洁有效的,这里声明对【BIGEMAP GIS Office】官方文档有所引用,链接参见文末。
首先需要精度较高的DEM数字高程数据,最好是12.5m精度或更高的,现实世界的地形范围必须为正方形(Unity 4.5不支持长方形的导入),否则需要ArcGIS创建新的地理数据库.shp格式文件,建立新的矢量面,进行剪裁,过程属于ArcGIS的基本操作,不再赘述。为了保证高精度遥感影像和DEM数据的地理范围完全一致,最好使用Bigemap GIS Office,创建矢量范围,下载任意位置这两种数据。
在Global Mapper 23.0中“文件”->“打开数据文件”,选择DEM数据文件,比如Test.img,弹出对话框选“全是”;成功打开地形文件后,如图所示:
之后,在工具栏下拉列表中选择“梯度渲晕”,在此列表框后面有一个“启用/取消晕渲”的按钮,我们需要取消渲染,这时显示的是此Dem数据的灰度图,之后文件中导出“输出光栅/图像格式”,周设置jpg的属性、采样间距,“图像品质”也可自行选择,而输出的带设置为“灰度(1个8位波段)”。最后,将生成一个带有地理坐标的.jpg文件,我们将其保存到自定义的位置。
接下来,借助的工具是Photoshop,笔者使用的是2017版本。导入之前导出的.jpg格式文件,在PS中设置画布的大小,单位选择像素,长宽值填写值需要是2的指数次幂,比如1024或2048,Unity 4.5的raw地形文件默认的最大参量为2048*2048,所以PS中不能超过2048像素,多余的部分必须剪裁,但剪裁后仍然需要正方形,而且需要和遥感影像对应,不能有任何不重合的像素,否则后期给地形做整体贴图的话影像与地形将不能重合。
这里涉及到地理坐标系和投影坐标系的关系问题,由于地理学通用的地理坐标系是建立在旋转椭球体上的,为了将椭球体上的地理目标和投影平面上的投影点建立一一对应的映射关系,难免会产生变形,因此原下载的呈正方形的,且已投影的遥感或DEM数据,可能在Photoshop中变为长方形,此时只能将剪裁,任意选取两者公共部分做全像素的保留,不能将图像在PSD中做变化拉伸处理,因为笔者尝试多次拉伸后发现,最后导入Unity反演的地形将会出现如下状况,像元之间出现断裂,具体原因不得而知。
回归正题,Photoshop中设置好画布参数,比如最大的支持像素为2048*2048以后,导出为.raw格式。打开Unity,建立地形后,在地形的设置里面导入.raw文件。
弹出的在弹出对话框中进行设置,Depth选择8bit,Width选择2048,Height输入2048,ByteOrder根据自己的情况选择Mac或Windows,地形x,z先默认2000,y是高度,表示地形最高处的高度,笔者写的是400,之后,直接点击“Import”即可以导入该地形;
(细心的小伙伴应该不难发现了,上图的原始反演的河谷,是不是和文章最开始展示的,也就是下图这张的地形场景很相似吗?)
其实这就是后期工作了,大约还需三周时间,来完成其场景搭建和细节美化。至于上图那些一块一块的,可不是梯田哦,而是DEM数据的空间分辨率,如果分辨率越高,自然而然“梯田感”也会越淡,但受限于2048的像素分辨率,如果DEM空间分辨率越高,能制作的地理范围也越小,在Unity中,可以通过地形柔和工具,消除这些“梯田”,使得山地变化更加柔和。
如果原来地形有遥感影像,可以在Global Mapper 23.0中使用地貌导出一张.jpg文件,在PS中剪裁为地形范围对应的正方形,作为Unity中可导入的纹理,并按地形大小,贴到这块地形上,并设置分辨率,但同样受限于U3D的处理能力,地形遥感影响的最大像素只有2048,直接使用比较粗糙,所以需要细节美化,下面,好比是学校美术课、软件操纵课和代码课的综合了。
为了使场景更真实,交互更流畅,这里具体有六大模块:
地形贴图与交互式漫游的方法设计、冰川元素与人文场景的搭建、雪岭云杉树的建模、风场的建立和阳光月光的轮转切换、云雾和降雪的粒子特效、河流湖泊与瀑布的水体效果。其中,前四者难度比后两者低一些,还需要两周时间来完成。
遥感影像的像素会被U3D压缩,地形贴图看上去很粗糙,因此需要上第二层皮肤:导入其他纹理粉刷调色,难度不大,但十分繁琐,好比曾经的美术课一样需要反复尝试,从而得到最后的画面。
相机需要调节加载和渲染范围,在大型场景中至少3000以上。交互过程需要之前的SendMessage()方法进行控制。将U3D摄像机位悬挂于Capsule上面,添加刚体碰撞,关闭重力引擎。在Unity 3D中,编写移动方法赋予Capsule(这里我将其命名为“Player”),设计其X,Y,Z三维空间方向,还有对应的三维旋转方向。以Z轴移动为例,笔者设计如下:
public void movez(float sp)
{
float speed = sp;
float distance = speed * Time.deltaTime;
Vector3 pos = this.transform.position;
pos.z += distance;
this.transform.position = pos;
}
三维旋转中,以上下的自我旋转为例:
public void lookupdown(float sp)
{
Vector3 angles = this.transform.eulerAngles;
float dx=sp;
angles.x+=dx;
this.transform.eulerAngles=angles;
}
上述两者在Winform程序中对应按钮的使用SendMessage方法,分别如下(这里设定每次移动800,还可以添加滑动条控制每次移动的大小、旋转角度等,方法自己另外编写即可):
private void button3_Click(object sender, EventArgs e)//北
{
axUnityWebPlayer1.SendMessage("Player", "movez", (-800.0));
}
private void button4_Click(object sender, EventArgs e)//南
{
axUnityWebPlayer1.SendMessage("Player", "movez", (-800.0));
}
private void button7_Click(object sender, EventArgs e)//上
{
axUnityWebPlayer1.SendMessage("Player", "lookupdown", (-15.0));
}
private void button5_Click(object sender, EventArgs e)//下
{
axUnityWebPlayer1.SendMessage("Player", "lookupdown", 15.0);
}
编写完移动与旋转的方法后,还需限制摄像机主人公的移动范围,不能让他溜太远,以本场景为例如下:
void Update () {
if (Input.GetKeyDown(KeyCode.Escape))//按ESC键退出
{
Quit();
}
Vector3 pos = this.transform.position;
if (pos.z < -40)
{
pos.z=-40;
this.transform.position = pos;
}
else if (pos.z>2260)
{
pos.z=2260;
this.transform.position=pos;
}
else if(pos.x<10)
{
pos.x=10;
this.transform.position=pos;
}
else if(pos.x>1950)
{
pos.x=1950;
this.transform.position=pos;
}
else if(pos.y>560)
{
pos.y=560;
this.transform.position=pos;
}
else if(pos.y<20)
{
pos.y=20;
this.transform.position=pos;
}
}
public void Quit()
{
Application.Quit();
}
上述的基本的漫游设计好后,导出现阶段的的程序,在VS中验证漫游的交互性能如图所示(此图为后续搭建完场景后截下的图):每次点击窗体右边按钮,都可以改变方位,实现一个安全但有些踉跄的漫游。
值得一提的是,在后期探索中,笔者发现使用Java编写的漫游方法效果更出色,甚至不用使用SendMessage()函数通信,也可以控制在VS窗口中玩家的操作:
#pragma strict
var lookSpeed = 0.1;
var moveSpeed = 0.1;
var rotationX = 0.0;
var rotationY = 0.0;
function Start () {
}
function Update () {
rotationX += Input.GetAxis("Mouse X")*lookSpeed;
rotationY += Input.GetAxis("Mouse Y")*lookSpeed;
rotationY = Mathf.Clamp (rotationY, -90, 90);
transform.localRotation = Quaternion.AngleAxis(rotationX, Vector3.up);
transform.localRotation *= Quaternion.AngleAxis(rotationY, Vector3.left);
transform.position += transform.forward*moveSpeed*Input.GetAxis("Vertical");
transform.position += transform.right*moveSpeed*Input.GetAxis("Horizontal");
}
如此一来,只需要以操控WASD四个键的方式,加上鼠标的方向控制,就能实现流畅的漫游了,这也能将1.2部分介绍的过程全部省略,很方便。但这样的方法对游戏画面渲染加载提出了极高要求,十分燃烧CPU,对电脑本身也是一种损害,一旦运行后处理器需要大量散热。
此部分需要在自行建模的基础上,导入更多模型,并且使用Blender 3.0整理贴图纹理,搭建的复杂程度和难度需根据自己的需求来调整,这里无法使用篇幅来描述了。
雪岭云杉树是新疆天山地区最为独特的树种,在模型不匹配,或者无法在地形工具中直接根据Mesh Render批量添加情况下,需要根据树木图片,在Unity中创建树木单体进行建模,调整比对,最终得到比较接近真实的云杉树模型。
在Unity中创建风场,设定相关参数后使树木和花草可以随风摆动。阳光和月光绑定在一个GameObject上面,作为其子物体,编写方法控制父物体自传,以带动阳光与月光的公转,模拟白天和月夜的切换,并调节配色与强度,提高虚拟地理环境的真实性。为了不让月亮升起时山体画面太暗,这里在月光旁补加了另外的白色点光源。(注:这里指的阳光和月光都是用原始的点光源制作的,细节可以根据自己的审美风格灵活调整)。受限于对Unity光照知识的缺乏,笔者无法让天空盒也随之变换,不知有何解决办法。
如图为夕阳下的俯视:
对于这部分来说,云雾和降雪都涉及Unity的粒子特效,无法添加太多篇幅介绍,可学习Unity相关资料。云雾扩散方法如下,将其悬挂在云雾发射器上就可实现,其他的性能参数还需要根据实际情况调整:
using UnityEngine;
using System.Collections;
[AddComponentMenu("Effects/SetRenderQueue")]
[RequireComponent(typeof(Renderer))]
public class SetRenderQueue : MonoBehaviour {
public int queue = 1;
public int[] queues;
protected void Start() {
if (!renderer || !renderer.sharedMaterial || queues == null)
return;
renderer.sharedMaterial.renderQueue = queue;
for (int i = 0; i < queues.Length && i < renderer.sharedMaterials.Length; i++)
renderer.sharedMaterials[i].renderQueue = queues[i];
}
}
降雪效果可能是这部分难度最温柔的了,笔者没有编写单独的代码,直接利用粒子特效调试比对就做好了,就是比较耗时,对耐心是极大的考验。。。
河流湖泊涉及水体效果,设置颜色、渐变,并编写方法打造水体的波纹、反射、折射、光照等,并加强渲染,让相机捕捉。如果使用的是Unity新版本,水体、河流可以导入专门的制作插件完成,但4.5版本不支持绝大部分插件,只能用专业版的水体渲染来替代,效果还是很真实的。这里分享其中一个重要的控制方法。
这里编写的OnWillRenderObject()函数的效果在于,当已知对象将由某些摄像机拍摄时,随着摄像机漫游镜头的移动,我们能持续渲染反射与折射,并进行反射范围等的更新,摄像机也会正常工作。
public void OnWillRenderObject()
{
if( !enabled || !renderer || !renderer.sharedMaterial || !renderer.enabled )
return;
Camera cam = Camera.current;
if( !cam )
return;
if( s_InsideWater )
return;
s_InsideWater = true;
m_HardwareWaterSupport = FindHardwareWaterSupport();
WaterMode mode = GetWaterMode();
Camera reflectionCamera, refractionCamera;
CreateWaterObjects( cam, out reflectionCamera, out refractionCamera );
Vector3 pos = transform.position;
Vector3 normal = transform.up;
int oldPixelLightCount = QualitySettings.pixelLightCount;
if( m_DisablePixelLights )
QualitySettings.pixelLightCount = 0;
UpdateCameraModes( cam, reflectionCamera );
UpdateCameraModes( cam, refractionCamera );
if( mode >= WaterMode.Reflective )
{
float d = -Vector3.Dot (normal, pos) - m_ClipPlaneOffset;
Vector4 reflectionPlane = new Vector4 (normal.x, normal.y, normal.z, d);
Matrix4x4 reflection = Matrix4x4.zero;
CalculateReflectionMatrix (ref reflection, reflectionPlane);
Vector3 oldpos = cam.transform.position;
Vector3 newpos = reflection.MultiplyPoint( oldpos );
reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
Vector4 clipPlane = CameraSpacePlane( reflectionCamera, pos, normal, 1.0f );
reflectionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
reflectionCamera.cullingMask = ~(1<<4) & m_ReflectLayers.value; // never render water layer
reflectionCamera.targetTexture = m_ReflectionTexture;
GL.SetRevertBackfacing (true);
reflectionCamera.transform.position = newpos;
Vector3 euler = cam.transform.eulerAngles;
reflectionCamera.transform.eulerAngles = new Vector3(-euler.x, euler.y, euler.z);
reflectionCamera.Render();
reflectionCamera.transform.position = oldpos;
GL.SetRevertBackfacing (false);
renderer.sharedMaterial.SetTexture( "_ReflectionTex", m_ReflectionTexture );
}
// Render refraction
if( mode >= WaterMode.Refractive )
{
refractionCamera.worldToCameraMatrix = cam.worldToCameraMatrix;
// 此处是设置斜投影矩阵,让近平面也成为反射
Vector4 clipPlane = CameraSpacePlane( refractionCamera, pos, normal, -1.0f );
refractionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
refractionCamera.cullingMask = ~(1<<4) & m_RefractLayers.value; // never render water layer
refractionCamera.targetTexture = m_RefractionTexture;
refractionCamera.transform.position = cam.transform.position;
refractionCamera.transform.rotation = cam.transform.rotation;
refractionCamera.Render();
renderer.sharedMaterial.SetTexture( "_RefractionTex", m_RefractionTexture );
}
if( m_DisablePixelLights )
QualitySettings.pixelLightCount = oldPixelLightCount;
//这里设置基于水体模式下的着色器关键字。
//Unity自带多种着色器,水体效果的着色器是其中之一
switch( mode )
{
case WaterMode.Simple:
Shader.EnableKeyword( "WATER_SIMPLE" );
Shader.DisableKeyword( "WATER_REFLECTIVE" );
Shader.DisableKeyword( "WATER_REFRACTIVE" );
break;
case WaterMode.Reflective:
Shader.DisableKeyword( "WATER_SIMPLE" );
Shader.EnableKeyword( "WATER_REFLECTIVE" );
Shader.DisableKeyword( "WATER_REFRACTIVE" );
break;
case WaterMode.Refractive:
Shader.DisableKeyword( "WATER_SIMPLE" );
Shader.DisableKeyword( "WATER_REFLECTIVE" );
Shader.EnableKeyword( "WATER_REFRACTIVE" );
break;
}
s_InsideWater = false;
}
如图展示的就是水面的纹理,还有对其他地理要素的反射效果,随相机位置的改变持续更新渲染。
瀑布顶的冰川天池:
瀑布的流水需要打开重力引擎,使用粒子特效,潭底也有水花飞溅的粒子组合。这里具体制作也因人而异,篇幅有限,无法再进行描述了,全网不同的制作方法也很多,挺方便学习的。
漫游演示
上面快速展示了一下AE环境下VS窗体中的运行效果,漫游方法改为了第三部分第1节给出的更新方法来控制,不再需要SendMessage()函数来通信交互了。在GIS应用中,通过鼠标点击获取地图上某一个经纬范围,就可以自行设计如何弹出这样的窗口,再在此窗口中对国家级自然保护区虚拟漫游。
以上就是今天分享的内容,时间和篇幅有限,很多地方只能描述大致处理手法,不能详细展开,有些抽象。笔者学习地理专业,对于Unity的很多地方理解并不顺畅,有很大局限性,制作效果也必然和专业水平有所差距,但基于真实地形环境的反演,以及在AE环境下Unity与VS窗体的交互,是本文主要的特色之处。
不同于本地应用,网络应用提供了跨平台的可用性,人手一个浏览器,使得Web端B/S架构的系统平台发展更加活跃,实用性也更高。本文的AE环境基于的是C/S本地架构,相比于网络VR,本地客户端可提供继续创新开发的范围就十分有限了。所以,谨以此分享一些个人所得,希望能为更多相关专业人员的开发设计,提供一些新的技术思路,也能为更多有兴趣的同行们,介绍一条特别的地理数据可视化之路。
地理信息技术与虚拟现实的结合,相互促进着发展,是未来交互创新融合的重要领域,前景广阔,我们也坚信,必将有更多探索在这条路上的人们,为我们未来的生活带来更加美好的体验!
真实世界地形创建方法原文链接:https://blog.csdn.net/bigemap/article/details/81131911