EP.7 第一个VR应用程序
在之前的章节中,我们已经学习了基本的开发虚拟现实应用的基本技术。除此之外,我们也对多种已经/即将面世的虚拟现实开发和应用平台进行了调查,也了解了一些流行的用于创建虚拟现实应用的工具。在最后一章中,我们将要把前面介绍的所有知识进行总结和综合,并创建出一个端对端的应用程序。
由于本书篇幅和时间的限制,我们暂时无法开发出一个产品级质量的应用程序。但此处将详细介绍开发一个支持简单用户交互的虚拟现实应用程序的过程,作为练习,本章中我们会开发一个可以被绝大部分人立刻理解的应用程序:一个基于智能手机平台的照片浏览器,而此处使用的开发工具和平台为谷歌公司的Cardboard VR。
书中的图片7-1是一副NEXUS5的屏幕截图:Yosemite国家公园的360°全景照片,在Cardboard上被并排渲染出来。在屏幕的下方是四张半透明的缩略图,如果用户浏览时注视着某一张缩略图,这张图片就会变得不透明以告诉用户:用户在此处选中了该图片。通过触摸Cardboard机器外部的电磁开关就可以进入被选中图像的全景画面,以上内容就是该章节要尝试构建的应用程序。
回忆一下,当开发Cardboard应用时,我们有几种选择可以使用的工具和编程语言。在此处我们选择Unity3D,它是现阶段最流行的虚拟现实应用开发环境,同时我们也可以通过基于Java的安卓开发环境(基于android studio IDE)。 此外,我们也可以通过HTML5语言对浏览器端口的网页代码进行移动端的编写,此处同样采用了基于Javascript的虚拟现实框架,例如Three.js。在最后,我决定使用Unity3D进行本章的软件开发。Unity引擎不仅十分流行,他同样具有许多优点:例如可以进行有用的图形化处理,拖拽功能同样优秀,这些便捷的功能使我们可以把注意力集中在如何设计应用,而不是繁杂的细节上。尽管如此,用户界面的创建仍然有些复杂和繁琐,所以如果你对Uniyu不是特别了解的话则需要仔细遵守每一步提示。
在为Cardboard开发一个简单的360°图片浏览器时,我们将回顾很多前面章节中我们已经讨论过的内容,其中包括:
1. 创造一个三维场景
2. 利用Unity3D引擎和编辑器来开发虚拟现实应用程序
3.利用Cardboard SDK渲染立体场景,并且使用手机的加速度计来跟踪运动轨迹;
4. 类似于其他大量VR应用程序所使用的功能:创造一个基于实现和点击的交互界面
5. 创建一个基于Android平台的应用程序
在此部分,我们建立的照片浏览器使用了大量的Google VR SDK在Unity中使用的案例,如果需要的话读者可以先回顾第六章的内容。
7-1 360°全景拼图
360°的全景拼图已经变得越来越常见:在IOS平台上有许多应用程序可以拍摄360°全景图像,而用户可以通过简单的触摸屏幕和朝着各个方向转动手机,应用程序就可以将多层图像无缝拼接成一张全景图片,并且在大部分情况下都有不错的效果。在许多Android平台的手机,例如三星Note4和Google Nexus5中已经自带了全景图像的应用程序了。
360°全景图像只是一种特殊排列的图像,这种图像和其他标准图片的存储格式相同,通常被存储为PNG或者JPEG。这种排列被称为等距离圆柱投影,这种布局方式最早可被追租到两千年以前的景点地图投影,在教材中的图7-2展示了图7-1所展示的全景图像在矩形视口下的效果,而图片7-1则被输出为球型视口。
7-2 创建项目
首先,我们需要创建一个Unity项目,创建几何对象并导入通过照片创建球型视口三维环境的资源。在执行该步骤时,该项目还不需要在Android平台上运行,也不要Cardboard。实际上,我们只是建立并预览了该工程文件,以确保建立了一个正确的三维场景。
7-2-1 获取软件,硬件与样本代码
如果你想重建该项目,你需要获得以下项目:
· Unity3D 5.2版本或更高。此处可在......处下载
· 面向Unity的Cardboard SDK开发环境以及该开发环境的指示说明
· 一个具有侧悬挂开关的Google Cardboard观看器,此处需要一个侧悬挂电磁开关。购买链接在主页
· 一个能运行Cardboard VR的安卓手机,作者此处使用了NEXUS 5生成和测试变异的应用程序。
· 应用程序的源代码,可以再Github上找到,此处文件夹Unity Code包含了所有的模块,如果想一步步按照示例来做,可以把这部分代码放到Unity项目的目录下,此处文件夹360°Viewer含有一个完整的Unity 项目,可以随意使用。
7-2-2 创建一个新的Unity场景和工程文件
· 创建一个新的Unity工程文件
· 把该工程文件命名为360°Viewer
· Unity会创建一个只有相机和灯光的默认场景
· 从主界面中选择场景,Unity会建议输入文件名来保存场景
· 将场景命名为360°Viewer,并保存到文件夹中。
· 将全景图像导入到Unity工程文件中
· 在示例代码中找到文件夹Spherepano, 并将该文件夹导入到Unity工程文件的Assets中。
· 此处会有 Importing Assets的进度条,之后文件夹会被现实出来
· 文件夹中的每张图像都会有一副缩略图。
7-2-3 创建一个全景背景
现在,我们要如何把一张等距圆柱投影的360°全景图变成一个360°的虚拟现实全景图像呢?答案会比你想象中的简单得多:我们只需要把图片设置为一个球型的表面材质即可。
首先,让我们在Unity中创建一个球体。在Hierarchy菜单中,点击鼠标右键会出现一个创建对象的菜单,此处选择3DObject-sphere并创建该物体。在Unity中默认的球体半径为1,默认材质为灰色纯色。此时我们需要把球体的大小放大到10倍,此时需要确保该物体的名字在Hirecrachy菜单内为Sphere,之后再观察者界面内,我们需要找到球的变换组建,再下面将会看到Scale属性,分别设置xyz的值为10。此时工作界面的尺寸单元更合适。
之后为了添加纹理,我们只需要把纹理拖动并在球型对象上松开即可。在工程界面内,打开spherepano文件夹,你会看到4站缩略图,选择pan0并拖拽到Hierarchy视口下的sphere对象上。此时该全景图片已经被成功的映射到了球面上,效果如图7-3所示。
然而,目前的状态并非温泉正确:Unity默认会把相机放在(0,1,-10),这意味着相机的位置在地平面上方一个单位以及屏幕外10个单位。
如果我们需要在虚拟现实应用中观察到全景图像,相机的位置应该在笛卡尔坐标系的原点。这样无论用户朝向什么房线观看,球面和相机就会始终保持相同的距离。所以我们需要回到Hierarchy面板中,选择Main Camera,并设置相机的位置坐标为(0,0,0),这样操作可以将相机设置在原点处。
现在我们已经创建了全景图像并添加了材质贴图,也将相机的初始位置设定到了原点,我们实际上即将完成工程文件的调试了,此时我们可以点击预览按钮。此时我们会触发一个窗口,对设置好的场景在做面上进行预览,此时的预览结果为图7-4。
这并非我们的预期,全景图去哪了?不要担心,我们会稍后修复这个问题,我们需要下讨论后端的渲染问题。
7-2-4 后端渲染
在现代的3D渲染系统中,所有的渲染工作都使用了shader组件:一段旨在计算每一个三维定点位置,并在二维视窗内绘制每一个像素的高级编程代码。Unity也是如此,编辑器提供了一个标准Shader组件,此时已经足够用于许多场景,例如网格的着色和光照,或者在其表面添加一张纹理。此外,如果有需求场景,Unity还允许用户开发和使用自己的shader。
标准Shader非常智能。他展示了多种多样的优化方法,其中一种就是背面剔除渲染法。简单地说,背面就是三角形和多边形从观察方向无法看到的一个面会被剔除渲染的方法。例如,如果有一个类似于球的几何纹理的面,由于这些面在几何对象的内部,因此在大多数情况下无法被观察到。 标准的Unity Shader将会将这些内部面忽略,并不进行渲染。但是当相机从内部朝外看是,就会像前面讲解的情况一样,因此我们需要关闭背面渲染提出功能。
然而,标准Unity着色器不提供类似的功能。幸运的是,一些被开发出来的Shader代码可以帮助我们规范并提出背面渲染的Shader。
下面我们会列举出这个Unity着色器的完整代码,使用时需要将它添加到项目中。这段代码采用了Unity着色器语言,Unity编译器将该代码编译为计算机显卡所采用的汇编编码,注意斜体行会设置Cull Off,使图形硬件画出图像内部的三角形而不是将这些面剔除。
把改代码放入到项目中后,我们可以观察全景图了。之后我们需要将调试好的着色器赋给球面网格:
· 首先在代码实例中找到DoubleSided.shader, 把她拖拽到Project面板左面的Assets子面板中。
· 在Project面板右面的Detail子面板中单击鼠标右键并选择Create-Material,命名为DoubleSided, 新的材质应该出现在子面板中并被选中,Inspector面板中会显示该材质的属性。
· 继续关注Project面板,在该我文件架结构中选中spherenpano。此时将Project面板的右面图像pan0拖拽到Inspector面板中DounbleSided材质的Base纹理预览区域。
· 在Assets自面板中选择材质DoubleSided,并在Hierarchy视口中将其拖拽到Sphere上。
如果改动顺利,我们可以看到Unity编辑器中的变化:4个视口左下角的Game视口将会显示全景图像的预览。如果将图7-5与图7-3进行对比,我们可以看到图7-3左下角的预览图像也仅仅显示了Unity通用的天空北京而不是全景图像。此时我们可以得出结论:球体的正反两面都被渲染了。
点击Preview按钮,我们可以看到桌面预览窗口如图7-6所示,现在我们马上就要成功了。
7-3. 添加Cardboard VR的支持
到现在为止我们还没有关注过Cardboard VR的设置和在Android平台上生成应用的过程。我们现在想要建立一个正常工作的三维全景图像。二正如你看到的那样,在把Vr界面渲染到Cardboard VR上并在手机上运行仍需要一些工作,这也是本节接下来要着重介绍的。
为了让Cardboard能支持该应用程序,我们需要将Cardboard SDK导入到Unity项目中,此时我们应该按照以下步骤:
· 找到在Unity IDE中的工程标签页,并选择Assets-Import package进行素材包的导入
· 你会看到一个文件对话框,用该对话框找到Unity SDK的下载位置。
· 选择文件 Cardboard SDK For Unity, unity package,如果是通过克隆GitHub来进行下载,可以在根目录下找到他。
· 如果你点击了Open, Unity会自动扫描文档并给你提供一个可以导入内容的表单。然后把所有的包Double导入到项目中,选中列表中所有的项目,并单击import按钮,就能在Assets面板中看到这些之前没有被导入的资源。
现在我们可以吧Cardboard 立体渲染和头部跟踪分工控制器添加到场景中了。在Project面板中找到名为Cardboard Main的Prefab,并选择文件夹 Prefabs,会在Detal面板中出现每一个Prefab的图标,在这个面板内把Cardboard main拖拽到Hierarchy面板中,就可以吧Prefab添加到场景中。
现在我们可以在桌面上预览你的场景了。该场景应该和图7-7中显示的截图类似
接下来,我们需要确保一切顺利:是时候在手机上检查了。查看前面章节的内容以建立一个安卓的Build:
· 在Unity主菜单中选择File-build Settings, 打开Build Settings 对话框。
· 在左下角的平台列表中选择Android,然后点击Switch Platform。
· 把建立的场景添加到Build栏目中。这个操作会打开Inspector菜单一设置Plater Settings面板,找到Bundle Identifier设置,并把该设置改为一个合理的Android 应用包的名字。此时应该改变Unity提供的默认值,否则Unity会在编译过程中报错。作者使用了vr.cardboard作为名字。此处同样需要把最低要求的API版本更新为安卓4.4。
· 选择resolution和Presentation选项,并把玩家设定从默认朝向改为景观左侧。
· 最后,回到Build Settings对话框并插入手机,点击Build & Run按钮。此时我们需要提供保存.apk文件,因此需要为他提供一个文件名。我选择了360°Viewer作为文件名,这里是在你的应用程序菜单上所需要显示的名称,点击保存以把.apk文件进行保存。
如果一切进展顺利,Unity会吧你所做的安卓程序包推送到手机上并打开该app。把你的手机放入Cardboard Viewer中,尝试进行调试并戴上这个设备没看一看周围:你会看到一个成功的3D全景图了!书中的图7-8展示了成功版本的软件的一个截图画面。
之后我们还需要进行一些小的调整:例如你可以看到,在你观察Cardboard Viewer时,全景图像的会出现一些黑色区域和闪烁。这是因为光照的原因。在这个场景中我们没有用到广元,但是Unity在除此创建项目是默认的放置了一个光源。在Hierarchy面板中找到名为Directional Light的项目,把它删除。而且我们也不需要默认相机这个组件,此处同样可以将它删除。
我们可以尝试在这个浏览器内观察另一张照片。在Project 面板中,我们可以在spherepano下选择Pan2的缩略图,并把它拖拽到Hierarchy的球上。确定运行桌面玉兰是否正常,然后点击Build And Run没带上浏览器设备,我们就可以看到新的全景图了:Yosemite大峡谷的天空。你同样会看到在Materials文件夹内出现了一个新的材质:pan2。在你把文件从资源中拖拽到面板内球上的时候,Unity自动为你创建了这个材质。这个新材质的Shader属性也同样被设置为了Doublesided着色器,因为Unity会记忆上一个材质的属性并应用于当前材质。
现在几乎大功告成了。我们可以使用自己的360°图像查看器来体验各种全景图片了,但我们不愿意每次都在编辑器中切换图像并重新生成软件。所以我们要制作一个用户交互界面,以便在应用程序内部进行图像的切换。
7-4 创建一个基于视线-点击的用户界面
如果你已经尝试使用了一段时间的虚拟现实设备,那你肯定对基于视线跟踪与点击的交互界面很熟悉了。VR场景包含了很多漂浮的物体,实质上是一些三维图标,当你注视这些图标时,这些图标会被标记为高亮。如果我们触摸屏幕,点击鼠标或者进行其他方式的交互之后,我们就会触发某种行为。
对于Cardboard来说,最流行的输入方式就是传统的电磁开关或者类似的触摸开关。例如Dodocase里荣国Popsicle Stick进行输入,当该物体触摸手机屏幕表面是,它实际上是通过弯曲一块电容塑料来产生于一个属于安卓的用户触碰时间,并模拟手指的触摸,此时我们假设已经有了一套具有电磁开关的Cardboard模型。这里我们会构建一个有四个方形的半透明图标的简单交互界面。他们位于倒数个第三个视图内,当我们注视某个图标时,这个图标会变得不透明,并选中我们对应的图像。此时如果对电磁开关进行触发就会激活该图标,并切换当前浏览全景图。在开始创建交互界面之前,我们需要进行一些清理工作。在Material界面下找到pan0材质。选择该材质,并重命名为PanoMaterial。。这是全景图的球面材质名称,此处将创建四种材质,每一种对应一个界面图标,在制作更多物体之前,我们需要进行材质管理,用同样的方法删除名为pan1的材质,这是我们在7-3中吧第二个全景图拖到球面上时创建的。
如果要创建而为平面,我们需要制作一些三维图表,此时我们需要使用扁平的方形对象。这里将从制作一个图标开始,设置正确的属性并为其添加事件来控制塔。在制作好一个图表之后,其他的图标都可以从第一个复制出来。
在Hierarchy面板中,点击鼠标右键并选择平面组件。这会在场景中添加面向一个水平方向的放行对象,我们在此处把对象重命名为PanoItem0。这个对象需要面对用户而非平放,所以我们需要给组件的rotation设置变换为(90,180,0),这个操作会让平面沿着X轴向旋转,而且面对用户。此外,我们还需要把这个对象放大到一个合适的大小,所以我们需要把组件的Scale设置为(0.05,0.05,0.05),并把组建的Position设置为(0,0,2),这箱操作会让图标沿着z轴退回2个单位,并处在x和y方向的正中央。此时我们可以带上观看器观看,以测试如下的实现操作。
现在我们需要设置材质。在Hierarchy面板中选中PanoItem0,并将Project面板中的pan1纹理拖拽到PanoItem0上,此时在平面上我们可以看到冰川点的全景纹理。然后我们需要设置图像的透明度:我们需要选定PanoItem0的材质组件,并把Shader组件的属性修改为Standard。
在Inspector面板中,点击材质名字旁边的小箭头可以展开材质属性。在主Map界面中,我们可以看到纹理映射相关的属性:在Albedo属性旁边我们就可以看到第二章全景图的缩略图。在缩略图的右边,会有一个彩色的矩形以表达该材质当前显示的颜色。双击该选项启动材质颜色的选择界面,在输入框A里吧Alpha值设置为128,此时A被设置为了一种半透明材质。之后回到Inspector面板,找到材质的Rendering Mode属性,设置为半透明。
在我们完成了上述操作之后,可以点击Preview按钮,我们可以看到如图7-9的图像:第二章全景图的半透明方形图标显示在视窗的中央,而这就是我们创建的第一个图标。
创建完成一个按钮后,我们需要制作一个完整的用户交互界面。由于完成了一个图标,此处我们将把该图标复制来生成其他三个图标。我们可以按如下步骤进行操作:
· 在Hierarchy面板中选择PanItem0,到Inspector面板中设置Position为(-1,2,-1,2),之后该图标就会被放在场景的左下角。
· 在Hierarchy面板中选择PanoItem0,然后单击鼠标右键选择Copy,再单击鼠标右键选择Paste每次是我们可以看到PanoItem(1)出现在了上面的层次结构图中。重复这一个步骤两次,我们会得到PanoItem2和PanoItem3两个对象,之后可以按照相等的间距推测出以下几个对象的Position。
此时我们进入Game预览,在场景下方我们可以看到四个横着排列的图标。
现在我们需要把图标的纹理改成不同的图片,否则每个图表看起来都是一样的。在Project面板中打开spherepano,我们可以看到每个纹理的缩略图,此时我们可以把他们分别拖拽到Hiearchy面板中对应的对象上,分别拖拽成功之后在game预览会出现更新后的纹理。
此时我们可以进行最后一步:由于拖拽的新纹理会导致Unity自动创建新的材质,所以我们需要设置每个材质的着色器,透明度和渲染模式。回到第一个图标,并找到Inspector中的材质组件,并确定Shader被设置为了Standard,双击Albedo旁边的颜色缩略图,在弹出的颜色选择界面设置alpha值为128,并把RenderingMode设置为Transparent,其他材质也需要进行同样的设置。
最后我们可以测试一下效果:把手机连接到计算机上,并在菜单中选择File-build standard,并点击Build and run按钮。当Unity把我们做好的Android应用包载入到手机中后,程序会自动启动。把我们的手机插入到Cardboard观看器中并试着旋转方向,注释每一个图标并按下电磁开关,我们就可以享受大量的360°全景照片了!
到现在为止,我们已经完成了第一个可用的虚拟现实应用程序,恭喜!