原文:
zh.annas-archive.org/md5/BB76013B3798515A13405091AD7CB582
译者:飞龙
前言
从计算机的角度来看,虚拟现实自上世纪 60 年代以来就存在。它在 90 年代末再次大规模出现,然后在一段时间内基本上崩溃了——尽管它从来没有真正消失。现在它又回来了,而且这一次它是来留下的。
导致这种变化的是手机——在手机上使用的大型、高分辨率显示技术已经帮助创建了 HMDs(头戴式显示器,或 VR 护目镜)。电路和计算机的速度也比以前快得多;1998 年曾经花费 25 万美元的计算机图形现在不到两千美元,而且速度更快。
构建 VR 世界一直是困难的。然而,你必须是一名 C++程序员,并且对高速编程、实时图形、几何学和其他复杂主题有很多了解。在过去几年中,游戏开发引擎已经简化了这一点,但只是到了一定程度。
使用 React VR,这更加简单。你现在可以使用 React 语法编写 VR 世界,这是一种简单的声明性类 HTML 语言。如果你想创建一个盒子,你只需要声明一个具有正确宽度、高度等的盒子,而不是编写过程性代码。语法可能很简单,但这些世界可以是事件驱动的,动画的,并且对用户输入和从网络获取信息也是响应的。
这将使你能够用简单的 JavaScript 和类似 HTML 的代码构建复杂的虚拟世界。这使用了一种名为 WebVR 的新基于浏览器的编程范式;普通 PC 和移动设备上的浏览器现在可以在 VR 中查看世界。
你也可以做到这一点,这本书将向你展示如何做到这一点。
你需要为这本书做什么
你需要一台 Windows PC,几乎任何类型的都可以;为了获得最大的乐趣,你需要一个 VR 设备。可以是 HTC Vive、Oculus Rift、三星 Gear VR、Google Daydream 或其他 VR 护目镜(包括 Google Cardboard)和一部手机。
即使你没有复杂的头戴式显示器(HMD)或 VR 头盔,你也可以开发这些 WebVR 世界;你可以在普通计算机屏幕上以平面模式查看它们。你可以花不到 20 美元甚至在许多地方免费获得一个简单的 VR 手机支架/头盔(Google Cardboard 或类似产品),所以不要让硬件成为了解下一个伟大事物的障碍。
这本书是为谁写的
这本书适用于任何想要在网络上学习虚拟现实并通过 React VR 创建引人入胜的 3D 网站的人。如果你已经了解一些 JavaScript,那么你会更快地掌握这些知识,如果你已经了解 React 或 React Native,那么学习速度会更快。即使你不了解,这本书也会逐步向你展示该怎么做。如果你已经知道如何进行多边形建模,这会有所帮助,但这本书也会向人们展示如何使用免费开源的 Blender 进行一些基本建模,以及在哪里获取免费下载。你不需要 VR 设备就能享受这本书——你可以在普通 PC 上进行示例,并且甚至可以将其发布到互联网上。
约定
在这本书中,你会发现一些区分不同信息类型的文本样式。以下是一些样式的示例及其含义的解释。
文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名都会以以下方式显示:“组件是真实的东西,不仅仅是标签或占位符,因为它们通过render()
函数内置了展示自己的方式。”
代码块设置如下:
<Box
dimWidth={4}
dimDepth={1}
dimHeight={9}
lit
/>
当我们希望引起你对代码块的特定部分的注意时,相关行或项目会以粗体显示:
f:
mkdir f:\reactVR
cd \reactVR
任何命令行输入或输出都以以下方式书写:
npm install mersenne-twister --save
新术语和重要单词以粗体显示。例如,屏幕上看到的单词,比如菜单或对话框中的单词,会以这样的方式出现在文本中:“一旦你分配了多边形,点击 View->Front,然后点击 Mesh->UV Unwrap->Cylinder Projection。”
警告或重要提示会以这样的框出现。提示和技巧会以这样的方式出现。
第一章:虚拟现实,到底是什么?
你正在阅读这本书来学习制作虚拟现实(VR),但虚拟现实到底是什么?
这似乎是一个足够简单的问题,但答案却五花八门。大多数人认为 VR 意味着几乎真实或者另一个现实。
这才不是虚拟现实的含义。
我认为这是因为虚拟这个词可以有几种不同的含义。对于计算机科学家来说,虚拟这个词意味着模拟其虚拟化的东西。换句话说,虚拟硬盘假装是一个硬盘。
虚拟对象表现得像是真实的,但实际上并不是——通常情况下,它比物理对象更灵活,更容易控制、修改和支持。在许多方面,它比物理对象更好。例如,虚拟磁盘就像计算机磁盘一样。它可以存储数据。然而,这些数据可能存储在物理旋转磁盘、固态硬盘,甚至内存中。虚拟磁盘可以调整大小,而物理磁盘只能复制到更大(或更小)的磁盘上。虚拟磁盘更加灵活。
有些人认为虚拟意味着几乎。如果特斯拉开过,他们可能会说,“那几乎是无噪音的!” 人们知道它并不真的无噪音,但它比大排量 V8 引擎开过要安静得多。或者,那个人几乎是个圣人,关于他们喜欢的人。在这种情况下,它的意思是几乎或者几乎可以说是。
虚拟也可以意味着有美德的人。行为端正的人是有美德的,尽管这不是这个词的正常用法(应该是 virtuous)。这就是这个词的起源;在拉丁语中,virtualis 意味着力量或美德。然而,在我们的情况下,我们指的是一些看起来真实,但实际上并不是。
我认为这是关于虚拟现实的误解。人们认为它几乎是真实的。许多人认为 VR 还没有达到那个水平,因为它看起来几乎不像真实世界。在 VR 头盔中看到的景象要像真实世界还需要相当长的时间;其他感官,尤其是触觉和味觉可能需要相当长的时间才能被模拟出来。
然而,这不是重点;虚拟现实的重点不是它几乎真实。重点是,当你在其中时,它看起来真实,即使它看起来与现实毫不相似。
我会再次说这个,因为这是一个重要的区别。虚拟现实,或者说增强现实,不需要几乎真实,但当你在其中时,它会看起来真实(即使它看起来并不真实)。
在本章结束时,你会学到:
-
虚拟现实是什么以及它是如何工作的
-
一些虚拟现实的历史-它并不新,这项技术已经有 50 多年的历史了!
-
用户代理-通过控制器与世界互动
-
渲染硬件
-
如何观看虚拟现实
-
头戴式耳机的类型
虚拟现实是什么以及它是如何工作的
我们有许多感官。为了让我们感觉到另一种现实是真实的,我们需要利用这些感官来愚弄大脑。大多数虚拟现实系统利用了两种:视觉和听觉;触觉也被使用,但不是以完全伸手触摸某人的意义(尽管人们正在研究它!)。
Tor Nørretranders 收集了有关感官及其相对带宽的数据,以计算机术语来说,这有点像比较苹果和机油,尽管有用,但可以看出它如何应用于虚拟现实。
因此,我们可以看到,如果我们让你看到几乎真实的东西,我们可能能够说服大脑它是真实的。然而,简单地将一个视觉屏幕放在我们面前并不是完整的答案。
给予某人深度感知是大部分答案。
这是一个相当复杂的话题,但显示物体深度的主要方法是立体深度感知。还记得那些 ViewMaster 玩具吗?这是一个例子:
你放入一张左眼和右眼图像的光盘。左右眼的图像几乎看起来相同,但它们代表了右眼和左眼在那个位置看到的东西;由于视差,每个图像略有不同。从光盘上,我们可以看到左眼和右眼的图像。前面的 View-Master 中的透镜将你的眼睛聚焦在图像上。
你的大脑会看着这两幅图像,将它们融合成看起来真实的东西。这使用了一种称为立体深度感知的深度感知技术。
是的,View-Master 是早期的虚拟现实观看设备!
现在,这里真正发生了什么?立体声是如何工作的?
当你看东西时,透视和你的眼睛之间的距离会使你在看近处的东西和看远处的东西时以不同的方式聚焦你的眼睛。在这个图示中,黄色线显示我们对近处物体的视线,橙色线显示我们对远处物体的视线。请注意,黄色线之间的角度大于橙色线的狭窄角度。
一个友好的机器人借给我们她眼睛的下半部分来制作这张图片(这就是为什么它显示电路板)。你的真实眼睛构造有些类似;出于说明的目的,我省略了光线以及它们落在眼睛背面的位置。
你的大脑会根据黄线和橙线之间的角度差异自动判断你的眼睛是指向近处还是远处的物体。
这只是我们的大脑用来区分深度的一种方法。另一种对虚拟现实同样至关重要的方法是使用视差。
视差指的是,不仅左右两个安卓眼睛指向不同(就像你的眼睛,当它们连接在你的头上时一样),而且每只眼睛看到同一物体的略微不同的视图。即使你只有一只眼睛,当你把头向左右移动时,这也会起作用,这是单视觉者感知深度的方式之一。
这是你的左眼看到的场景:
这是右眼看到同一物体的方式:
视差指的是,当用另一只眼睛观看时,更远的物体将比附近的物体更少向右/左移动,或者(一个延伸)当你把头从左到右移动时。我们的大脑(以及动物的大脑)会本能地将这些视为更近/更远。
红色的立方体要么在蓝色的立方体旁边,要么在绿色的立方体旁边,这取决于哪只眼睛看到了这张图片。你的大脑会结合这一点,再加上立方体在你左右移动眼睛时的移动方式,来给你一种深度感。
如果你是那个不感知 3D 电影的人群,不要绝望。它们严格依赖立体深度感知,不考虑视差效应;它们是预先录制的。
使用真正的VR(计算机生成或基于光场的 360 度视频),如果你移动头部,你会看到视差效应,VR 看起来就像拥有立体深度感知的人一样真实。
我有单视觉,因为我有一个近视眼和一个远视眼,而且 VR 对我来说效果很好。你的体验可能会有所不同,但如果你不喜欢 3D 电影,可以试试 VR(再说一遍,我真的很喜欢 3D 电影)。
视差深度感知会在你只有一只眼睛时起作用,当你把头从右到左移动时。
你的大脑还有一种额外的方法来确定物体的深度 - 焦点。 (实际上,除了列出的这些方法之外,还有许多其他方法,比如远处物体的蓝色移位,如山脉,以及其他效果)。在现实世界中聚焦于一个物体会使该物体和大致相同距离的其他物体看起来清晰,而更远和更近的物体会显得模糊。有点像这样:
当前的头戴式显示器无法准确显示焦点效果。你看到的是一个小屏幕,通常焦点固定在你前面约 5 英尺处。所有物体,无论远近,看起来都是一样清晰的,因为它们实际上只是显示在屏幕上。这可能会导致轻微的虚拟现实不适,称为住宿-聚合冲突。基本上,如果你聚焦于远处的立方体(鲑鱼色的那个),你的眼睛仍然会聚焦于鲑鱼立方体实际所在的位置,而你的眼球会以立体视觉的方式瞄准它应该在的位置。这种效果在非常近的物体上最为明显。
住宿-聚合冲突在靠近物体时最严重 - 因此尽量不要让任何东西,比如 GUI,位于用户位置太近的地方。这样可以减少晕动病。
这意味着你可能需要将 GUI 元素浮动到房间外,而不是让它们非常靠近。这可能会导致 UI 元素重叠。
虚拟现实设计是具有挑战性的。我期待你的设计!
在虚拟现实中使用立体声和视差
早在 1968 年,伊万·E·萨瑟兰就首次观察到具有立体深度知觉的物体,当用户的头部移动时似乎位于空间中(运动视差),看起来是真实的。
他和鲍勃·斯普劳尔开发的系统,通常被称为“达摩克利斯之剑”,只在空中显示了几条发光的线,然而:
“即使是这种相对粗糙的系统,三维幻觉也是真实的。” -伊万·E·萨瑟兰,AFIPS’68(秋季,第一部分)1968 年 12 月 9-11 日秋季联合计算机会议的论文集,第一部分:bit.ly/2urAV5e
在这种情况下,“真实”意味着尽管缺乏逼真的渲染 - 只是一个发光的立方体 - 人们仍然将其视为真实。这是由于立体渲染和视差效应。人们可以转动头部并稍微左右移动。
他们发明了第一款 VR 头显,或者头戴式显示器(HMD)。
被广泛认为创造虚拟现实术语的人,贾伦·兰尼尔(Jaron Lanier)说:
“这是一种非常有趣的现实。它绝对和物理世界一样共享。有些人说,嗯,物理世界并不那么真实。这是一个共识世界。但问题是,无论物理世界有多真实 - 我们永远无法真正知道 - 虚拟世界也完全一样真实,并且达到了相同的地位。但与此同时,它还具有无限的可能性,这是物理世界所没有的:在物理世界中,你不能突然把这座建筑变成郁金香;这是不可能的。但在虚拟世界中你可以……[虚拟现实]给了我们这种感觉,让我们能够成为没有限制的自己;让我们的想象力变得客观并与其他人共享。” - 贾伦·兰尼尔(Jaron Lanier),SIGGRAPH Panel 1989,虚拟环境与互动性:未来之窗。http://bit.ly/2uIl0ib
一位名叫梅尔·斯莱特的研究人员对这个概念进行了进一步的研究,提出了进一步的术语“存在感”和“可信度”。有些人称这一切为沉浸。屏幕上的 3D 图像不如当你戴上 HMD 时,你的视野只能看到构建的 3D 世界那样引人入胜。即使渲染不像真实世界那样,你也会因音频和视觉线索而产生存在感。可信度意味着你所看到的有规则,并且即使不完全像真实世界那样,也能正常运作。
只能看到 HMD 中的东西,加上视差和立体视图,以及任何音频(如果做得好的话,声音非常重要),都会让你沉浸在 VR 世界中。有了所有这些东西,即使图形不真实,你也会感到沉浸其中,它变得真实起来。有关更多学术细节,请参阅bit.ly/2vGFso0
,尽管我将在本节中对此进行更多解释。
这真的很有效。
虚拟现实不必看起来像现实,但它会看起来像是真实的。例如,看看游戏Quell4D:
图形像积木一样,图像看起来一点也不像现实。然而,当古老的三根象鼻巨树巫师向你走来时,你会感到恐惧。它们看起来很真实。对你来说,当你玩游戏时,它们实际上是致命的真实,这意味着如果你不认真对待它们,你的(游戏中的)人物将会死亡。
虚拟现实中的火焰模拟会让大约 10%的人因恐慌而离开房间,即使火焰看起来一点也不像真正的火焰。
虚拟现实已经出现了。我们不必等到图形变得更好。很多人都这样说虚拟现实,但这是因为他们还没有尝试过,并且正在对它必须是什么做出假设。
跳进去,水很好!
因此,虚拟现实是一种看起来真实的东西,而不是一定看起来真实的东西(但如果看起来真实会更有帮助!)。
你不必等到更好的图形出现。
虚拟现实为什么会起作用,即使它看起来并不 100%真实?
我们的眼睛可能是向我们展示世界构成的最重要的感官。如果我们用这些图像替代图像并让某人沉浸其中,它们会开始变得真实。当你第一次进入虚拟现实时,你的最初反应是,“那看起来并不真实”,但是通过一个良好的虚拟现实设置,你会达到一个程度,你会认为“哇,那是真实的”,即使你知道你看到的基本上是一个电脑游戏。
快速的帧速率(显示速度)和足够的分辨率会欺骗你的大脑,让你认为你所看到的视觉上真的存在。这是一个强大的效果,大多数人在沉浸在这样的图像中时会有,但并非所有人都能看到 3D 电影(即使视力正常的人也不是每个人都能看到)。
事实上,现实感是如此强烈,以至于人们可能因观看 VR 而感到恶心。这是因为你的眼睛可能会说那是真的,但你的其他感官,比如你的内耳,会说我们并没有跳 10 英尺高。如果你的眼睛认为你在空中弹跳,而你的腿部肌肉(本体感觉)说你在地面上,你的皮肤说你感觉不到风,你的内耳说你在向前飞行时没有倾斜,你的大脑会在非常深层次上感到困惑。
当你的感官强烈不一致时,你的身体会有一种防御机制。它会认为你中毒了;因此,你的身体会感到恶心,甚至可能会生病。你的身体担心你的眼睛看到的与身体其他部分感觉到的不一样,所以它可能会试图排除你胃里的一切,以防你吃的东西中毒了。
是的,这并不好玩;不同的人会有不同的反应。
然而,并非所有的 VR 都会产生这种效果!一般来说,制作不良的 VR 会让你有这种感觉。关于这种效果已经有学术论文写成。本书将把这些讨论总结成几条简单的规则,让你的 VR 对人们来说更加舒适。
虚拟现实的另一个重要方面是,它是你可以与之互动的东西(现实本身)。这带来了机械困难;并不是每个人都拥有 3D 控制器。我们在用户代理-通过控制器与世界互动部分进行了讨论。真正的虚拟现实可以进行互动,即使只是简单的凝视检测-看着某物(凝视)然后事情发生-移动发生,你被传送,动画播放。
其他类型的 VR;AR,XR,SR/FR
还有另一种图像,有时被称为 VR,那就是360 视频。有特殊的视频播放器可以记录所有方向。复杂的软件会将不同的摄像头输入拼接在一起,制作成一个视频流,播放软件会将其投影到你周围。当你转头时,你似乎改变了在拍摄的世界内的视角。就好像你在现实世界中,随心所欲地四处张望。
360 视频看起来可能比大多数计算机生成的 VR 更好,但对我来说,它并不是现实,因为在最好的情况下,你只是一个无形的幽灵。当然,世界看起来很棒,但你无法伸手触摸事物,因为它已经被拍摄了。360 视频和类似的系统超出了本书的范围。话虽如此,我认为 360 视频肯定是一种有效的艺术形式,值得追求-只是本书没有涉及。
请理解 - 我并不是贬低 360 度视频,只是因为它不是真正的虚拟现实。(像最后一个词加上引号那样发音)。360 度视频可以是非常温暖、激烈、情感丰富的戏剧。你确实会感受到一丝存在感,视觉效果令人惊叹。随着更多人熟悉它并解决细节问题,我们应该看到更多令人惊叹的艺术作品。
我提出一个新术语用于 360 度视频;拍摄现实(FR)或环绕现实(SR)。(尽管这些实际上没有人用胶片,但“拍摄”一词仍然意味着通过镜头记录某些东西,但也许 SR 更好。你来选择!)
还有其他类型的虚拟现实。有很多人使用短语XR,意思是(任何)现实;主要指增强现实和虚拟现实。什么是增强现实?
头戴式显示器(HMD)由一些小型显示器和复杂的光学器件组成,当你戴上头盔时,可以看到立体的 3D 图像。大多数虚拟现实头盔在你使用时会故意屏蔽其他世界,以进一步让你沉浸在虚拟现实中。虽然这是虚拟现实的一个重要组成部分,但还有一种称为增强现实(AR)的虚拟现实类型,其中虚拟现实物品通过戴一种透明的 HMD 投影到现实世界中。有许多制造商,尽管微软的 Hololens 可能是最知名的。还有游戏Pokemon Go,这是一种增强现实。人们举起手机,显示在现实之上叠加的图像。这不是头戴式显示器,但仍然是增强现实。现实世界已经被 Pokemon 世界增强。
虚拟现实系统也可以是世界之窗系统,尽管今天通常不被称为虚拟现实。换句话说,一个真实的、持久的 3D 世界,你坐在键盘前通过屏幕观看。在上一波虚拟现实浪潮中,这被称为虚拟现实,尽管今天它已经很普遍,人们不再称之为虚拟现实。你可能听说过魔兽世界。
这是一种虚拟现实;虽然它通常不是 3D 的,但它是一个持久存在的世界在另一个现实中。它也是一个完整的 3D 世界,你可以通过屏幕看到;屏幕将你带到一个虚拟现实中,所以它类似于世界之窗系统(尽管没有头部跟踪)。
观看电影可以被视为一种有效的虚拟现实形式;你被带到另一个世界,短暂的时间里,感觉自己沉浸在故事中。电视是一种虚拟现实。
事实上,对 VR 术语的第一次使用是指戏剧。虽然今天许多人会说这不是虚拟现实,但他们大部分时间都在观看其他现实,而不关注坐在他们旁边的人。这难道不是虚拟现实吗?你对《与星共舞》很着迷,但你认识他们吗?他们在虚拟现实中是真实存在的。
然而,这并不是大多数人所认为的。本书将使用现代(2014 年以后)对虚拟现实的解释,即通过 VR 头盔或某种 HMD 查看的东西。如今,VR 这个词通常意味着头盔或 HMD,并且经常与某种形式的手柄配对。现在,对消费者来说,有很多好的、有效的 HMD 可供选择。现在是对 VR 感兴趣的好时机。
WebVR 的好处在于,我们可以在没有头戴式显示器的情况下,通过浏览器仍然可以看到这些虚拟现实世界;这对于测试和没有硬件的人来说非常方便。
WebVR 非常包容。
虚拟现实的历史
大多数人也认为 VR 是相当新的,但实际上它已经存在很长时间了,我指的是传统类型的带头盔的 VR。第一个 HMD 是由伊凡·萨瑟兰和鲍勃·斯普劳尔在 1968 年创建的。由于当时的技术,它又大又重,因此悬挂在研究室的天花板上。它只显示线框图像。由于它的大小,它被称为达摩克利斯之剑。它展示了一个简单的线框世界。当时的计算机速度不够快,无法显示比一些发光线更复杂的东西。
在 90 年代末,个人电脑开始足够快,可以显示 3D 世界,于是出现了新一波的 VR。我参与了这些努力;我当时正在为 CompuServe 开发一个 3D 环境,那时是一个热门的地方。
你可以去商场参与,通过昂贵的 HMD,在线上与最多四个人共享虚拟世界。这被称为基于位置的娱乐,因为这些系统又大又昂贵。如今,你也可以去 VR 游乐场,尝试硬件,但如今 VR 的令人兴奋之处在于,许多这些系统对家庭爱好者来说非常实惠。
用户代理-通过控制器与世界互动
HMD 并不是一切,尽管它确实是最重要的部分。能够看到虚拟现实世界很棒,但在某个时候,你希望能够与之互动。如果世界是静态的,你会感觉自己像一个游荡的幽灵。只有当你能够与世界互动时,才算是虚拟现实。
最终,类似全身触觉反馈和身体跟踪的东西,再加上复杂的软件,将使我们能够伸手触摸虚拟世界。这是未来值得期待的事情。
目前,我们通常通过各种手持控制器与世界互动。不同的控制器具有截然不同的功能和要求。高端(但仍然面向消费者)VR 设置,如 Rift 和 Vive,控制器的工作方式与移动 VR 控制器大不相同。我们将先讨论高端系统,然后再讨论移动 VR 控制器。
PC、Mac 和 Linux 的高端控制器
对于 PC VR,比如 HTC Vive 或 Oculus Rift,控制器给予了与虚拟现实世界互动的非常重要的能力。这些控制器在 3D 空间中被跟踪,以便软件知道它们的位置。开发人员可以编写代码,使其看起来像手、枪等。这使你能够伸手触摸周围的世界——这对于使你与之互动的虚拟现实非常重要。
为了实现这一点,Oculus 和 Vive 控制器都需要外部跟踪硬件。对于 Vive 来说,这些是放置在 VR 区域角落的光线或 VR 基站。(这里有一个图表,可以在bit.ly/VIVEManual
找到)。这些小巧的不显眼的立方体发出红外跟踪信号,控制器和头戴设备接收并用于准确定位它们在真实的 3D 世界中的位置。对于 Rift,还有两到三个传感器来跟踪设备,以给出它们在真实世界中的位置:
基站和跟踪硬件对于 HMD 本身也非常重要。
你真实世界位置的跟踪(你的真实头部/手)是使移动、转动头部、移动手/控制器看起来真实的原因,因为头戴设备和控制器在真实的 3D 空间中被如此精确地跟踪,一旦软件向用户显示了 VR 世界,任何头部运动都会看起来真实。
实际上,这意味着 PC 控制器看起来就在你看到它们的地方。我在科技演示中第一次体验 HTC Vive 时感到惊讶——我戴上 HTC Vive 头盔,在虚拟世界中看到控制器在我面前。我以为我会摸索一番,直到控制器在我想象的位置。我伸出手,我的手指确切地感觉到控制器在我眼睛看到的地方——通过 HMD。
我着迷了!虚拟世界真的是虚拟现实!我看到的幻影控制器真实,尽管我知道我看到的是我脸前的一个小屏幕。
它们是如何工作的?
HTC Vive
HTC Vive 使用两个小方块,称为基站或灯塔,位于它们覆盖的区域的对立面。它们发出红外线束,覆盖 120 度;这意味着如果它们在一个角落,它们可以离角落几英寸远,仍然覆盖墙壁(否则,你就得在墙上挖个洞,把灯塔放在正确的位置!)
通常,你会在房间的对立面安装两个基站,相距约 16 英尺或 5 米,并且在头顶高度上方,大约 2 米或 6 英尺 6 英寸。如果你个子更高,也可以安装得更高!
基站也可以用合适的适配器或自定义支架安装在麦克风架上。并不是每个人都有一个大客厅,所以这些安排可能有助于安装。
Vive 也可以以坐姿配置使用,尽管真正的重点是所谓的Room Scale。
Room Scale VR 意味着你可以在虚拟现实世界中四处走动,就像在现实世界中一样。不需要传送或其他技巧。当然,区域需要清空家具,这是虚拟现实的一个问题;并不是每个人都有一个可以清空的大房间。
如果你离得太近,Vive 会通过显示边界或保护来保护你的安全。
如果是墙或其他区域,使你的房间边界略小于实际房间。如果是沙发或椅子,你可以走到椅子的尽头。
我们这样做是为了避免你撞到墙。如果你站在墙边,但在虚拟世界中看不见墙,挥动手臂,你的手不会穿过真实的墙!
靠沙发边缘是不错的,因为你的小腿会碰到沙发,而不是你的手碰到墙。实际上这并不是什么大问题,因为你会在靠近之前看到防护装置。注意 Vive/Steam VR 教程!
HTC Vive 通过一些惯性测量单元(IMUs)工作,这些单元可以检测 HMD 以及控制器的位置。这些 IMU 会漂移,所以基站有一个红外线束在房间里扫过。当控制器、跟踪器或 HMD 检测到这些光束时,它们会重新校准自己。这种重新校准是完全不可察觉的。这种系统的优势在于,即使一个控制器从一个基站或光房的视线中消失,VR 系统仍然知道该物品的位置和指向。
总体效果是精准和存在感,尽管主要效果是稳定性。如果你交叉双手,一个控制器短暂地从基站的视线中消失,控制器不会失去锁定。
尽量不要把你的 VR 空间放在有很多窗户或镜子的地方。
红外线束可以反射,导致不稳定。
Oculus Rift
Rift最初只是一个头盔,没有控制器。它最初的基站是两个摄像头,你可以把它们放在桌子的左右两侧;它们指向 HMD,并用于将其定位在世界中。
不久之后,Rift 增加了第三个摄像头的功能;有了三个摄像头,你可以进行房间规模的 VR。它们的位置略有不同于 Vive;查看 Rift 文档以获得最佳位置。
小心处理电缆。当我写这本书时,Rift 的电缆直接插在 PC 的后面。如果你绊倒电缆,可能会猛烈地把它们从 PC 上拔出来,导致损坏。
Vive 有一个分机箱,所以如果你绊倒电缆,希望你能把它从盒子里拔出来。
不要绊倒电缆。
这本书的目的不是分析 Vive 或 Rift 比另一个好还是差;它们都大致以相同的方式工作,基站/摄像头帮助控制器和 HMD 跟踪它们的位置和旋转。以下是一个典型的设置:
在其中,Vive 基站安装在墙上;我们有一台台式电脑和一个 VR 用户观看一个 3D 模型,就像它是真实的一样。VR 用户手持两个 Vive 控制器;虚拟图像手持一个类似 Xbox 的游戏控制器。
这张图片还展示了 Oculus Rift 3 摄像头跟踪系统。它们是坐在屏幕左右两侧的浅灰色物品,以及沙发后面的橱柜上(就在我们面前)。
没错,这个人是使用者。她不需要 HMD;系统直接将视频传输到她的眼睛。虚拟对象是一个看起来坐在桌子旁的人。
前面的图是沙发前面的第三人可能看到的场景。
灯塔看到的实际上有点不同,但很有趣。它们实际上有一对红外线条,横扫视野,控制器看到这些线条在跟踪。当他们这样做时,控制器(和 HMD)将重新同步它们的惯性跟踪定位。这意味着即使一个控制器不在基站的视野范围内,它仍然保持跟踪,尽管你不希望控制器长时间隐藏。惯性跟踪系统会漂移。漂移的视觉问题是你的手臂似乎慢慢远离你的身体 - 这显然是非常令人不安的。Vive 灯塔和 Rift 摄像头可以防止漂移发生。Vive 灯塔投射的视角约为 120 度。如果灯塔有这个视野的摄像头,右后方的灯塔将看到以下内容:
你可以通过这个灯塔看到两个控制器和 HMD。然而,有一个问题。注意红色圆圈 - 左侧的大镜子实际上是一个巨大的电视,但它很闪亮。因此,灯塔红外线会反射在上面,控制器会感应到两束光:一束直接,一束反射。
这可能会导致 HMD 和你的视角跳动,或者你的控制器莫名其妙地移动。
在你的 VR 房间里避免闪亮的物体、镜子和窗户。
你可能需要拉上窗帘,甚至在电视、玻璃橱柜等上面铺上床单。
艺术需要牺牲!
从另一个灯塔,一个控制器被阻挡,但仍然通过其内部惯性跟踪和另一个灯塔 100%跟踪。
移动 VR
对于移动 VR,还有 Google Daydream 和三星 Gear VR 控制器。由于使用了更简单的硬件,使价格更合理,这些控制器并非完全 3D 跟踪。
由于移动 VR 缺少房间跟踪外部传感器,Vive 和 Oculus 都有,VR 控制器的跟踪就不那么精确。实际上它们看起来会一样真实,但会周期性地漂移。就好像你的手在没有你控制的情况下慢慢向右移动。因此,移动 VR 有一个重置控制器的按钮,可以将控制器移动到预定义的位置,比如靠近你的臀部。你的手可能是伸直的,但如果你按下 Home 按钮,VR 显示将会显示你的手现在在你的臀部。
这可能需要一些时间来适应。这种设置有一些优势;它更便宜,需要更少的外部硬件,而且世界上有更多这样的系统。然而,PC 硬件确实提供了更好的 VR 体验。
移动控制器的另一个问题是只有三个自由度(DOF)。这意味着它们可以跟踪倾斜、偏航和翻滚,但不能跟踪位置;如果你把控制器平放在左边,在游戏中你的控制器根本没有移动。这就是为什么你不能用移动控制器抓东西。Vive 和 Rift 都有 6 个 DOF 控制器,所以你可以移动它们并抓取东西。
渲染硬件
为了避免 VR 晕动病,你需要一个快速的帧速率。什么是帧速率?这是你的电脑生成屏幕上图像的速度。当然,很多事情取决于场景的复杂性;显示一个立方体和一个盒子比显示洛杉矶市所有建筑物的速度要快得多。
当然,你在设计 VR 世界时可以控制这一点。
每个图像都必须实时生成。大多数 VR 头显都尝试达到 90 赫兹。赫兹指的是频率-每秒循环次数,或者在这种情况下,每秒帧数。
VR 的难点在于没有什么可以减慢这个帧速率。如果有什么东西需要加载,或者需要获取一个网页,如果你稍微减慢帧速率,人们会感到头晕。
加快帧速率有两种方法。一种是减少场景复杂度,另一种是使用快速的电脑。
电影《大白鲨》中的经典台词是当他们发现鲨鱼比他们预期的要大得多,并且撕毁了他们的船时。罗伊·施耐德说:“你需要一艘更大的船。”
观看 VR,你需要一台更大的电脑。
幸运的是,电脑变得越来越快。在这里我们构建的世界中,一个相当快的智能手机应该没问题。
场景复杂性有点两难;您希望拥有丰富、详细的虚拟世界,但您也希望该虚拟世界能够快速渲染。快速渲染意味着每秒 90 帧(更新),如前所述。您还需要了解硬件支持的目标受众。他们是否都使用配备数千美元视频卡的高端 PC?(有点过度;我在这里表达一个观点。)还是他们使用去年的手机型号,配备 10 美元的硬纸板盒和一些透镜?如果您了解潜在的目标受众,您可以开发一个能够在其系统上良好运行的 VR 应用程序。
美国海军陆战队有一句话:“训练就像你将要战斗一样。”在二战期间,他们练习了在南加利福尼亚海岸进行两栖登陆的作战行动。当他们在太平洋战争期间不得不执行这项任务时,他们没有计划过珊瑚礁。因此,他们制定了一个原则,即应该在与他们预期要在其中作战的相同或相似的环境中训练人员。
虽然良好的 VR 体验(希望)并非生死攸关,但这仍然是宝贵的建议。如果您认为大多数客户或您的 VR 应用程序的消费者将使用去年的手机,那么请使用去年的手机进行测试。如果您认为他们将使用高端 PC,请使用高端 PC 进行测试。
不要假设,如果您的 VR 应用程序运行缓慢,客户的计算机会好得多,一切都会没问题。获取与他们使用的类似的设备,然后您将在客户之前遭受恶心和眩晕,然后重新编码或简化场景以使其足够快速。
需要多少硬件?为此,您应该咨询您计划针对的头戴式显示器的最低规格。由于这可能会发生变化,我不会在本书中总结它,但不同 VR 制造商提供的指南是很好的建议。
您可能需要更大的 PC(或手机);这是成为早期采用者所付出的代价!
如何查看 VR?
要查看 VR,您需要某种类型的头戴式显示器或 HMD。在过去,VR 还以 2D 屏幕上的 3D 图像为特征。实际上,当时的 VR 意味着使用任何设备查看的任何 3D 程序 - 基本上就像正常情况下坐在 PC 前一样,但这并不是真正沉浸式的体验。今天,VR 意味着使用 HMD/头戴式显示器;因此,要查看 VR,您需要一个头戴式显示器。
具有讽刺意味的是,React VR 在浏览器中作为一个 3D 世界也可以正常运行,并且可以用来制作具有视差效果的网页,尽管这有点过度。
VR 可能是危险的
你可能会认为,这很安全。然而,一个 VR 头戴设备附带了 33 页的警告。请阅读它们。
大多数警告都是常识,例如,如果你在靠近物体或人的地方,不要挥舞你的手。戴上这种类似眼罩的东西,你可能会真的打到自己的手。从哲学上讲,我不相信有人会过分干涉,但你确实可能会在 VR 中受伤。想象一下,如果有人给了你一个眼罩,告诉你戴上它,然后在你家里四处走动。你可能会感到有点不舒服。
这大致是我们在这本书中要做的事情,只是增加了一个你将在一种惊奇和兴奋的状态中四处游荡的细节。有很多 YouTube 视频显示人们撞墙,撞到墙上,打翻灯具等等。他们看起来很傻,但当你戴上头戴式显示器时,你完全沉浸在虚拟世界中,不会想要控制自己的力量。因此,请确保你清空房间,并警告朋友不要进来。
这也包括你的毛茸茸的朋友。很难让宠物远离你的 VR 区域,但这是个好主意,因为它们不会明白 HMD 让你分心,而且你看不见它们。如果可以找到方法,最好防止它们在你脚下乱跑,否则你可能会无意中踩到它们。
VR 是安全的;请负责任地使用。
VR 头戴设备选项
在 WebVR 中,有一些选择。我将保持这些最简单、最常用的头戴设备。你当然可以使用开源虚拟现实(OSVR),这实际上是一个硬件平台,但你需要弄清楚要使用什么浏览器等等。一些术语,比如凝视,稍后将在 UI 部分介绍。目前,凝视移动意味着你需要盯着某物才能移动到那里或选择一个对象,通常是这样的。
以下是各种主流的 WebVR 选项(您可以在webvr.info/
上阅读相关信息):
类型 | 控制 | 移动 | 成本 |
---|---|---|---|
Gear VR(移动) | 1 个手持设备,头戴式显示器 | 凝视/触摸板 | 中等 |
Daydream VR(移动) | 1 个手持设备 | 凝视/触摸板 | 中等 |
Cardboard/其他头戴设备 | 无(可以点击) | 凝视选择 | 低 |
HTC Vive | 跟踪,2 个控制器 | 四处走动 | 高 |
Oculus Rift 2 摄像头 | 键盘/游戏手柄 | 凝视选择 | 高 |
Oculus Rift 3 摄像头 | 跟踪,2 个控制器 | 可四处走动 | 高 |
头盔类型
广义上来说,它们可以是连接 PC 的头盔或移动耳机。一些独立式头盔,如 Hololens 或 Vive 独立式 VR 头盔,包括一个完全工作的 PC,因此它们实际上更像是移动耳机,但不需要 PC。
移动耳机
移动耳机实际上只是利用你的手机来显示数据并将你置身于 VR 世界。因此,性能完全取决于你的手机能做什么。
这是一个时候,更大真的更好。
不过有一个限制;有一些头盔使用平板电脑,但它们非常重,并且实际上没有比较小的移动设备更多的优势。
当你使用移动耳机时,你会遇到电池寿命、重量和控制问题。市场上有各种各样的 VR 控制器,还有捆绑选项,如三星 Gear VR 和 Google Daydream,其中包括一个控制器和一个手机壳。
这些捆绑包的好处是,手机通常经过认证可以正常工作,软件也很容易使用。你可以自己组装 VR 头盔/控制器组合。
移动耳机也可以简单到只是一个带有一些镜片的盒子,尽管在光学尺寸和细节方面实际上有很多数学。最常提到的是 Google Cardboard;谷歌不直接销售它们,但公司可以实现 Cardboard 观看器。还有非官方的 Cardboard 以及一些价格合理的更好的支架,可以放入手机。
一般来说,它们大多没有传感器。一些有一个小杠杆会触摸屏幕,除了移动之外还允许一些控制。
你也可以购买单独的蓝牙控制器,尽管它们很可能没有三维定位。我们在第十一章中介绍了不同类型的控制器,走进未知领域。
一些 VR 头盔适合戴眼镜,而一些则不适合——很大程度上取决于你的脸部大小、你使用的眼镜大小以及你的视力问题。我一个近视眼和一个远视眼,都不需要戴眼镜(任何一只眼睛都不需要!),但你的情况可能不同。我强烈建议在购买前试戴一下头盔,或者从有良好退货政策的商家购买。
在移动头戴式耳机的高端产品中,有三星 Gear VR 和 Google Daydream。它们提供了一个精心制作的头戴式耳机,你可以把手机放进去,还有一个独立的控制器。
控制器是最重要的部分(尽管头戴式耳机也很值得,因为它们比最好的 Cardboard 观看器舒适得多)。Daydream 和 Gear VR 捆绑包中的控制器都是蓝牙无线的,并且可以进行一定程度的跟踪。
它们内置了传感器来检测运动,但在空间中并不是精确定位的。因此,它们上面有中心按钮。这是因为这些单位中的三维定位传感器类型会随着时间漂移。在虚拟现实世界中,你的手/控制器/枪(或者控制器的任何视觉表示)似乎会从你身边漂移,甚至移动到你的身后!这可能会让人感到非常不安。如果/当这种情况发生时,只需使用适当的按钮重新校准你的控制器。
高端 PC 设置有不同类型的跟踪,通常不需要重新校准。但是,请注意它们需要初始校准/设置,并且也可能出现跟踪问题。
关于 GearVR 的一些注意事项。我在使用 GearVR 时犯了一些小错误。有一个额外的弹性带,我以为是用来松弛的;其实不是。他们告诉你要戴上这些带子,但忽略了这个额外的带子是用来固定控制器的。在戴上头戴式耳机的带子之前,先翻到控制器部分。
实际上,控制器应该是你首先要摆弄的部分。你需要配对它并进行一些下载,而这在戴着头戴式耳机时是做不了的,所以先做这部分。
PC、Mac 和 Linux 头戴式耳机
大多数人认为选择 PC 头戴式耳机将在 HTC Vive 和 Oculus Rift 之间进行,但实际上有数十甚至数百种 PC 类型的头戴式耳机。
它们的性能都取决于你的 PC 的性能。朋友们,这就是 Mac 有点劣势的地方;你需要一张快速的显卡,而 Mac 通常对于图形和一些游戏来说速度足够快,但不适合虚拟现实。然而,苹果已经推出了准备好虚拟现实的 PC。当你决定用哪个平台进行 React VR 时,请考虑这一点。
就目前而言,Mac 对 Oculus Rift 或 HTC Vive 的支持充其量是试验性的,因此这些步骤和示例将假定您正在使用 PC。Linux 对几款头戴设备承诺提供支持,但就目前而言,这是试验性的。如果您使用 Linux,您需要查阅文档和/或尽量按照 Windows 示例进行操作。
大多数 React VR 演示的几何图形比许多虚拟现实世界简单,因此它们可以在相对较小的硬件上运行。请查看您头戴设备的制造商的最低要求;不要认为您可以用低于最低要求的硬件。否则您会感到不适或体验不舒适。
在整个市场中,我们将在本书中介绍的头戴设备实际上只有两款:HTC Vive 和 Oculus Rift。如果您有其他头戴设备,示例应该可以正常运行,但您可能需要稍微调整一下。
一般来说,PC、Mac 和 Linux 头戴设备将与 Firefox 或实验性浏览器Servo.org兼容。实验性版本的 Chrome(Chromium)也可能可以用于查看 WebVR。请在webvr.info上查看完整的最新列表。
总结
在本章中,我们已经涵盖了虚拟现实,为什么它有效,以及它真正是什么(故意的双关语)。我们还介绍了如何通过简要概述虚拟现实硬件和软件进入虚拟现实世界。
请记住,即使您没有昂贵的 HTC Vive 或 Oculus Rift,您仍然可以在台式电脑上查看 WebVR。
接下来,我们将介绍如何在非常高的层面上编程虚拟现实。有许多不同的构建虚拟现实软件应用程序的方法,我们将介绍这些不同的方法,以及它们的优缺点。您将阅读有关不同软件包的概述,以及 WebVR 的优势。由于本书是关于 WebVR 的,我们将介绍安装 React VR、Node.js 和其他工具,开始创造您自己的现实 - 真的!
第二章:Flatland and Beyond: VR 编程
在上一章中,您了解了 VR 是什么以及它可以成为什么样子。程序员和开发人员(就像你)是如何创建这些虚拟世界的?我们发现这是一件难事。我们必须保持快速的帧率和正确的立体渲染。我们如何快速而轻松地做到这一点?继续阅读,找出答案。
在本章中,我们将涵盖以下主题:
-
HTML 和常见的编程方法,如 Node.js,JavaScript 和游戏引擎
-
React 库
-
图形库,用于显示 2D 和 3D 图像
-
如何安装所有这些软件,以便我们可以开始编程
HTML 和超越 2D 互联网的方法
在 Web 发展的同时,早期的 HTML 语言发生了巨大变化。一个良好的网页体验通常不仅仅涉及 HTML。增加更多互动性的一种方式是通过 JavaScript。HTML、XML 和 JavaScript 的组合是构建 Web 的重要部分,包括 Google 文档或在线 Microsoft Word 等应用程序(也是免费的)。
然而,这些都是平面的。要进入第三维度通常需要高速软件,通常是用 C++编写的。随着计算机变得越来越快,图形处理单元(GPU)已经接管了实际 3D 生成的大部分工作,用于描述 3D 游戏的语言也在不断发展。
目前有许多编程 VR 的方法。在 Rift 和 Vive 显示的分辨率下生成每秒 90 帧是具有挑战性的,因此大多数 VR 编程是用高速语言进行的,这些语言直接面向硬件或低级,如 C 和 C++。然而,游戏引擎,如 Unity、Unreal 或 Cryengine,可以为你做很多工作。
起初,你可能会想“为什么我要使用游戏引擎?我不是在写游戏”。更普遍地说,这些引擎是为游戏而构建的,但不一定只能构建游戏。现代游戏引擎处理渲染(我们需要的)、物理学(我们需要这个来构建逼真的世界)、地形(用于户外场景)、照明(用于复杂的渲染)、人工智能(用于填充我们的世界)、网络(用于构建多用户环境)和其他代码。这些都不一定是游戏特定的,尽管各种游戏引擎都有更适合游戏而不是企业数据可视化的命名约定。例如,在 Unity 中,一个基本的 3D 对象被称为GameObject
。所以即使你不是在写游戏,你也会有GameObject
。
目前,VR 软件的主要竞争者是:
-
Unity(由 Unity3D 制作,更多信息请访问
bit.ly/UnityForVR
) -
Unreal(由 Epic Games 制作,制作了虚幻竞技场;更多信息请访问
bit.ly/UnrealForVR
) -
Cryengine(由 Crytek 制作,制作了游戏 Crysis;更多信息请访问
bit.ly/CrytekForVR
) -
Lumberyard(由亚马逊制作;更多信息请访问
bit.ly/LumberyardForVR
)
许多这些游戏引擎也适用于移动平台。使用游戏引擎的优势在于你可以“一次编写,到处运行”,这意味着大多数游戏引擎都支持移动设备和 PC。基本上,你构建一个 PC 应用程序,然后更改构建设置并构建移动应用程序。现在你有了每个平台的两个或更多不同的应用程序。
使用游戏引擎可能存在相当陡峭的学习曲线,尽管这仍然比编写自己的渲染代码要容易。你确实需要构建一个完整的应用程序,这可能令人望而生畏。
与当前的网络编程技术相比,人们只想描述他们想要看到的东西,而不是编写服务器端代码将网页发送到手机,也不是编写自定义应用程序来下载信息并显示它。
那么,为什么 VR 需要你这样做呢?
使用 React VR,你不需要这样做。
你可以用 JavaScript 构建你的世界,而不是学习游戏编程引擎。你可以使用声明性组件构建 VR 世界和 UI,而不是构建渲染代码。实际上,你可以在更高的层次上描述你的 VR 世界中有什么,而不是一次一个像素地构建这个世界。这听起来不是更有趣吗?
Node.js 和 JavaScript 的背景
大声说出 Node.js。祝你健康!
Node.js 是一个开源系统,用于在服务器端使用 JavaScript。当然,这是 Web 浏览器执行代码的主要方式。它是在 Web 早期的时候发明的,有几个原因。
React 和 React VR 大量使用 JavaScript。要将 React 网页呈现到浏览器,需要服务器端 JavaScript,这意味着 Web 服务器不仅仅是将文件发送到浏览器,而是在服务器端执行代码。Node.js 允许您使用与浏览器相同的语言编写服务器端代码。对于全栈开发人员来说,这是理想的,因为您可以沉浸在一种语言中。
使服务器 React
React VR 基于 React,这是一个允许通过声明而不是编程构建 Web 页面和交互式用户界面的框架。您为应用程序中的每个状态构建视图,然后 React 将使用正确的组件来显示该应用程序。
声明式视图使您的代码更容易、更健壮,更容易修改和调试。
组件使用了封装的面向对象概念,这意味着它们是自给自足的,并管理自己的状态。然后,您可以使用这些组件来创建复杂的用户界面。
React 允许开发人员创建随时间变化的应用程序,而无需不断刷新浏览器页面。它使用了模型-视图-控制器设计模式/模板,并且可以与其他 JavaScript 库(如 Angular.JS)结合使用。
React 首次在 2011 年与 Facebook 的新闻订阅中使用。它在 2015 年 3 月开源。
您可以在facebook.github.io/react/
找到有关 React 的更多详细信息。
图形库-OpenGL 和 WebGL
本节涵盖了一般的 3D 编程,但需要讨论一些不同的事情。
OpenGL是一种显示图形的标准。不涉及 PC 与工作站政治(现在已经是古老的历史),它是一个工作站供应商(SGI)开创的标准,用于标准化计算机图形和程序显示图形的能力。
还有其他 API,比如由微软支持的 DirectX,许多 PC 游戏开发人员、CAD 软件和其他 PC 计算机图形都在使用。
OpenGL 严格来说不是开源的;然而,该软件可以在不支付版税的情况下使用,并且有文档和免费提供(公平地说,DirectX 也是如此)。
基本上,OpenGL 是软件显示图形的一种方式。在这种情况下,软件通常指的是 C++(或其他可以调用本地库和操作系统实用程序的语言)。
Vulkan 基本上是 OpenGL 的预期继任者。它比 OpenGL 更低级,并且提供了更多的并行任务处理能力,并直接利用大多数智能手机和 PC 中的 GPU 的能力。由于它是一种低级格式,你会听到更多关于 Vulkan 在计算机图形方面的讨论,而在 Web 图形方面的讨论较少。与 OpenGL 一样,它通常由编译的本地模式软件(C++等)使用。
WebGL是一个 JavaScript API,用于在 Web 浏览器中渲染 3D 图形,无需插件。由于 OpenGL 的创建者 SGI 已经不再经营业务,现在由 Khronos 集团支持、定义和推广 OpenGL 和 WebGL,这是一个非盈利的、由成员资助的联盟。WebGL 可以通过 JavaScript 或其他浏览器支持的语言使用。
three.js是一系列使 WebGL 编程更容易的 JavaScript 文件。然而,它是一个相当大的下载。
React VR 是基于 three.JS 和 React 构建的。
A-Frame是另一个 WebGL 前端;它与 React VR 有类似的概念,意味着它是声明性的、高级的,并且基于 three.js 构建。你不必创建点并将它们连接起来以制作一个立方体;你只需声明一个立方体并给它一个位置、颜色等。虽然本书主要涵盖了 React VR,但它们之间有一些区别。
-
React VR 应用程序是用JSX编写的。这是一种允许类似 HTML 的标记混合到 JavaScript 代码中的语法。React VR 基于 React 和 React Native。如果你已经了解 React,你可以很快学会 React VR,底层概念是相同的,所以你会感觉很自然。
-
A-Frame 应用程序使用 HTML,带有自定义 HTML 标记。它是一个强大的框架,为 three.js 提供了一种声明性、可组合、可重用的实体-组件结构。A-Frame 可以从 HTML 中使用,尽管开发人员仍然可以访问 JavaScript、DOM API、three.js、WebVR 和 WebGL。
-
它们都允许自定义 JavaScript 代码并直接与 three.js 和 WebGL 进行交互。
但为什么要做出决定呢?你不必这样做。你可以同时使用两者。让我们安装 React VR。
安装 Node.js 和 React VR。
目前大多数桌面 VR 硬件都使用 Windows;因此,以下说明,实际上本书的大部分内容将是 Windows 安装和 GearVR 观看的混合。在撰写本书时,Linux 可以被黑客入侵以适配 HTC Vive 和 Oculus Rift,但这是一条艰难的道路,超出了本书的范围。苹果电脑刚刚具备了添加外部显卡以进行 VR 的功能,因为它们中的大多数根本没有足够的视频处理能力来渲染 Vive 和 Rift 头戴设备使用的分辨率。
然而,React 并不特定于 PC。您可以使用 Linux 或 Mac 构建本书中的所有示例,并跟随使用 Google Daydream、非官方 Cardboard 或三星 Gear VR 来查看所有示例。在这种情况下,一些示例可能使用略有不同的语法。我写这本书是为了让大多数使用 Vive 和 Rift 的用户能够跟上,对于其他用户的平台限制,我在此提前致歉。
为什么我们不能和睦相处呢?
在可能的情况下,我会包含其他平台的链接和信息。
安装 Node.js
我们假设您知道自己所在的平台,并且有一台能够安装 Node.js 和 React VR 的计算机(台式 PC)。
首先,我们需要安装 Node.js。如果您已经安装了,那很好,只需确保(在撰写本书时)您至少拥有 4.0 版本。本书是使用 LTS 版本:v6.11.0 和 v8.5.0 构建的,这应该是稳定的,并且在您阅读本书时已经发布。(包括npm
3.10.10)
- 您应该能够从以下网址获取 Node.js:
nodejs.org/en/download/
。下载 64 位的.msi
预构建安装程序文件。下载完成后,双击该文件或根据您的浏览器运行它。
我们生活在一个保姆国家,所以它会警告你正在下载本地代码。再说,人们经常被利用钓鱼攻击(病毒邮件让你自己感染)而受害。这个应该是安全的。
只有在您下载的程序没有经过数字签名时才会出现这些警告。数字代码签名证书并不难获得;坚持要求公司和非营利组织对其代码进行签名。
这将使互联网更安全。
对于您发布的任何代码都要这样做。
-
点击“下一步”。
-
我知道,但你真的应该阅读条款和条件——同意并点击“下一步”。
-
默认位置没问题。谢天谢地,我们不必担心 Linux 无法处理文件名中的空格(开个玩笑,针对所有 Linux 用户)。
-
大多数安装选项都没问题。它们不需要太多空间,所以安装一切都没问题。
-
点击“安装”。
-
您可能(应该)会收到一条警告,提示正在安装一款软件;告诉 Windows 没问题。
-
您已经完成了!现在安装其余的 React VR。点击“完成”。
Mac 下的 Node.js:
Node.js 组织建议使用 Homebrew:brew.sh/
。
但是,您也可以通过 Node.js 下载页面安装 Node.js:nodejs.org/en/download/
。
安装应该很简单。
Linux 下的 Node.js:
虽然源代码在这里:nodejs.org/en/download/,
您可以从软件包管理器更轻松地下载 Node.js;说明在这里:nodejs.org/en/download/package-manager/.
安装应该很简单;这是 Linux,我相信你可以处理任何问题。
Node.js 安装后 — 安装 React VR
Node.js 软件包括一个名为npm
的软件包管理器。软件包管理器安装软件以及该软件的依赖项。您将使用它来安装 React VR。它使安装变得非常简单和及时更新。无论您使用哪种平台,您都需要打开命令提示符才能使用本书中的大多数示例。命令提示符是一个被错误地称为 DOS 的窗口。在 Windows 中,这被称为 Node.js命令行界面(CLI)工具,尽管实际标题是 Node.js 命令提示符。Node.js 安装程序在安装时设置了这一点。您应该使用安装时的 CLI,因为它设置了某些环境变量等。话虽如此,我使用了一种叫做Take Command Console(TCC)的替代命令行工具,在 Node.js 进行了安装并注册了路径变量(安装的一部分)后,我可以从我的 TCC shell 运行npm
和其他命令。
安装步骤如下:
-
打开您首选的 CLI(开始 |
Node.js 命令提示符
) -
输入以下命令:
npm install -g react-vr-cli
您可以从任何位置(文件夹)执行此操作,软件包管理器(npm
命令)将安装以下内容:
如果你第二次运行这个程序,好处是它会确认已经存在的内容(尽管像很多开源程序一样,它有点简洁)。
npm
还有很多其他非常有用的选项。例如,你可以使用npm ls
,它会(就像 linux 中的ls
一样)给你列出所有已安装的对象。你可以通过运行命令npm help npm
来获得详尽的文档,它会打开一个网页。
- 然后,我们想使用相同的 CLI 来安装
WelcomeToVR
示例。首先,进入一个你想要安装示例和代码的地方(文件夹/目录)。我安装了第二个大硬盘作为 F:(你的里程、平台和磁盘配置会有所不同)。所以,在我开始在桌面或我的文档上安装所有东西之前,我切换到了我的数据驱动器:
f:
mkdir f: 2;reactVR
cd \reactVR
- 然后,我继续使用 ReactCLI 安装
WelcomeToVR
演示:
f:\reactVR>react-vr init WelcomeToVR
进程将开始:
这将需要一些时间。在过程结束时,它会完成并告诉你接下来该做什么:
- 然后,进入工具刚刚创建的
WelcomeToVR
项目目录,并初始化/启动本地开发服务器:
cd WelcomeToVR
npm start
这个过程会花一些时间。在运行时,这个命令行界面窗口将忙于运行程序。它不是一个服务。如果你关闭窗口,它会停止。所以,不要关闭窗口。
这个窗口还会在你访问各种网页时显示有用的状态信息:
- 然后,从你的桌面打开浏览器到
http://localhost:8081/vr/index.html
,就像 CLI 提示的那样。你完成了!
有一个比在 CLI 窗口中打开网页浏览器并输入 URL 更容易的方法。你应该打开快速编辑模式。以下是显示这一过程的截图:
-
点击 CLI 窗口角落的小 C:\窗口。这被称为系统菜单:
-
完成后,点击属性。在属性中,打开快速编辑模式:
-
点击确定。现在,快速编辑模式已打开,你可以在窗口中高亮文本并按Enter进行选择。然后,你可以直接将 URL 粘贴到你的 WebVR 浏览器中。简单!
-
如果像我一样,您喜欢使用与 COMMAND.COM 不同的 CLI(我使用 4nt 或 TCC),假设默认安装如我们讨论的那样,您只需将以下内容添加到您的
path
中,假设您将 Node.js 安装在默认位置:C:\Users\<user>\AppData\Roaming\npm;C:\Program Files\nodejs
。
安装 WebVR 浏览器
现在您已经安装了服务器端软件,您需要安装一个能够显示 WebGL、OpenGL 和 WebVR 的 Web 浏览器。这些内容会不断变化,所以我强烈建议去 WebVR 并检查他们的兼容性列表。
Firefox,或者实验性的 Firefox Nightly 可能是最容易使用的浏览器。更多信息,请参考bit.ly/WebVRInfo.
好消息是,从 Firefox 55 版本开始,常规的 WebVR 支持已经内置到 Firefox 中,所以只需确保您的 Firefox 是最新的,您就可以查看 WebVR。要查看您刚生成的新的 VR 网站,您需要执行以下步骤:
-
确保您的浏览器可以运行 JavaScript。这是默认设置,除非您以安全意识的方式锁定了浏览器(这是一件好事)。WebVR 广泛使用 JavaScript。您还可以将本地主机添加到白名单中。
-
一旦您打开了支持 WebVR 的浏览器(在 PC 上,基本上是 Chromium、Firefox Nightly 或 IE),您将看到 hello。但是,您还没有进入 VR!您需要点击在 VR 中查看。您的 VR 应用程序应该启动。然后您可以戴上头盔,您将看到一个简单的 hello。没有世界?您已经进入了一个 VR 世界!
-
您将看到一个链接,上面写着在 VR 中查看。在您点击此链接之前(在您的常规桌面上),您的 Vive/Oculus 将无法工作。
-
一旦您点击在 VR 中查看,并戴上头盔,您将在您的 VR 世界中看到 hello。
恭喜!您已经建立了您的第一个 VR 世界。我敢打赌这比学习 Unity 要快得多。
您也可以使用移动 VR 进行查看,但您需要找出开发机器的 IP 地址,并从移动设备访问该网站,类似于:http://192.168.1.100/vr
。
在 URL http://localhost:8081/vr/index.html
中,您将localhost
替换为服务器的 IP 地址(您的桌面开发 PC)。
请注意,这几乎肯定不是正确的 IP 地址,您需要从开发机器/服务器获取 IP 地址,并将其输入到移动 VR 头戴式显示器中。对于 Windows,可以转到网络属性,或者从 CLI 中输入ipconfig
。如果您的台式 PC 是 192.168.0.100,那么从移动头戴式显示器中,您可以通过虚拟键盘输入http://192.168.0.100:8081/vr/index.html
。
清洁您的移动屏幕
如果不清洁屏幕,任何规格、指纹或污垢都会清晰地显示出来。这些规格会分散注意力,因为它们似乎悬浮在一切之前。
恭喜!您已经运行并查看了您的第一个 React VR 世界!
现在,如果您没有头戴式显示器,或者厌倦了频繁戴上和摘下头戴式显示器,并不得不走出您的房间范围以返回到您的 PC,有一个快速的预览世界的方法。在 Firefox Nightly 中,您只需点击小雷达显示器,屏幕上显示的内容将向您展示 VR 头戴式显示器中一个眼睛所看到的内容。这对像您这样的开发者来说非常有用!
总结
在本章中,我们介绍了如何实际编程 VR 世界的基础知识,以及使用哪些软件。我们还介绍了安装 React VR 系统,以便开发我们自己的 VR 世界!在下一章中,我们将介绍 3D 基础知识和构建 VR 世界所需的数学知识。
不用担心数学;不会有弹出式测验。
现在要真正创建一个有趣的世界。但首先,我们需要了解世界由什么组成。下一节将描述 React VR 术语,以描述您的虚拟世界。
第三章:3D 或其他维度中的现实
我们决定进入一个几乎真实的世界。要理解如何绘制这个世界,我们需要准确理解如何描述它。
这一章描述了我们如何在数学意义上做到这一点。别担心,这不是回到高中数学!(好吧,也许是几何。好吧,也许有一点点。好吧,也许有很多。我会尽量让它不那么痛苦。)
描述世界的方式有很多种;不管我们如何描述,它仍然是同一个世界。正如莎士比亚在《罗密欧与朱丽叶》中所说的:
“名字有什么含义?不管我们如何称呼玫瑰,它依然芬芳。”
在我们的情况下,有趣的是,情况并非如此:一个描述错误的盒子看起来会完全不同。你需要学会这种语言。不仅如此,你还需要了解 React VR 如何描述这个世界,因为不同的 3D 图形程序都使用不同的数字(缩放)、方向(向量)和旋转。
对于虚拟世界,不同类型的软件和硬件都需要不同的描述方式。例如,坐标可以是左手系或右手系。如果搞混了,物体会朝着与你预期的方向不同的方向移动!
特别是,在 3D 中,上下有不同的含义;更具体地说,不同的 3D 程序之间通常上方方向是不标准的。在 React VR 中,Y 轴向上。为什么 Y 轴向上?继续阅读了解:
-
坐标:这些是空间中的固定点
-
点:这些是多边形的构建块
-
向量:这些是方向
-
变换:这些是将事物移动到你想要它们的地方
-
渲染:这将把点和变换的讨论转化为真实的东西
超越平面 - 3D 概念
为了在 3D 中表示事物,我们必须将所见的东西转化为计算机可以用来生成图像的东西。这些方法将涉及到具有 3D 几何、图片和代码的文件。首先,让我们讨论如何在 3D 中定位事物。
为了在 3D 中表示物体,我们需要它们的位置。像 Excel 这样的电子表格使用 A-Z(横向)和 1-66(实际上是 A-XFD 和 1-1048576)。计算机图形使用数字表示所有三个轴。然而,编码这些坐标的方式有很多种。
这适用于比例(什么是一英寸?一英里?)以及它们移动的方向(是 Y 还是 Z 向上?)。为了弄清楚这一点,我们需要谈论坐标系。
坐标
我们都习惯于使用图纸、网格、发光的电子表格,带有X和Y网格,或者在你使用的任何电子表格程序中的数字和字母,比如 A1 和 B1。进入第三维可能会让人感到困惑,尽管那是我们生活的空间。这就是为什么我将这一部分称为超越平面。
我们在二维或一般数学中认为理所当然的数学运算在三维中却是不同的。例如,如果你将X和Y相乘,你得到的答案与将Y和X相乘的答案相同。然而在三维中,旋转并不是这样的。要看到这一点,试着拿起这本书的两份副本。(我买了两本,你呢?妈妈,你在读吗?)
好了,认真地,请拿起任意两本书,纸质书。如果你有两个 Kindle,你也可以用它们。
-
对于第一本书:
-
物理上将其(合上)向左旋转四分之一圈(朝向你的右手)。
-
然后,将背边向你翻转(翻过来)。
-
你现在看到的是背面,侧面。
-
对于第二本书,以相反的顺序翻转:
-
将背边向你翻转(翻过来)。
-
然后将其顺时针向左旋转四分之一圈(朝向你的右手)。
这两本书面对着不同的方向,尽管你两次以相同的方式旋转它们,只是顺序略有不同。
三维数学可能会让人感到困惑。通常,如果你将A和B相乘,你得到的结果与将B乘以A的结果相同。
当涉及到平移、旋转和缩放时,这个概念非常重要。你编码的顺序决定了你的物体最终在世界中的位置和它们的外观。
我们将使用三个数字来指定三维空间中的每个位置,具体来说是X、Y和Z。
这被称为笛卡尔坐标系。还有其他类型的坐标系,但几乎每个计算机系统都使用笛卡尔坐标系来表示空间位置。旋转和向量有时会使用其他坐标系。这是一个欧几里得空间。
为了让三维更加令人困惑,有些人使用X和Y,Z成为新的维度,而其他人则说Y是向上的。为什么(它)是向上的?当处理屏幕时,你习惯于使用X和Y。一张纸类似,尽管纸通常是水平的,而屏幕是垂直的。
这导致了一个有趣的三维转换问题。在三维中,我们使用X、Y和Z。如果你习惯使用X和Y,那么Z就必须成为新的第三维,它将是向上的。然而,如果你习惯将X和Y看作是一张图纸,那么Y已经是向上的,所以Z最终会是内部和外部。每个三维系统似乎都有些不同。
基于 WebGL 的 React VR 使用熟悉的X和Y作为左/右和上/下;所以Z必须是内部/外部。然而,一个不同之处是在 React VR 中,Y是向上的;在标准的 HTML 中,Y是向下的。换句话说,HTML 和 React 使用的坐标是(零乘零)作为左上角。Y是向上的吗?大多数三维程序使用Y或Z作为向上,也就是说在我们的情况下,正的Y是向上的。
WebGL 和 HTML 与 React 不同,可能需要一些时间来适应。要将一个物体放在你面前,以便你能看到它,你需要给它一个负的 Z。
在三维中,坐标可以是左手的或右手的。正如我们在X、Y和Z中看到的,有时箭头的方向并不是你所期望的。为什么 React VR(实际上是 OpenGL)没有决定让 Z 进入屏幕呢?那么坐标将是左手的。相反,大多数图形系统使用右手坐标系。
我哥哥是左撇子。
左撇子也没什么不好。
(实际上,他是右撇子,但为什么要用事实来破坏一个好故事呢?)
右手和左手有什么意思?这是一个助记符,有助于箭头的方向和旋转。如果你拿起任何一只手,伸开前三个手指,它们就会拼出X、Y和Z的方向。一个图表会有所帮助;你的前三个手指(拇指、食指、中指)指向正的X、Y和Z:
在这个图表中,有几件事情需要注意。相机代表我们在左边,透明地看着屏幕。因为Y是向上的(为什么?),X是向右的,OpenGL 使用的坐标系统与 HTML 或一张图纸并不相似,但它更或多或少是网络上的标准。
选择这种方式更容易映射到 3D 模型、计算机辅助设计(CAD)和建模程序(如 Blender、Maya、3DSMax)的构造方式。这与 React 的工作方式相反——Y向下为正。这是一个右手坐标系;如果你用左手尝试,X、Y和Z轴的顺序会不同。
旋转呢?
在 React VR 和 OpenGL 中,围绕任何轴的旋转也是右手坐标系。这意味着围绕任何轴的正旋转将沿着拇指指向的方向进行,手指弯曲。例如:
你是否在看着你的右手并弯曲手指?没关系,这有助于可视化。是的,那些箭头显示了各自轴上的正方向。
老实说,Y朝上和Z朝上似乎在 3D CAD 世界中很常见混淆。你的 CAD 系统可能工作方式不同。没关系,我们可以翻转和反转它——只要知道在导入模型时,你可能会发现它们侧倒,甚至是里外颠倒。
在 Blender 中,Z朝上,X和Y在平面上;然而,在导出时,Y可以替代朝上。为什么?因为这是正当合法的。
这些数字是无单位的;一个边长为 1 的立方体可以被视为 1 英里或 1 英尺。然而,在 WebVR 和 React VR 中,单位通常被认为是米。
Blender 可以使用无单位、公制或英制,因此在导入物体时需要调整比例。
程序 Poser 使用奇怪的单位——你需要调整从中导入的任何东西的比例。
用于导入模型的 OBJ 文件没有单位信息;它们是无尺寸的:1 就是 1,不是 1 米。
点
点是指空间中的 3D 位置,通常通过X、Y和Z位置来确定。在 React VR 中很少直接描述点,除非你正在进行本地渲染,但空间中的位置经常被描述为点。例如,一个变换节点可能会说:
transform: [{
translate: [0, 400, 700]
}]
应用变换的对象的中心将位于位置X=0,Y=400,Z=700 处。
向量
向量指的是方向。在航空中,飞行员谈论向量。从电影《飞机》中的场景中,克拉伦斯·奥弗、罗杰·默多克、维克多·巴斯塔和控制塔讨论航向:(bit.ly/WhatsOurVector
)
罗杰·默多克:2-0-9 号航班,你已获准起飞。
奥弗船长:罗杰!
罗杰·默多克:啊?
塔台声音:L.A.离港频率,123 点 9。
奥维尔船长:罗杰!
罗杰·默多克:啊?
维克多·巴斯塔:请求矢量,结束。
奥维尔船长:什么?
塔台声音:飞行 2-0-9 清除矢量 324。
罗杰·默多克:我们有许可,克拉伦斯。
奥维尔船长:罗杰,罗杰。我们的矢量是多少,维克多?
塔台声音:塔台的无线电许可,结束!
奥维尔船长:那是克拉伦斯·奥维尔。结束。
从我们作为 VR 人的角度来看,他们真的是指航向。在 3D 空间中,你也可以朝上或朝下。这三个方向对我们来说都非常重要。
翻译,严格来说,使用向量;如果你给一个对象的变换属性为[0, 2, 0],你是在告诉对象在*+Y方向上移动2*个单位,而不一定是绝对位置 0,2,0。然而,需要注意的是,如果对象的原点是在 0,0,0,那么它是一样的。在翻译时,考虑你的 3D 对象的原点以及对象是否采用绝对或相对定位是很重要的。
变换
这不是一本关于奇异的可折叠机器人的书,所以我们谈论的是变换,而不是变形金刚。
变换是放置、定位、移动和缩放对象的方式,基本上是任何转换对象、点等的X、Y、Z坐标。
在 React VR 中,变换通常是样式的一部分。例如:
style={{
transform: [
{rotateZ : this.state.rotation},
{translate: [0, 2, 0]},
{scale : 0.01 },
],
}
变换顺序非常重要。正如我们之前讨论的,在 3D 中,变换不是传递的 - 如果你先平移,然后旋转,你最终会停在一个不同的位置,而不是如果你先旋转,然后平移。记得书上的例子吗?
在 React VR 中,变换是大多数具有物理存在的对象的标准属性节点。(见附录和第四章《React VR 库》。)
变换确实有三个主要参数(和一些已弃用的属性);一个变换,或矩阵参数。
是的,我说的是矩阵。
矩阵一直是一个数学概念已经有一段时间了。它也是一部很棒的电影。由于版权限制,我不能在这里包含矩阵的图片,但上面是我在矩阵中查看我们的 VR 控制器场景的表示。无论如何,我不是指电影。我们将使用矩阵来创建我们自己的 3D 场景。
矩阵是描述平移(向量)、旋转、缩放和扭曲的数学方式。我的一些朋友在周末会有些扭曲,但扭曲是一个数学术语,意思是移动对象的顶部比底部更多。你可以把它想象成倾斜。
要完全理解矩阵,让我们谈谈非基努·里维斯的做法。
每当有一个物理对象,比如一个盒子、一个模型、一个灯光或一个 VR 按钮,你都有各种样式属性,其中之一就是变换。变换节点可以使用矩阵,或者有时候更容易一些,直接使用平移属性。例如,如果你在 React VR 中定义一个Cylinder
,你可以这样变换它:
<Cylinder
radiusTop={0}
radiusBottom={2.20}
dimHeight={2.8}
segments={10}
style={{
transform: [
{rotateX: -45},
{translate: [0,1, -4]}
{scale: .4}
]
}}
/>
变换顺序很重要。这是一个三个圆柱体的例子,除了颜色和变换之外都是相同的:
<Cylinder
radiusTop={2}
radiusBottom={2.20}
dimHeight={5}
segments={10}
lit = {true }
style={{
color: 'red',
transform: [
{translate: [1,.5, -6]},
{rotateZ: -90},
{scale: .2}
]
}}
/>
<Cylinder
radiusTop={2}
radiusBottom={2.20}
dimHeight={5}
segments={10}
lit = {true }
style={{
color: 'blue',
transform: [
{rotateZ: -90},
{translate: [1,.5, -6]},
{scale: .2}
]
}}
/>
<Cylinder
radiusTop={2}
radiusBottom={2.20}
dimHeight={5}
segments={10}
lit = {true }
style={{
color: 'green',
transform: [
{scale: .2},
{rotateZ: -90},
{translate: [1,.5, -6]}
]
}}
/>
这是生成的 React VR 世界:
矩阵怎么样?
矩阵是一个四列四行的数字系列(数组)。
您也可以在变换节点中使用矩阵
。矩阵数学的完整讨论超出了本书的范围。网上有很多参考资料。基本概念相当简单,但旋转可能有点困难(尽管是确定性的)手工编程。
平移被存储为:
[1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
Tx,Ty,Tz,1].
缩放由以下表示:
[Sx,0, 0, 0,
0, Sy,0, 0,
0, 0, Sz,0,
0, 0, 0, 1].
旋转可以用 R 值表示:
[R00,R01,R02,0,
R10,R11,R12,0,
R20,R21,R22,0,
0, 0, 0, 1].
通过矩阵数学进行的旋转非常精确,但也非常复杂。正如我们之前所看到的,旋转的顺序会改变结果对象的位置。矩阵本身不会有这些问题,因为顺序已经嵌入到矩阵中。计算旋转可能会很混乱。
一般来说,当手动移动对象时,您会希望使用变换样式而不是矩阵数学。
当复制对象的位置和方向或以编程方式移动对象时,您会希望使用矩阵。
通过矩阵
按比例缩小所有轴十分之一并按[3, 2, 1]进行平移的矩阵
可以应用如下:
style={{
transform: [
{matrix : [0.1,0,0,0, 0,0.1,0,0, 0,0,0.1,0, 3,2,1,1]},
],
}}
您不能同时使用变换矩阵和变换样式(单独的平移、旋转、缩放)。实际上没有必要,因为您可以通过指定单独的变换来完成矩阵可以完成的一切。如果您使用矩阵,您就是相当专业!无论如何,您创建的任何变换都将在幕后转换为矩阵。
欢迎来到矩阵-现在你可以创建它。
渲染
Bing 将渲染定义为:
好吧,那很有趣,但显然不是我们的意思。渲染是指将我们一直在讨论的所有数学描述转化为可视化的东西。
React VR 使用的渲染引擎是 three.js(bit.ly/2wHI8S9
),通常使用 WebGL 进行渲染(bit.ly/2wKoKCe
)。WebGL 是一个强大的 JavaScript API,用于生成高性能图形。它将使用系统中的任何高性能图形硬件(GPU),并且在大多数浏览器中是本地的,无需插件即可进行 3D 图形处理。
然而,网络通常是一个狂野的地方。你会认为浏览器制造商会从 20 年前的兼容性问题中吸取教训,但遗憾的是情况并非如此。一些浏览器可能会出现问题或兼容性问题,尤其是在移动设备上。
我们难道不能和平相处吗?
这将影响 React VR 的运行情况。如果你想知道你的浏览器和硬件是否支持 WebGL,请访问bit.ly/WebGLTestPage
的 WebGL 测试页面。该页面将显示一个旋转的立方体;如果你使用的浏览器不是百分之百兼容的,它可能还会显示一些警告。WebGL 维基百科页面(bit.ly/2wKoKCe
)对各种浏览器的功能有很好的描述,但事情会发生变化。
你需要进行测试。
测试它的外观
在第二次世界大战中,美国海军陆战队在太平洋战争前对水陆两栖登陆进行了大量测试。他们已经掌握了战术和战略。
他们第一次登陆时,撞上了珊瑚礁。圣迭戈/彭德尔顿营地没有珊瑚礁。因此,尽管这是一次成功的登陆,但比他们预想的更加灾难性。因此,美国海军陆战队有一句话:
“训练就像你将要战斗一样。”
这显然是一个长期存在的说法。罗马军团过去常说:
训练应该是一场无血的战斗,这样在战斗中,就会像血腥的训练一样。-罗马军团训练法则
如果你习惯使用 Firefox,将你的 React VR 解决方案发布到世界上,你的 Petunia 阿姨使用 Orchestra 浏览器查看你的世界,可能不起作用,Petunia 阿姨仍然会认为你是她那个无目标的兄弟的懒散后代。请注意,据我所知,Orchestra 不是一个真正的浏览器,尽管我使用 Opera。
如果这是一个内部公司专用的应用程序,并且你有一个标准(并且可以强制执行!)只使用一个浏览器,那么你可以使用该浏览器进行测试和开发。
然而,如果你希望许多人使用你的 React VR 应用程序,你确实应该使用各种浏览器进行测试。如果你有一台 Mac、一台 PC 和一台 Linux 机器,那就更好了。这些可以是虚拟机(特别是 Windows,带有适当的许可证,和 Linux),否则你需要 Beta 测试人员。
你需要像你期望人们使用它一样测试它,否则你会认为它运行良好,但人们对你的虚拟世界不满意。你需要尽可能多地在各种浏览器和硬件平台上进行测试。当然,这实际上并不现实,但却是必要的。这就是 Beta 测试人员可以派上用场的地方。
渲染的工作原理
我们正在谈论渲染。这是将数学模型与表达的属性转化为屏幕上可见的东西的过程。
为了在 VR 中渲染你的模型,React VR 使用了基于 OpenGL 的 WebGL。WebGL 是 JavaScript 的实现,如果浏览器中有一个相当功能的实现,它通常应该在大多数平台上看起来一样。如果你描述一个竖直的红色箭头,那么在所有浏览器中它都应该看起来像一个指向上方的箭头。关于测试的建议通常是确保它能够正常工作,而不是确保一个向上的箭头不会突然指向右边。
在游戏行业,人们通常需要在 DirectX 和 OpenGL 之间做出决定。WebVR 既不使用 DirectX 也不使用 OpenGL,尽管 WebGL 是基于 OpenGL 设计的。与 OpenGL 不同,它还包括 HTML 元素,如 HTML5 画布和 DOM(文档对象模型)。如果你在使用 WebVR,你就是在使用 WebGL。
如果你正在测试的浏览器没有正确显示你的场景,但其他浏览器可以,请务必提交错误报告。这些应用程序中的许多都是像互联网上的大多数酷东西一样处于前沿。
你希望这些错误得到修复-所以告诉人们。程序员无法修复他们不知道的问题。你将为更加理智、更加清洁、更加有效的网络做出贡献。
成为解决方案的一部分!
WebGL 通常会使用高性能的 GPU,通过 OpenGL 或 DirectX。你不需要担心,也无法控制 React VR 使用的内容(除非使用原生应用),尽管这是一个优势。Web 浏览器通常会做正确的事情,并利用手机(手机,平板电脑),笔记本电脑或台式机上可用的任何硬件。
如果你想更精确地控制渲染呢?这是可能的,使用 React VR Native,我们将在以后的章节中更详细地介绍。
总结
在本章中,我们学习了描述我们的 VR 世界的重要方法。如果你想这样想,我们正在学习矩阵的语言,甚至涉及到矩阵。我们学习了关于 3D 坐标,点,向量,右手和左手世界以及变换。
我们学习了所有这些概念是如何结合在一起并呈现的,以及不同的网页如何利用这些信息创建一个视觉空间。我们还学会了如何进行测试!
为了进行测试,我们需要在浏览器中有一些东西。为了做到这一点,我们不仅需要知道如何用数字和旋转来描述世界,还需要知道这些数字如何在构建块中使用。下一节将介绍 React VR 用来描述世界的关键词,组件和对象。
第四章:React VR 库
这一章是关于 React VR 库的布局;其中的对象和组件。本章中的许多概念将在后面的章节中引用,所以如果你正在阅读电子版本的这本书,它将被大量超链接以供你享受和愉悦。
React VR 有六个基本元素,并且使用一种新的、但又熟悉的范式 JSX(JavaScript eXtension)进行编码。如果你已经了解 React,那么你对 React VR 也应该很熟悉,尽管有一些重要的区别。我们将涵盖以下内容:
-
JSX,React VR 的语言和语法:
-
React VR 和 React 之间的区别
-
组件和 VR 组件:
-
属性
-
状态
-
事件
-
布局
-
样式
-
所有组件和关键字的细节:
-
对象-可见和不可见
-
灯光
-
多媒体-声音和视频
-
摄像机和视图
我没有涵盖所有可用的 API,因为那基本上是一个冗长的、像字典一样的背诵,最好是使用网站上的文档来探索 API。在后面的章节中,我们将使用关键的 API 来为我们的世界增添生机并在其中导航。要获取完整的、最新的 API 列表,请查看文档(facebook.github.io/react-vr/docs/getting-started.html
)。
JSX-React VR 的语法
React VR 看起来很像 HTML;这使得阅读、编辑和部署变得容易。在幕后,React 和 React VR 使用的 UI 语法粘合剂将被编译成 JSX 或 JavaScript eXtension。JSX 是 React 的语法扩展,允许在 HTML 和 JavaScript 的混合中编码。你也可以直接编写 JSX。
React JSX 的一个例子如下:
const element = <h1>My title!</h1>;
这不是一个字符串,因为它不在引号内,也不是 JavaScript。它比直接在 JavaScript 中编码更易读、更易用。JSX 使编程更快速、更具声明性。
它很有用,但所有这些易读和易编程的特性也伴随着一些陷阱。其中之一是分号会自动输入。就像 HTML 一样,你可以包含额外的行,但你的代码可能会得到你没有打算的额外的分号。
在你的代码周围加上括号以避免这个问题——我也强烈建议你学习一下 JavaScript 的语法。这本书中的一些内容花费了我比应该更长的时间,因为我是 C++程序员,不是本地的 JavaScript 程序员。
在编译 React VR 之后,JSX 会自动转换为 JavaScript。这意味着你可以在任何需要使用 JavaScript 的地方包含 JSX。
React VR 和 React 之间的区别
在 React 中,你的大部分思考都围绕着自 JavaScript 诞生以来一直吸引和激怒我们的文档对象模型(DOM)。在 React VR 中,你需要忘记 DOM;在这方面,React VR 更类似于 React Native。即便如此,也有一些概念你需要忘记。
忘记像素作为一个维度;在 VR 中这个概念是没有意义的。
你可以将一张巨大的照片铺满世界,看起来很模糊,而一张小照片放在一个小物体的一侧,看起来非常清晰。你可以离物体更近或更远(假设你已经编程了移动),这将极大地改变某物的“像素”宽度。相反,一切都以现实世界的米为单位(如果你来自一个坚持使用过时的“英尺”单位的国家,你可以假装单位是码。对于 VR 工作来说足够接近)。
在 React VR 中可能会显得有点奇怪的另一个概念是渲染的速度。在 React 中,你的页面被加载,然后显示,然后页面的元素可以被交互(点击),但整个页面很少重新渲染,除非有人点击刷新。当对象的属性发生变化时,render
方法会被调用。这并不意味着你必须有一个定时器来“刺激”你的页面在 VR 中的渲染。
使用 React VR,整个页面在不到 16 毫秒的时间内被渲染,以实现每秒 60 帧的速度,这现在被认为是 VR 的基本要求。整个页面不会被重新解析。这在某种程度上与常规 HTML 相反。特别是在活跃的网页中,单独的 VR 组件将以每秒 60 帧的速度被渲染(显示),当它们的属性发生变化时,它们将被渲染(到 three.js 代码)以更新该表示。
渲染对象并不等同于页面渲染。这可能会有点令人困惑。即使各种对象的render()
方法尚未运行,页面在开始加载时就会被渲染,以将它们转换为 three.js 代码。
最终结果是,没有任何额外的编程,当属性在您的世界中更新时,对象将根据这些属性更改适当地显示。这是 React 工作的基石,它在 React VR 中同样适用。这增加了每秒多个帧率的渲染。
既然我们已经讨论了 React VR 不是什么,让我们来看看 React VR 是什么。
核心组件
React VR 具有可重用的 UI 元素,您可以在各种地方使用。这些被称为组件。有两个内置组件:
-
文本
-
图像
您还可以通过扩展React.Component
来构建自己的组件。
组件是真实的东西,不仅仅是标签或占位符,因为它们已经以通过render()
函数在世界中呈现自己的方式构建。这不仅仅是一个函数;像 React VR 的一切一样,render()
通常有一组子组件,用于呈现或描述其内容。组件的一个示例可能如下:
<Greeting/>
这将是一个文本组件,一个内置类型。
VR 组件
VR 对象,您通常会认为是组件,稍后会涉及。React VR 文档在核心组件部分没有提到它们,这有点令人困惑。您可能会想“只有文本和图像?物体呢?” VR 组件是我对以下事物的术语:
-
VR 物理组件:这些是您可以在世界中“看到”的对象:
-
3D 基元,包括盒子,圆柱体,平面,球体和导入的对象(可以非常详细)
-
UI 元素,如面板和按钮
-
灯光:这些照亮前面的对象,可以是几种类型。请注意,在 React VR 中,当前照明不会为实时速度投射阴影。
-
多媒体:这包括视频和声音。通过这种方式,您可以为 360 度视频创建移动背景,也可以在您创建的世界中有“电视”。
-
摄像头和场景:摄像头控制渲染,场景包含您放置其中的所有对象。
我们将在下一级-细节部分中逐个介绍这些关键词。
属性
如果组件没有属性,那将会很无聊。我们的问候语的一个属性示例可能如下:
<Greeting name='Hello React VR Dude!'/>
name
和其他值如此被称为props。属性是名称,有一个值,我设置为一个幽默的字符串。它们可以通过编程访问,例如,{this.props.name}
。
许多 3D 对象也有属性;这些属性因对象而异。
状态
也许我们处于一种困惑的状态,但 React VR 状态非常重要,因为它影响所有组件的显示,因此也影响这些组件的各种属性。如果组件的属性(外部)或状态(内部)发生变化,组件将重新渲染自身。
渲染并不一定指的是“为眼球创建图像”,尽管它可以。在这种情况下,渲染可以指的是通过 React VR/JSX 编译过程构建代码。
React VR 是封装的,根据对象导向的哲学/编程范式,因此可修改的状态位于组件内的this.state
对象中。它应该只通过一个“set”函数进行修改,具体如下:this.setState({myStateVariableBeers: 99 })
请注意,虽然乍看起来这似乎是在扭曲 HTML/JSX 格式,但这正是使 React VR 如此强大和易于使用的原因。
事件
事件不仅仅是在你的社区里去玩的有趣的事情,它们也是让你的 VR 世界真正活跃起来的方式。事件是当用户通过用户界面(UI)执行某些操作时生成的。当你将光标移入和移出视图区域时,View
组件会发送onEnter
和onExit
事件。
敏锐的读者可能会感到困惑——我们正在谈论 VR,我刚提到了区域。为什么一个 2D 的概念被讨论为 3D 语言的一个基本组成部分?
事件和布局(下面会讨论)遵循 2D 范例,并且是易于桥接的一个例子,使你能够轻松地过渡到你习惯使用的东西(HTML、CSS 和 JavaScript 以及 VR 世界)。然而,也存在差异,其中一个声明“像素”在任何属性和关键字中根本不被考虑可能看起来基本上很奇怪。这是因为在真正的 3D 世界中,使用像素作为测量单位的概念基本上是没有用的。在你面前一米处的物体将具有比你身后十米处的物体更宽的屏幕表示。因此,尺寸是以世界空间的单位给出的;一个单位是一米(略大于一码)。
React VR 的重点是快速和声明性地构建出色的 3D 世界。这是一种声明性的编程方法。如果你想要构建更复杂的世界,React VR 的强大之处在于你可以使用 React Native 和其他 Node.js 编程方法来扩展 React VR。
布局和样式
WebVR 和 React VR 的一些方面仍然遵循浏览器范式。光标被视为 2D 交互,UI 元素通常以 2D 弹性框和布局规则的形式描述,以在 2D 中布置这些组件。这并不意味着我们没有开发 VR 环境;尽管大多数 UI 是以 2D 格式存在,但这些完全存在于 VR 环境中。
布局和样式自然地转移到 3D。您不必为每个项目(内联)描述您的 3D 对象,而是可以设置类似于样式表或 CSS 的东西。它实际上并不类似于样式表,它就是一个样式表,因此您所有的技能都将转移到这里。
样式表可能会很混乱,所以 React VR 使得布局 UI 元素变得更加容易。它使用 Flexbox,通过 YogaLayout(在github.com/facebook/yoga
)。React VR 的重点是快速创建现实。React 关注用户界面,因此 React VR 中的 UI 元素非常强大是很自然的。
下一个级别 - 细节
尽管 React VR 库很简单,但要真正了解它的全部内容,您需要学习很多语法。您可以略读,但是知道一点而不熟悉所有内容是有危险的。
“一知半解是危险的事情;
深饮,或者不尝试皮耶里亚之泉:
浅浅的饮料会使大脑陶醉,
大口地喝则会使我们清醒。-亚历山大·蒲柏的《批评论文》。
您可能会想“好吧,但是所有的 VR 东西在哪里?你知道,桌子,椅子,灯,人… *等等。”这确实需要深入研究-有相当多的组件。
最好的参考是在线文档,尽管有时可能有点稀疏。请记住在线文档是实时的,这意味着您可以提交问题,甚至修改它,如果您发现有错别字或需要澄清。
我强烈建议您将下一节视为参考部分。当然,你可能需要帮助入睡,那么请继续阅读!在说完这些之后,这一部分非常重要,因为您需要使用许多或所有这些组件来实际构建您的 VR 世界。我将尝试使这一部分有趣。我写书是件好事,而不是试图在舞台上谋生。
物品(可见或不可见的对象)
世界上大部分有趣的事物都是可见的物体或可以与之交互的物体。大致上,按照复杂性的顺序,它们如下:
-
盒子
-
圆柱体
-
平面
-
球体
-
圆柱面板
-
模型
-
全景
-
VideoControl
-
VrButton
基元
盒子,圆柱体,平面和球体都是 3D 基元。它们具有lit
,texture
和wireframe
属性。发光的物体将受到场景中的光线影响。如果指定了纹理(通常是图像文件),您的浏览器将查找(获取或渲染)此图像,并将其用于包裹 3D 基元。UV 映射我们将在第六章中讨论,与 Poly 和 Gon 家族合作和第七章中讨论,与(虚拟)茶壶坐下,但大多数 3D 基元的映射方式与您期望的方式相同。
请注意,纹理可以是一个string
(指向图像文件),一个asset()
调用,或一个require()
。
盒子
Box
是一个基本的立方体。如果未指定尺寸,它的尺寸将默认为一(单位)。
<Box
dimWidth={4}
dimDepth={1}
dimHeight={9}
lit
/>
这将是《2001 太空漫游》中的石碑;尺寸是前三个质数的平方。有关更多信息,请参见facebook.github.io/react-vr/docs/box.html
。
圆柱体
Cylinder
是一个基本的封闭圆柱体。也可以通过将顶部半径设为零(或底部为封闭漏斗)来制作圆锥体。
Cylinder
使用半径,而不是直径。不要把你的圆柱体做得比需要的大两倍!
// Round cylinder
//Doric order column
<Cylinder
radiusTop={.825}
radiusBottom={1}
dimHeight={8}
segments={20} />
// Great Pyramid
<Cylinder
radiusTop={0}
radiusBottom={2.20}
dimHeight={2.8}
segments={4}
/>
注意创造性地使用边数来使圆锥体成为金字塔。有关更多信息,请参见facebook.github.io/react-vr/docs/cylinder.html
。
与所有 3D 基元一样,Cylinder
具有lit
,texture
和wireframe
属性。
平面
这不是一架空中客车,而是一个平坦的表面。虽然它被称为平面,但更像是一个平坦的,正方形的二维板。它不是一个立方体板,那将是一个Box
:
//concrete slab using industry norms for size
<Plane
dimWidth={2.4} dimHeight={2.4}
/>
关于平面的一件棘手的事情是它们只能从其主要一侧可见。它们是快速,轻量级的对象,但只能在其上有一个纹理贴图,因此如果使用大平面可能会显得重复。如果你把平面旋转错了,可能什么也看不到;你可能在看背面。在变换时要小心,或者使用Box
而不是Plane
。
有关更多信息,请参阅facebook.github.io/react-vr/docs/plane.html
。
与所有 3D 基元一样,“圆柱体”具有lit
、texture
和wireframe
属性。
球体
跟随弹跳的球,尽管动画稍后会涉及。与“圆柱体”一样,“球体”有一个属性可以改变其分辨率:
<Sphere
radius={0.5}
widthSegments={20}
heightSegments={12}
/>
与我们做金字塔的方式类似,为宽度和高度输入非常低的段数可以使“球体”看起来像不同类型的固体。有关更多信息,请参阅facebook.github.io/react-vr/docs/sphere.html
与所有 3D 基元一样,“球体”具有lit
、texture
和wireframe
属性。
模型
“模型”组件允许我们做非常有趣的事情。到目前为止,VR 对象一直相当简单,但模型允许您导入任意复杂度的 CAD 模型。
小心使用“模型”:
您可以轻松导入比您的平台处理能力更复杂的对象。请记住,您仍然保持所需的平滑帧速率,以使虚拟现实看起来真实。
在第六章中,使用 Poly 和 Gon Family,我们将探讨有效使用“模型”的细节。显示“模型”的基本方法如下:
带有材料文件的“模型”:
<Model
source={{
obj: asset('sculpture.obj'),
mtl: asset('sculpture.mtl'),
}}
/>
没有材料文件的“模型”:
<Model
source={{
obj: asset('standalone.obj'),
}}
/>
在撰写本书时,“模型”导入 Wavefront OBJ 文件格式,以及 GL 传输格式(glTF)。 OBJ 是最常见的 3D 模型格式。人们可能会想知道为什么 React 不导入 X3D,这是 WebVR 的首选格式。这是我当初对 VRML 和 X3D 投入如此多精力的事情之一,让我感到沮丧。
无论如何,OBJ 文件通常由两个文件组成;filename.obj
包含对象的几何形状,而伴随的.MTL
文件(材料)包含颜色、材料和对外部纹理(图像文件)的引用。请注意,这意味着如果 OBJ 文件在材料文件中加载了许多纹理,您可能需要远不止这两个文件。
我们将在第六章中更深入地介绍这一点,使用 Poly 和 Gon Family。
请注意,Model
有 lit
、texture
和 wireframe
属性。纹理属性应用于整个模型,可能有多个 UV 映射。通常最好通过 .MTL
文件分配纹理,这可能会自动从建模程序中完成。
不要指望纹理关键字适用于您导入的 Model
。最好在您使用的 CAD 程序中为模型贴图和映射,而不是在 React VR 中尝试覆盖它。
其次,您可能需要手动编辑 .MTL
文件;我的经验是,大多数导出程序无法处理基于节点的着色器的所有复杂性,即使是实时引擎也会大量使用;因此,您的 .MTL
文件几乎肯定不会包含所有不同的烘焙贴图。
CylindricalPanel
CylindricalPanel
是一个过渡对象。它旨在拥有子对象,并提供在当前视点上的无形圆柱体上绘制这些对象的能力。它的主要目的是允许将熟悉的 2D 元素放置在 3D 世界中。要能够做到这一点需要一些过时的元素。
当您使用 HTML 时,要精确布局 HTML 元素,您可能需要考虑和编写像素级的代码;例如,某个元素可能有 200 像素宽。这使您能够精确布局图形。
在 3D 中,这些都不适用。月亮宽是 1 个像素、2 个像素还是 10 个像素?世界没有每英寸点数。因此,大多数 VR 基元将它们的实际大小,嗯,它们的虚拟大小,以米为单位。然后,您的 VR 显示方法将显示正确数量的像素。如果您把头移动到那个立方体旁边,它可能是 2000 像素;如果您在走廊的尽头看到它,它可能只有 10 像素宽。因此,通常不使用像素来确定 React VR 中的大小。
然而,CylindricalPanel
对象确实需要一个像素数量的属性。这不是为了对象本身(嗯,有点是),而是为了一个屏幕外缓冲区来保存任何子对象的可见渲染。像网页中的许多东西一样,它有合理的默认值。默认值相当大,但这是为了使它在近距离看起来不那么粗糙。
我强烈建议不要使用 CylindricalPanel
,而是将您的 UI 重新编码为实际的 3D 对象。这种方式可能会降低分辨率和系统资源使用(主要是 RAM)。
例如:
<CylindricalPanel
layer={{
width: bufferWidthPx,
height: bufferHeightPx,
density: numberOfPxForACompleteTurn,
radius: distanceFromTheViewer
}}>
... Child components ...
</CylindricalPanel>
Child components
行非常重要–这里是你放置实际的 2D 对象,这些对象将显示在CylindricalPanel
上。这不是字面上的代码。
VideoControl
VideoControl
是一个具有正常VideoPlayer
功能的物理对象,换句话说,开始、暂停等等。由于它旨在用于播放视频,这里的示例(直接来自文档)将显示它嵌入到一个动画对象中:
class VideoPlayer extends React.Component {
constructor(props) {
super(props);
this.state = {
// init with muted, autoPlay
playerState: new MediaPlayerState({autoPlay: true, muted: true}),
};
}
render() {
return (
<View>
<Video
style={{height: 2.25, width: 4}}
source={{uri: 'assets/1.webm'}}
playerState={this.state.playerState} />
<VideoControl
style={{height: 0.2, width: 4}}
playerState={this.state.playerState} />
</View>
);
}
}
不要局限于其预期用途。你也可以进行实验–也许它是一个不错的火车控制器!
VrButton
VrButton
实际上并不是一个真正的按钮(好吧,它都是虚拟的,对吧?),这意味着它没有任何几何形状,但是这是一个你可能会发现非常有用的对象,可以包含在世界中。
VrButton
主要用于凝视检测。我们将在第十一章 走在野性的一面中讨论这个以及其他 VR 移动(运动)技术。现在,让我们只讨论一下VrButton
是什么:
<VrButton
style={{width: 0.7}}
onClickSound={{ ogg: asset('click.ogg'), mp3: asset('click.mp3'), }}
onClick={()=>this._onViewClicked()}>
<Image style={{width:1, height:1}}
source={{uri:'../../Assets/Images/gaze_cursor_cross_hi.png'}}
inset={[0.2,0.2,0.2,0.2]}
insetSize={[0.05,0.45,0.55,0.15]} >
</Image>
</VrButton>
这个VrButton
包裹了一个图像并播放声音。我们将在第八章 给你的世界注入生命中进一步讨论声音,但在这里简要介绍,文件格式允许浏览器决定在你选择的浏览器中播放哪种声音。
灯光
如果没有灯光,世界将会是一个非常黑暗且充满吸血鬼的地方。让我们赶走这些不死族。有四种主要的灯光:
-
AmbientLight
-
DirectionalLight
-
PointLight
-
SpotLight
常见灯光属性
所有灯具都有两个共同的属性:
-
intensity
:这是场景中灯光的亮度。默认值是{1.0}
,但你可以调高。在实践中,更高的设置会使物体(例如球体)曲边上的阴影更加清晰和更亮(变淡),但实际上不能比白色更白(RGB 255, 255, 255)。 -
color
:颜色并未列在灯光属性下,但它是所有灯具都具有的样式属性。这是一个 RGB 属性。你甚至可以拥有有颜色的环境光,这可以用于赭色调等,以及模拟来自明亮环境的背景照明。例如,在森林中,也许是浅绿色的环境光。默认值是白色。
其他灯具具有特定于它们所代表的照明类型的属性。
AmbientLight
AmbientLight
是使你的场景可见的最简单的方法。实际上它并不是一个真正的光,但它确实照亮了场景中的一切。
现实世界中的照明非常复杂。光子四处反射,反射物体,渗透进物体,甚至使一些物体发光(荧光和发光)。一个有用的技巧是让物体即使没有灯也能发光,或者在房间中添加一些光线以帮助模拟背景光的散射,而不需要计算这个过程的开销。
这被称为环境光。许多 CAD 系统在材料中都有环境作为一个值。 AmbientLight
让你点亮整个房间。对于喜欢迪斯科、节日的人来说,它甚至可以让你把颜色从白色变成任何你想要的颜色。现在,你可以制作一个看起来像 W 酒店连锁酒店走廊的场景。
奇怪的是,React VR 下载中没有一个示例显示如何使用AmbientLight
;尽管这并不难,但它很重要。
这是一个环境为.2
的球体的屏幕截图:
代码如下:
<AmbientLight
intensity={.2}
/>
注意几件事——我们在最后一张照片中还有一个定向光,所以你可以看到不同之处。球体是来自定向光的白色,然而底部是黑暗的,但不是漆黑。 AmbientLight
可以在实时中伪造一点全局照明或辐射。GI 是光线从其他物体反射并在现实的、非虚拟的世界中创造“填充光”的数量。Three.js 还有一个THREE.HemisphereLight
*用于此,*你可以通过本机视图或本机桥将其添加到 React VR 中。
DirectionalLight
从AmbientLight
到DirectionalLight
,我们正在从抽象到稍微不那么抽象。 DirectionalLight
实际上是用来代替太阳的。太阳的光线总是相互平行的;同样,DirectionalLight
不像更接近的光源那样扩散。
这是一个DirectionalLight
和没有AmbientLight
的情况:
代码如下:
<DirectionalLight
intensity={.9}
style={{ transform: [{ rotateZ: 35 }] }}
/>
在图片中,我们把DirectionalLight
稍微旋转到一侧;球体看起来很有趣,但与场景的其余部分相比不太对。这是因为全景背景的照明与场景的照明有很大不同。你会想要尝试用适当的变换语句来匹配两者与你的<DirectionalLight>
。
PointLight
Pointlight
就像一个老式的灯泡;光从点上向各个方向扩散。关于点光和聚光灯的一个有趣的事情是,再次简化了我们的 VR 看起来真实的方法。为了避免渲染速度非常慢,大气层并没有严格建模。这意味着通常由于大气层而逐渐消失的光会在数英里之外发光(大气效应可能在我所在的地方比在你所在的地方更严重,除非你住在月球上。如果是的话,给我一张票,我会亲自去给你大声朗读这本书)。
为了避免建模大气效应,如消光(褪色)、雾、云等,PointLight
和SpotLight
都采用了衰减和距离属性。
distance
是光线照射的距离。如果它不为零,光的强度将在那个距离为零。
decay
是光线消失的频率。这是一个通用的(无量纲)数字;2
是物理上真实的光线衰减。0.1
会使光线消失得更快,对艺术效果很有用。
例如:
<PointLight
intensity={1}
style={{ transform: [ { translate: [0, 0, -5] }]}}
distance={10}
decay={2}
/>
为了更好地可视化前面的内容,我构建了一个演示场景三次;第一次距离为 10,第二次距离为 4,第三次距离为 4,衰减为0.1
而不是 2。你可以看到第三个场景看起来非常不自然。请注意,所有三个场景的强度都是完全一样的。
如果你的点光看起来昏暗,检查一下距离参数。我建议将衰减保持在 2。
SpotLight
SpotLight
就像那些在糟糕的黑色电影中对着坏家伙脸上发光的灯罩一样,或者像手电筒。与PointLight
一样,它也有衰减和距离属性(如前所述)。
distance
和decay
属性与PointLight
相同。SpotLight
还有penumbra
和angle
属性;这两个属性决定了光的扩散范围。angle
是最大的外部angle
,而penumbra
是一个从一到 100 的数字,定义了SpotLight
的柔和程度。
<SpotLight
intensity={1}
style={{ transform: [{ translate: [0, 2, -5] }] }}
distance={25}
decay={.1}
penumbra={1}
angle={40}
/>
目前,SpotLight 的位置定义了光线的“发光”位置。光的目标,换句话说,它指向的东西,目前在 React VR 中没有暴露出来。在撰写本书时,这个问题还没有解决。
使用 View 来包装 SpotLight 似乎也不会改变目标。
我建议不要使用 SpotLight,除非你可以安排你的场景让感兴趣的物体位于[0,0,0]。
多媒体-声音和视频
如果你听不到任何声音,世界将是一个无聊的地方。视频通常是动态网页的一部分,尽管在 VR 中,我们有一些挑战——视频本身可能不那么吸引人,除非是 360 度视频,有些人称之为 VR(它不能给你更多的超脱感,所以从我的观点来看,它并不是真正的 VR,因为你不能完全沉浸其中,但其他人可能认为它是 VR。在 VR/AR/XR 的这一点上,我们真的需要和平相处!)。
在 VR 世界中,视频可以是提供氛围的重要组成部分。如果你走进一个房间,视频正在播放,它会更像大多数家庭。
声音
在 VR 中,Sound
比起最初听起来要复杂得多(双关语)。Sound
节点允许将音频源放置到您的 VR 世界中。Sound
会让你的世界变得生动起来。
从 React VR 手册中,考虑一个waterfall
的例子:
<Image style={{height: 2.0, width: 2.0}} source={uri: 'images/waterfall.jpg'}> <Sound source={uri: 'sounds/waterfall.wav'} /> </Image>
这个例子展示了在 React VR 中声明并添加东西是多么容易。waterfall
声音简单地附加到waterfall
图像的位置。如果你在 3D 世界中四处走动,你会听到瀑布就好像它就在图像所在的位置;这一切都是通过简单地将Sound
组件添加为叶节点(在这种情况下是图像的子节点)来完成的。Sound
节点本身不应该有任何子组件。
如果Sound
节点没有附加到具有位置的对象上,它将默认为绝对位置,例如position: absolute
。
Sound
节点有许多属性。它们如下:
autoPlay
:布尔值
当组件加载时音频自动开始播放。默认值为true
。
loop
:布尔值
当音频播放完成时自动重复。默认值为false
。
-
muted
:布尔值 当音频被静音时。默认值为false
。 -
onDurationChange
:(回调函数)
当声音持续时间改变时,调用此函数,带有声音持续时间的参数。
onEnded
:(回调函数)
当音频播放完成时,将调用onEnded
函数。
-
onPlayStatusChange
:(回调函数)当播放状态改变时调用此函数。event.nativeEvent.playStatus
:这是声音的播放状态;字符串之一为'closed'
、'loading'
、'error'
、'ended'
、'paused'
、'playing'
或'ready'
。 -
onTimeUpdate
:(回调
函数)当声音的currentTime
改变时调用此函数。 -
event.nativeEvent.currentTime
:音频文件的currentTime
。 -
playControl
:play、pause 或 stop。
这个变量控制播放状态。如果未设置,autoPlay
的值将决定组件加载时是否播放音频。
playerState
:(对象)
playerState
是一个控制视频播放的 MediaPlayerState
,具有其内部状态。设置 playerState
时,autoPlay
的值、静音音量和 playControl
属性将被忽略,因为它们将由 playerState
而不是自己设置。参见 MediaPlayerState
。
source
:(对象)
以 {uri: http} 形式的对象源音频。
volume
:0-1.0(实际上没有限制)
音频音量的值。最小值为零,将声音静音,建议的最大值为 1.0,这也是默认值。允许大于 1 的值;这可能会导致剪切/失真,取决于音频硬件。
例如:要将音量降低 50%,设置 volume={0.5}
。由于不同平台可能具有不同的音频功能(叹息),源可以是几种不同的文件格式,浏览器将选择它可以读取的适当格式。
单声道文件似乎效果最佳;并非所有浏览器都支持立体声音频文件。这是因为浏览器会将声音转换为立体声音,并尝试复制 3D 音频(这可以通过头部相关传输函数仅用两个扬声器完成)。
使用单声道文件以获得最佳兼容性。
Video
由于 Video
只是一个二维(2D)对象,它需要宽度和高度。这不是像你可能习惯的以像素为单位,而是以世界单位,出于前面讨论的原因。如果人们将视角移近或远离你的 2D 视频,它将从每英寸点数的角度改变分辨率。您可能需要尝试不同的大小和视频压缩/存储方式,以找到质量、下载速度和分辨率(颗粒度)的理想平衡。
Video
与前面在本章中描述的 VideoControl
结合使用效果最佳。
这个例子展示了一个 Video
以及一个 VideoController
:
<Video
style={{height: 3, width: 4}} source={{uri: 'assets/Video1.webm'}} playerState={this.state.playerState} /> <VideoControl
style={{height: 0.2, width: 4}} playerState= {this.state.playerState} />
请注意,VideoControl
不是 Video
的子级,它是一个独立的对象,有自己的位置。在这个例子中,可能播放一个 4:3 的视频,通过 this.state.playerState
协调它们的停止/开始/暂停活动。实际上,你可以将 VideoControl
的 playerState
视为输出,将 Video
的 playerState
视为输入。
相机和观看
有一个名为LiveEnvCamera
的相机对象,尽管它不是你通常期望的那样。
在大多数 CAD 系统中,相机设置基本统计数据,如焦距、焦点、相机朝向的方向等。
在 React VR 中,我们在index.vr.js
的顶层有一个<View>
;这是构建 VR 视图的方式。
视图的可见性参数实际上由您的物理观看设备控制。如果您的 HMD 水平视野为 110 度,您将看到 110 度的水平视野。
这是传统 3D 艺术家必须适应的事情——VR 是不同的。
同样,镜头眩光和其他效果,尽管在电影中看起来很棒,在 VR 中看起来很糟糕。你的眼睛没有镜头眩光。
不要尝试使用 React Native 添加它们。
您可能会想,我们如何移动相机?答案是您转换<View>
。如果您想向前移动五米,您将视图向后移动五米,视点将移入场景。请注意,这对于SpotLight
不起作用。
LiveEnvCamera
该对象显示面向环境的相机。这可能是您的观看设备的一部分,也可能不是。例如,GearVR 可能有一个面向环境的相机;Vive 有,Google Cardboard 可能没有。
默认情况下,相机是position: absolute <LiveEnvCamera />
。相机图像显示在距离观看者 1000 米的几何体上。
LiveEnvCamera 可能是为增强现实(AR)应用而设计的;在 React VR 中使用它充其量是实验性的。
视图
View
对象既是世界中的初始场景或相机,也有助于聚合世界中的对象。在这种方式上,它与传统 CAD 程序中的组节点非常相似。作为分组节点,它对于有效的 React VR 软件非常重要,而不仅仅是主渲染循环。
我们提到视图是相机的原因是它具有布局属性和变换。如果在您的主render()
循环中,您转换了<View>
,实际上是在移动相机,您当前的视点所看的位置。
如果您将<View>
用作分组节点,则变换将应用于其所有子节点。您可以通过适当地公开其属性和变换以此方式构建关节模型,尽管更可能是通过 glTF 文件来实现这一点。
总结
在这一章中,我们介绍了 React VR 库的基础知识,其中包括它的组件、API 和编码技术,它们将用于构建您的应用程序。足够的背景知识!现在我们已经做到了,接下来让我们在下一章中创建一个真正的 VR 应用程序。
第五章:您的第一个 VR 应用程序
好吧,四章的背景。希望您刚才浏览了最后一章,尽管我希望您会经常查看它,或者在线文档,因为您构建每个世界时都会用到。既然您已经有了这个背景,我们已经准备好构建第一个 React VR 应用程序了。我们将首先深入研究 React VR 组件、props 和状态。
您将学习以下主题:
-
初始世界创建/建立 React VR 框架
-
设置一个良好的背景图像
-
如何修复背景图像,使其真正等距圆柱形
-
添加 VR 组件
-
创建新的 VR 关键词(类构造)
超越“你好,世界”-我们的第一个 VR 世界
这实际上不是我们的第一个 VR 应用程序,尽管我们并没有真正制作第一个应用程序,npm 安装程序做了。在第二章,“平面世界与超越-VR 编程”中,在“安装 Node.JS 之后-安装 React VR”部分,我们安装了一个简单的Hello World
示例。
我们将首先创建一个新的应用程序(目录)。但首先,让我们谈谈我们正在创建的东西,为此,您将获得特别奖品!
VR 世界设计-或者,恭喜,你是新的天文博物馆馆长!
任何项目都应该开始,即使不正式地,也应该有一个设计。在这种情况下,您收到了一封电子邮件,告诉您“恭喜,您已被选为新的欧洲航天局户外博物馆馆长!”关于您的奖品,您不必担心,这次访问期间不会损坏任何天文学家的夜视。作为博物馆馆长,您可以选择与我不同的艺术品;实际上,您也可以创建一个全新的位置。
如果我们在这方面做得很好,那么我们将进入外太空,并且能够成为轨道上的第一个艺术博物馆。因为我们是第一个,所以我们将是轨道上最好的博物馆。
创建基本的 React VR 组件
React VR 有许多基本组件和代码需要安装。将近 19,987 个文件和 8,111 个目录。您不必手动安装所有这些(浏览器也不会下载所有这些,其中许多是可能打包的框架)。那么,我们如何安装所有这些?
安装所有内容只需要一步。我们将打开一个 Node.js 命令提示符,导航到您想要放置应用程序的任何目录,并创建一个新的 React VR 模板。在进入正确的目录后,键入以下代码:
react-vr init SpaceGallery
这将安装一个名为SpaceGallery
的新应用程序。它将开始安装东西:
注意这一行:
这些命令通常需要很长时间才能运行,但它们显示的一些信息很重要。
在这种情况下,当我写书时,一些工具升级了,系统告诉了我。我通常建议一旦收到这些通知就立即升级;如果不这样做,您提交的任何错误都不会得到您想要的关注,并且经常会添加新功能和错误修复。
有时,您会收到关于您无法真正控制的东西的警告,比如关于connect@2.30.2
的提示。
React VR init 命令将处理安装所需的所有内容,包括依赖项(有很多),然后将其全部安装。完成后,它将列出所有内容,然后退出。不用担心,不是所有这些代码都会传送到客户端。实际下载相当小,与 Unity 或 Unreal 等 VR 游戏引擎相比。
React VR init 命令甚至会告诉您如何启动您的世界。如果我们启动它,我们将看到基本上与我们在第二章中看到的hello world应用程序相同,平面世界与超越-VR 编程。
首先,确保您已经停止了已经在运行的 hello world 应用程序–使用Ctrl+C,然后使用Y
来停止批处理文件。然后,使用cd
命令(更改目录)进入您创建的新SpaceGallery
目录。
如果您无论尝试什么都似乎无法改变您的应用程序,很可能您在其他目录中留下了一个 npm 包在运行。终止所有 npm 会话并重新启动它。
继续启动它:
如果您收到错误消息
yarn is not recognized as an internal or external command, operable program or batch file
*,*您可以忽略此错误;Yarn 类似于 npm。高级用户可以使用任何一个;由于 React VR 示例中使用了 npm,我将在本书中使用它。
欢迎来到开源世界,有时会有太多的选择。DuckDuckGo 是您的朋友。
让我们改变背景来让我们进入状态。
创建更大的世界-背景图像
这是一个做章节 - 之前的章节涵盖了 VR 的一般背景。然而,现在我们实际上要改变背景(图像)。
你在 hello world 应用程序中看到的国际象棋世界实际上是在Pano
语句(在index.vr.js
文件中)中使用的全景图像。当我第一次安装 React VR 时,我以为这是默认世界中包含的一些几何图形。这是我们在SpaceGallery
应用程序的视图(2D 浏览器视图):
除了 hello 文本框之外,所有内容都来自<Pano>
对象:
<Pano source={asset('chess-world.jpg')}/>
这是一个特别构造的球形全景或等距投影。它被扭曲以在 360 度左/右和 180 度上/下(就像纬度和经度覆盖+/- 90 和+/- 180 一样,一个 360x180 的球形图像覆盖整个球体)。
这是chess-world.jpg
,这是每次创建 VR 世界时包含的背景文件:
需要注意的几点:
-
这是一个非常大的文件。它是 4096x2048 像素。即使如此,当你在 3D 中四处张望时,它偶尔会显得有点粗糙。这是因为当你在一个 15 英寸的笔记本电脑上看一个物品时,比如说大约 35 厘米宽,离你的眼睛大约半米远,你看到的是一个 1920 像素的图像,看起来很清晰。当你把这个图像 360 度展开时,相当于超过 17,000 像素(2pi.5m * 1920 / .35m)。
-
大文件会导致下载速度变慢,即使在今天的世界中也是如此。
-
现在想象一下,如果背景是一个视频。VR 具有非常高的带宽要求。这是入场的价格。
-
文件看起来是扭曲的,但当它在浏览器和你喜欢的 HMD 中显示时,它会看起来是直的。
-
由于直线,这张特定的图像非常适合测试;如果你直视下方或直视上方,一切都会匹配。
你可以在网上找到 360 度全景照片,但要确保它们是球形 360x180 全景照片。如果它们是由相机拍摄的,通常在图像的顶部和底部会出现奇怪的东西;许多人认为你不会直视下方或直视上方。
地图投影也是等距投影图像,所以你可能熟悉它们以及在极点处拉伸的事物。如果你把一个地图投影作为你的背景,它会看起来像你在一个地球仪里面。
可能有点奇怪。
在你的世界中直接在视点下方放置一些几何图形是个好主意,以覆盖你的球形全景图中的任何不连续或畸变。
这也有助于避免漂浮的感觉;因为Pano
是二维的,而且无限远,立体深度感知无法显示Pano
有多远。如果你在 VR 世界中改变你的视角,物体看起来会在地板上奇怪地移动。通过在视角下方的基本几何图形或模型语句来避免这种情况,可以使你的世界看起来更加真实。
由于我们正在谈论太空中的一个画廊,在我们进入轨道之前,让我们调查一些新的全景照片,并用它们来准备我们的背景。随意使用你喜欢的搜索引擎在网上搜索更多的全景照片。以下是我遵循的步骤:
- 让我们去欧洲空间局(ESO)并从
bit.ly/PanoESO
复制一个奇妙的太空全景图像。如果你想尝试不同的分辨率,他们在下载页面上有一系列分辨率可供选择。这张图片很棒:
-
将其下载到我们创建新应用程序的
static_assets
文件夹下,然后打开index.vr.js
。在那个文件中,对Pano
语句进行更改:<Pano source={asset('uhd_vlt_circular_cc_eq.jpg')}/>
-
现在刷新你的浏览器,我们已经看到太空了:
- 你会注意到建筑物看起来有点奇怪。如果我们继续往下看,背景图片看起来更奇怪:
-
这是因为,如果你仔细看原始背景图片,它实际上不是 360x180 度,而更像是 360x90 度。这在使用手机拍摄全景图像时非常常见;很少有人捕捉顶部和底部使其成为真正的 360x180 度。真正的全景照片在底部和顶部看起来一样扭曲。例如,看看我们之前看到的平面国际象棋世界图像。
-
这是可以修复的。修复方法就是在原始的 360x90 全景照片底部添加一条黑色条纹(或背景颜色),就像这样:
使用这种技术,你可以更或多或少地修复任何全景图像,只要它至少是 360x90;即使没有标记,这些也是相当常见的。现在,如果我们把这张全景图像放在我们的
Pano
语句中,或者 VR 应用看起来会好得多:为了做到这一点,我使用了一个名为Irfanview的免费图像查看/编辑程序,尽管你也可以使用 Photoshop 或任何其他图像编辑软件。我强烈推荐 Inkscape 或 Gimp;它们功能齐全并且是免费/开源的。
-
当我们在 VR 中查看这个并检查控制台时,我们可以看到一个错误:
THREE.WebGLRenderer: image is not power of two (2000x2000). Resized to 2048x2048
让我们通过调整大小来修复这个问题。Irfanview 有一个很好且相当快速的调整大小功能,可以保留大部分细节。只需调整大小(Ctrl+R),使其成为 2048x2048 或 2048x1024。由于我们在底部添加了黑色条纹,我们可以调整图像的大小而不会拉伸它。如果必要的话,你应该裁剪或延伸图像的底部;这些 360 度的投影很难分辨,但如果你改变图像的宽高比,当你查看它时,世界看起来会被挤压。
如果你得到了一个空白或不正确的背景,请检查图像格式和大小。
混乱世界-添加我们的第一个 VR 组件
好的,现在让我们开始添加我们的物体。
在本章开头的描述中,我们提到了移动到轨道。你可能会想知道为什么我们没有从太空背景开始。我们会的,在我们在地面上创建一个世界之后。我发现太空图像作为背景可能会让人迷失方向,没有地板,我们需要在漂浮和上下变得毫无意义之前讨论等距投影图像。所以,现在,在我们最终到达太空之前,你必须在地球上做出出色的画廊。
这带来了一个重要的观点。<Pano>
语句通常被描述为一个背景。更好的方式是把<Pano>
语句和背景图像看作是整个世界或者你无法触摸的世界的一部分。无论你的背景图像是什么,没有任何 VR 对象,它都会把你放在那里。<Pano>
实际上不仅仅是一个背景,它实际上是整个世界,除了你放置的对象之外。
这就是为什么你选择的背景对于存在感很重要。如果你选择了奇怪或者令人迷惑的背景,人们会感到迷失方向。这可能是你想要的,也可能破坏了沉浸感。此外,他们永远无法触摸到“全景”中的物体,所以如果有靠近的物体,可能会让人感到迷失方向。
因此,让我们为室外画廊的地板添加一个“平面”,这样我们就不会漂浮在我们的背景上。在index.vr.js
中代码生成器生成的第一个<View>
语句之后,添加以下“平面”和“盒子”元素:
export default class SpaceGallery extends React.Component {
render() {
return (
<View>
//the above code is generated automatically, add your code below
//or after the <Pano> statement
<Plane
dimWidth={5}
dimHeight={5}
texture={asset('DeckPlate.jpg')}
lit
style={{
transform: [
{translate: [0,-1.8, -5]},
{rotateX: -90}
] }}
/>
<Box
dimWidth={5}
dimDepth={5}
dimHeight={.1}
texture={asset('DeckPlate.jpg')}
lit={true}
style={{
transform: [{
translate: [5.2,-1.8,.1],
}]
}}
/>
在保存并查看世界之前,我们需要DeckPlate.jpg
文件。你需要从bit.ly/VR_Chap5
下载这个文件,放在static_assets
文件夹里,并将其复制到SpaceGallery
文件夹内的static_assets
文件夹中。我使用了一个叫做 Substance Designer 的程序,由 Allegorithmic 公司开发,位于bit.ly/AllegSub
上的他们的材料分享网站。我在这里找到了这个材料bit.ly/MatSciFi01
。由于你可能没有 substance player,我为你导出了这张图片,名为DeckPlate.jpg
。以后,我们将构建更复杂的模型,并使用其他纹理来构建实际的材料。如果你使用 Photoshop,你也可以使用 Quixel.se 来获得良好的效果。请参阅bit.ly/QuixelSuite
。
我们这样做之后会发生什么?我们点击刷新,然后得到一个空白屏幕。打开你的网页开发者控制台。如果你使用的是 Firefox,点击工具|Web 开发者|切换工具(或者在 PC 上按 Ctrl+Shift+I)。
控制台将打开,我们会看到很多错误,然后是:
Expected a component class, got [object Object]
这是我们忘记添加到import
指令的线索。当我们生成对象时,它会放置我们需要的所有 React VR JSX 导入,以便进行 hello world。每当我们添加新的对象或 API 时,我们需要确保 React VR 知道它。插入以下粗体行:
import {
AppRegistry,
asset,
Box,
Pano,
Plane,
Text,
View,
} from 'react-vr';
在一个大型项目中,你可能偶尔会忘记这一点。如果你的场景没有改变,可以在浏览器中检查控制台。我还建议通过按字母顺序或者你使用它们的顺序添加你的import
声明来进行一些代码组织。按字母顺序更容易快速扫描。你可以直接导入所有内容,但这可能会增加一些你不需要的开销。
点亮世界
一旦我们添加了导入语句,我们会注意到世界有些黑暗;可能很难看到我们添加的盒子和平面。为了照亮事物,我们将添加AmbientLight
和DirectionalLight
(太阳或月光)到图像中。在这个应用程序中,我们有一个明显的夜间图像背景,但我们的物体将被照亮。添加AmbientLight
和DirectionalLight
是伪造自然世界的一种简单方法。在现实世界中,物体会从附近物体反射的光线中获得光照。AmbientLight
可以模拟这种柔和的发光(在其他渲染系统中,这经常被称为全局照明)。方向光模拟了室内顶部照明(许多灯光,比如在教室中)或太阳或月亮的光线。
将您的照明语句放在应用程序的render()
语句顶部,这样您就可以轻松找到它们。我会把它们放在顶级<View>
之后的第一条语句。
如果灯光是物体的一部分,比如一盏台灯,保持灯光靠近物体(或作为物体的子级)。
这将允许您快速修改场景照明。
我们的AmbientLight
语句很简单;DirectionalLight
需要更多的思考,但也很简单。按照这种方式编写它们:
<AmbientLight
intensity = {.3}
/>
<DirectionalLight
intensity = {.7}
style={{
transform:[{
rotateZ: 45
}]
}}
/>
现在你应该能看到平台了,尽管它们看起来很小。
不要忘记导入语句!
为什么我让你粘贴Plane
和Box
?
它们都成为了我们世界的地板,那么为什么我们两者都做了呢?如果你在 VR 视图中向右看,你会发现Box
的边缘看起来有点奇怪。纹理映射均匀应用于所有六个面,当拉伸到整体尺寸不接近的盒子时可能看起来很奇怪。这是Box
的一个缺点。Plane
没有这个问题,但如果你把Plane
倾斜错了方向,你可能看不到它;Plane
是单面的,所以如果倾斜离开当前摄像头,它将是看不见的。Plane
也是无限薄的。我包括了两者,这样你就可以看到它们的样子。
在变换部分,我提到了变换的顺序是多么重要。这对于Plane
对象尤其重要;如果你先旋转,然后平移,图像可能会完全错位,在Plane
的情况下,会变得看不见。
要构建整个画廊,我们需要为平台包括几个正方形。在它们之间留下一点间隙。现在,我们可以通过复制和粘贴每个 Box
或 Plane
(使用你喜欢的任何一个!)并更新 translate
语句来实现这一点:
{translate: [0,-1.8,-5.1]},
...
{translate: [0,-1.8,.1]},
...
{translate: [5.1,-1.8,.1]}
然而,有一个更好的方法;React VR 不仅仅是一个硬编码的几何文件,而是一个真正的面向对象的 JSX 文件,我们可以利用它。
让我们定义一个新对象,它是我们的甲板板块之一。我们将除了位置之外的所有内容都放入该对象的组件中。首先(我在这个问题上纠结了一段时间),将代码的第一行从:
import React from 'react';
到:
import React, {Component } from 'react';
请注意,这是一个不同的 import
语句,不同于我们在第二行使用的(很容易忽视)。
如果你看不到你创建的对象,或者得到一个你知道是有效的关键字但却没有被识别的错误,请不要忘记文件顶部的 import
指令!
得到类 - 将对象合并为新关键字
一旦我们导入了 React 组件,我们就可以将一个对象定义为 class
。对于这个版本,我们将使用单独的数字作为 props(就像参数),然后根据需要实例化它们。我们可以将这段代码粘贴到任何地方;现在,你可以将它放在 index.vr.js
文件中的 export default class SpaceGallery extends React.Component
行之前。你的新 class
是:
class Platform extends Component {
render() {
return (
<Box
dimWidth={5}
dimDepth={5}
dimHeight={.1}
texture={asset('DeckPlate.jpg')}
style={{
transform: [
{
translate: [ this.props.MyX, -1.8, this.props.MyZ]
}
]
}}
/>
);
}
}
要实例化此对象的副本,在你的 <View>
中使用此代码(在你的 SpaceGallery
类中):
<View>
...
<Platform MyX='0' MyZ='-5.1'/>
<Platform MyX='0' MyZ='0'/>
<Platform MyX='0' MyZ='5.1'/>
<Platform MyX='5.1' MyZ='-5.1'/>
<Platform MyX='5.1' MyZ='0'/>
<Platform MyX='5.1' MyZ='5.1'/>
<Platform MyX='-5.1' MyZ='-5.1'/>
<Platform MyX='-5.1' MyZ='0'/>
这是制作预制件或类的最直接方式,这样你就不必剪切和粘贴无休止的 Box
组件声明来制作你的地板。
如果你熟悉 React 中状态的概念,这个时候不要使用状态。你应该为随时间变化的值使用状态;我们将在第七章 给你的世界注入生命 中更多地讨论这一点,特别是第十一章 走进未知领域。由于这是应用的静态版本,你不需要它。我们将使用 props 在正确的位置构建平台。
你可能会认为,你可以用一个向量来代替两个命名参数,但是 JavaScript 没有向量的概念。但是,你可以使用 { }
运算符,粘贴适当的代码。创建我们的板块地板段的另一种方法如下:
class VecPlat extends Component {
render() {
return (
<Box
dimWidth={5}
dimDepth={5}
dimHeight={.1}
texture={asset('DeckPlate.jpg')}
style={{
transform: [
{
translate: this.props.MyPos
}
]
}}
/>
);
}
}
注意translate
语句中没有方括号围绕this.props.MyPos
,就像使用文字时那样。然后像这样实例化它:
<VecPlat MyPos={[-5.1, -1.8, -5.1]}/>
请注意translate
语句中额外的{}
括号和缺少的括号,如前所述。在这种情况下,你需要它来创建向量(数组)。
把所有东西放在一起
现在我们有一个基本的平台来放我们的艺术品,你已经学会了如何创建我们可以实例化的对象,我们已经在场景中放置了基本的照明–现在让我们添加一些物体。
你从权威人士那里得知,你做得很棒,现在可以把太空画廊移到太空中。从 GitHub 的链接中,你可以下载一些新文件,包括BabbageStation_v6_r5.jpg
。一旦我们用这个文件改变Pano
语句,我们就会直接进入轨道–没有在加速椅子上摇晃的情况。非常安静。
让我们回到建造画廊。首先,让我们巩固并使用Platform
;一旦你下载了新的static_assets
,你的新index.vr.js
文件应该如下:
import React, {Component } from 'react';
import {
AppRegistry,
asset,
AmbientLight,
Box,
DirectionalLight,
Div,
Pano,
Plane,
Text,
Vector,
View,
} from 'react-vr';
class Platform extends Component {
render() {
return (
<Box
dimWidth={5}
dimDepth={5}
dimHeight={.1}
texture={asset('DeckPlate.jpg')}
style={{
transform: [{ translate: [ this.props.MyX, -1.8, this.props.MyZ] } ]
}}
/>
);
}
}
export default class SpaceGallery extends React.Component {
render() {
return (
<View>
<Pano source={asset('BabbageStation_v6_r5.jpg')}/>
<AmbientLight
intensity = {.3}
/>
<DirectionalLight
intensity = {.7}
style={{
transform:[{
rotateZ: 45
}]
}}
/>
<Platform MyX='0' MyZ='-5.1'/>
<Platform MyX='0' MyZ='0'/>
<Platform MyX='0' MyZ='5.1'/>
<Platform MyX='5.1' MyZ='-5.1'/>
<Platform MyX='5.1' MyZ='0'/>
<Platform MyX='5.1' MyZ='5.1'/>
<Platform MyX='-5.1' MyZ='-5.1'/>
<Platform MyX='-5.1' MyZ='0'/>
<Platform MyX='-5.1' MyZ='5.1'/>
<Text
style={{
backgroundColor: '#777879',
fontSize: 0.8,
fontWeight: '400',
layoutOrigin: [0.5, 0.5],
paddingLeft: 0.2,
paddingRight: 0.2,
textAlign: 'center',
textAlignVertical: 'center',
transform: [{
translate: [0, 0, -4]}]
}}>
Hello
</Text>
</View>
);
}
};
AppRegistry.registerComponent('SpaceGallery', () => SpaceGallery);
请注意,如果你使用Platform
或VecPlat
作为你的对象,你的代码会更短。
请注意,我使用了MyX='-5.1'
而不是MyX={5.1}
。这样做是可以的,但实际上是不正确的。{}
用于将 JS 插入代码中。基本上,如果你想让MyX
成为一个数字,就在初始化列表中使用{}
。JavaScript 会进行转换,但有时如果你没有将数字作为数字传递,可能会导致奇怪的行为。
你可能已经注意到,React VR 包含的基本原语有点稀疏。没有建设性的实体几何,通常称为布尔运算,或者本地 three.js,你可以创建的东西是有限的。
幸运的是,你可以从其他 CAD 程序中导入文件。我们将在第六章《与 Poly 和 Gon 家族合作》中更多地介绍这一点,但现在,你可以导入一些我在书中的文件中包含的模型,网址是bit.ly/VR_Chap5
。
添加基座
在我们把艺术品放到世界上之前,我们需要建造一个基座。为了更容易对齐,我们可以像之前一样创建一个对象。让我们制作一个方形基座,并在顶部和底部放一个盖子。
如果你为了胜利而结束了一天的工作,那就重新启动 React VR 服务器吧;为了方便起见,我们可以复制 URL,这样我们就可以将其粘贴到我们的网络浏览器中:
如果您选择了前面的文本粘贴到浏览器中,您将等待很长时间。请注意地址栏中显示的“选择 npm”。
当您使用控制台启动应用程序时,如果“选择”任何文本,并保持其选定状态,可能会阻止 Web 浏览器提供内容。
因此,我们将不再使用简单的立方体作为地板,而是创建一个带有顶部和底部的方形“基座”:
class Pedestal extends Component {
render() {
return (
<Box
dimWidth={.4}
dimDepth={.4}
dimHeight={.5}
lit
texture={asset('travertine_striata_vein_cut_honed_filled_Base_Color.jpg')}
style={{
transform: [ { translate: [ this.props.MyX, -1.4, this.props.MyZ] } ]
}}
/>
<Box
dimWidth={.5}
dimDepth={.5}
dimHeight={.1}
lit
texture={asset('travertine_striata_vein_cut_honed_filled_Base_Color.jpg')}
style={{
transform: [ { translate: [ this.props.MyX, -1.1, this.props.MyZ] } ]
}}
/>
<Box
dimWidth={.5}
dimDepth={.5}
dimHeight={.1}
lit
texture={asset('travertine_striata_vein_cut_honed_filled_Base_Color.jpg')}
style={{
transform: [ { translate: [ this.props.MyX, -1.7, this.props.MyZ] } ]
}}
/>
)
}
}
现在,当您尝试这样做时,您会收到一个错误:
Adjacent JSX elements must be wrapped in an enclosing tag (31:10)
请记住,这是 React VR;在常规的 React 中,您会将多个标签包含在<div>
语句中。这在这里行不通,因为我们不处理 HTML;最接近的是 React-Native。因此,对于 VR,我们希望将多个元素/对象包装在<View>
语句中。因此,正确的代码是这样的:
class Pedestal extends Component {
render() {
return (
<View>
<box etc='...'/>
<snipped for='brevity'/>
</View>
}
}
如果您在 Web 控制台中收到错误Expected a component class, got [object Object]
,可能是您意外地输入了 view 而不是View
。
现在我们已经设置了“基座”对象,将其粘贴到您的index.vr.js
中,放在所有平台的下面:
<Platform MyX={ 0.0} MyZ={-5.1}/>
<Platform MyX={ 0.0} MyZ={ 0.0}/>
<Platform MyX={ 0.0} MyZ={ 5.1}/>
<Platform MyX={ 5.1} MyZ={-5.1}/>
<Platform MyX={ 5.1} MyZ={ 0.0}/>
<Platform MyX={ 5.1} MyZ={ 5.1}/>
<Platform MyX={-5.1} MyZ={-5.1}/>
<Platform MyX={-5.1} MyZ={ 0.0}/>
<Platform MyX={-5.1} MyZ={ 5.1}/>
<Pedestal MyX={ 0.0} MyZ={-5.1}/>
<Pedestal MyX={ 0.0} MyZ={ 0.0}/>
<Pedestal MyX={ 0.0} MyZ={ 5.1}/>
<Pedestal MyX={ 5.1} MyZ={-5.1}/>
<Pedestal MyX={ 5.1} MyZ={ 0.0}/>
<Pedestal MyX={ 5.1} MyZ={ 5.1}/>
<Pedestal MyX={-5.1} MyZ={-5.1}/>
<Pedestal MyX={-5.1} MyZ={ 0.0}/>
<Pedestal MyX={-5.1} MyZ={ 5.1}/>
现在,我们有一系列漂亮的“基座”。我们所做的另一件事是,通过仔细的缩放,每个物体在二维空间中都位于相同的位置[5.1 … 0 … -5.1]。这将使导入各种艺术品更容易。
正如我们在创建其他类或组件时所看到的,React VR 中的项目组合有点困难。它并不打算成为一个完整的 3D 建模工具;它是一个 VR 演示系统。因此,任何真正复杂的对象都应该在 CAD 系统中创建。在那里,您将拥有某种类型的视觉建模,比起尝试估计堆叠时<Box>
的偏移量要容易得多。
我们将使用 Model 语句。在第四章“React VR 库”中,我们详细介绍了 Model 关键字。现在是使用它的时候了!在主代码中的<View>
语句内部添加以下行,放在它下面的“基座”上。不要忘记更改import
行!
<Model
source={{
obj: asset('teapot2.obj'),
mtl: asset('teapot2.mtl'),
}}
lit
style={{
transform: [{ translate: [ -5.1, -1, -5.1 ] }]
}}
/>
我从互联网来源和 Blender 中进行了一些 UV 编辑,创建了一个版本的犹他茶壶,这是'teapot2.obj'
对象。在 Blender 中创建对象可能是一整本书的主题,也可能是,所以现在,您可以从书中的文件中下载茶壶。它们位于bit.ly/VR_Chap5
的static_assets
文件夹中。
这个有点不同,因为它有一个橡胶把手,蓝色珐琅和一个铜龙头。目前,材料文件(.mtl 文件)只有简单的颜色,但在下一章中,我们将学习如何使用纹理贴图使它们丰富多彩。
如果你得到Model is not defined
,这意味着你忘记在文件顶部的import
行中添加Model
。
继续保存,你会看到我们添加了一个茶壶!然而,有一些问题 - 甲板看起来有点无聊,边缘有点奇怪,我们想看到茶壶看起来有点不同。我们可以通过在下一章中创建自己的模型来实现这一点。
摘要
恭喜!你的画廊现在完成了,只有一个简单的物体。你已经学会了如何修改我们的世界,改变背景使它看起来像我们想要的任何地方,还学会了如何创建对象组并实例化它们。接下来,你将学会如何用更多的模型来填充它;阅读下一章以了解详情!