WebVR 和浏览器边缘计算革命
已发表: 2022-03-11一个巨大的技术浪潮已经到来——虚拟现实(VR)。 无论您第一次拿着智能手机时的感受如何,第一次体验 VR 都会在计算的各个方面提供更丰富的情感体验。 距离第一部 iPhone 仅 12 年。 作为一个概念,VR 的存在时间更长,但将 VR 带给普通用户所需的技术直到最近才出现。
Oculus Quest 是 Facebook 的 VR 消费者游戏平台。 它的主要特点是不需要PC。 它提供无线、移动 VR 体验。 你可以在咖啡店里给某人一个 VR 头显来分享一个 3D 模型,这与在对话中使用谷歌搜索一样尴尬,但共享体验的回报更引人注目。
VR 将改变我们工作、购物、享受内容等的方式。
在本系列中,我们将探索当前支持 WebVR 和浏览器边缘计算的浏览器技术。 第一篇文章重点介绍了这些技术和我们的模拟架构。
在接下来的文章中,我们将重点介绍代码中的一些独特挑战和机遇。 为了探索这项技术,我制作了一个 Canvas 和 WebVR 演示,并将代码发布在 GitHub 上。
VR 对用户体验意味着什么
作为一名 Toptal 开发人员,我帮助企业将项目从构思转变为与用户一起进行的 beta 测试。 那么 VR 与商业 Web 应用程序有什么关系呢?
娱乐内容将引领虚拟现实的普及(就像它在移动设备上所做的那样)。 然而,一旦 VR 像移动一样成为主流,“VR-first design”将是预期的体验(类似于“mobile-first”)。
“移动优先”是一种范式转变,“线下优先”是当前的范式转变,“VR 优先”即将出现。 对于设计师和开发人员来说,这是一个非常激动人心的时刻,因为 VR 是一种完全不同的设计范式(我们将在本系列的最后一篇文章中探讨这一点)。 如果你不能抓握,你就不是 VR 设计师。
VR 始于个人计算 (PC) 革命,但正在作为移动革命的下一步到来。 Facebook 的 Oculus Quest 基于 Google Cardboard 构建,使用高通公司的 Snapdragon 835 系统级芯片 (SoC)、耳机跟踪(使用移动摄像头),并在 Android 上运行——所有这些都经过封装,可以舒适地安装在您脸部的柔软感觉器官上。
400 美元的 Oculus Quest 拥有我可以与朋友分享的惊人体验。 一部价值 1,000 美元的新 iPhone 不再给任何人留下深刻印象。 人类不会吐出虚拟现实的钩子。
VR行业应用
VR 开始在众多行业和计算领域中得到体现。 除了经常受到媒体报道的内容消费和游戏之外,VR 正在慢慢改变从建筑到医疗保健的行业。
- 建筑和房地产以非凡的成本(与数字相比)创造了物理现实的价值,因此建筑师和房地产经纪人很自然地适合通过虚拟现实来展示客户体验。 VR 提供了对价值 2 亿美元体育场的“beta 测试”,或通过电话进行的虚拟演练。
- VR 中的学习和教育传达了原本无法通过图像或视频复制的体验。
- 汽车公司从 VR 中受益,从设计和安全到培训和营销。
- 斯坦福大学露西尔帕卡德儿童医院的医疗保健专业人员一直在使用 VR 来计划心脏手术,让他们在进行单个切口之前了解患者的解剖结构。 VR 也正在取代药物来缓解疼痛。
- 零售、营销和酒店业已经提供产品和地点的虚拟游览。 随着零售商开始了解他们的购物体验有多么引人注目,零售创新者将把最后的钉子钉在实体购物棺材上。
随着技术的进步,我们将看到各个行业的采用率增加。 现在的问题是这种转变将以多快的速度发生,哪些行业将受到最大的影响。
VR 对 Web 和边缘计算意味着什么
“边缘计算”将计算从您的主应用服务器集群中移出,并更靠近您的最终用户。 它引起了营销热议,因为托管公司迫不及待地在每个城市为您租用低延迟服务器。
B2C 边缘计算示例是 Google 的 Stadia 服务,它在 Google 的服务器上运行 CPU/GPU 密集型游戏工作负载,然后将游戏发送到 Netflix 等设备。 任何笨拙的 Netflix Chromebook 都可以突然像高端游戏电脑一样玩游戏。 这也为紧密集成的单片多人游戏创造了新的架构选项。
B2B 边缘计算示例是 Nvidia 的 GRID,它为廉价的 Netflix 类设备提供支持 Nvidia GPU 的虚拟远程工作站。
问题:为什么不将边缘计算从数据中心转移到浏览器中?
浏览器边缘计算的一个用例是计算机的“动画渲染农场”,它通过将长达一天的过程分解成数以千计的计算机可以在几分钟内处理的块来渲染 3D 电影。
Electron 和 NW.js 等技术将 Web 编程带入了桌面应用程序。 新的浏览器技术(如 PWA)正在将 Web 应用程序分发模型(SaaS 是关于分发)带回桌面计算。 示例包括 SETI@Home、Folding@Home(蛋白质折叠)或各种渲染农场等项目。 无需下载安装程序,现在只需访问网站即可加入计算场。
问题: WebVR 是“真实的东西”还是 VR 内容会被挤进“应用商店”和围墙花园?
作为一名 Toptal 自由职业者和技术专家,我的工作就是了解。 所以我建立了一个技术原型来回答我自己的问题。 我找到的答案非常令人兴奋,我写了这个博客系列与你分享。
剧透:是的,WebVR 是真实存在的。 是的,浏览器边缘计算可以使用相同的 API 来访问支持 WebVR 的计算能力。
写起来很有趣! 让我们建立一个概念证明
为了验证这一点,我们将对 n 体问题进行天体物理学模拟。
天文学家可以使用方程式来计算两个物体之间的引力。 然而,对于具有三个或更多物体的系统,没有方程,这对于已知宇宙中的每个系统来说都是不方便的。 科学!
虽然 n 体问题没有解析解(方程),但它确实有一个计算解(算法),即 O(n²)。 O(n²) 几乎是最坏的情况,但它是如何得到我们想要的,以及为什么首先发明大 O 表示法。
图 2: “向上和向右? 好吧,我不是工程师,但性能对我来说看起来不错!”
如果您正在学习大 O 技能,请记住大 O 表示法衡量算法的工作如何根据其操作的数据大小“扩展”。
我们的收藏是我们模拟中的所有物体。 添加新实体意味着为集合中的每个现有实体添加新的二实体重力计算。
虽然我们的内部循环是 < n,但它不是 <= O(log n),所以整个算法是 O(n²)。 这些是休息时间,没有额外的功劳。
for (let i: i32 = 0; i < numBodies; i++) { // n // Given body i: pair with every body[j] where j > i for (let j: i32 = i + 1; j < numBodies; j++) { // 1/2 n is > log n, so n. // Calculate the force the bodies apply to one another stuff = stuff } }
n-body 解决方案也让我们置身于物理/游戏引擎的世界,探索 WebVR 所需的技术。
对于我们的原型,一旦我们建立了模拟,我们将进行 2D 可视化。
最后,我们将把 Canvas 可视化换成 WebVR 版本。
如果你不耐烦,你可以直接跳到项目的代码。
Web Workers、WebAssembly、AssemblyScript、Canvas、Rollup、WebVR、Aframe
通过一系列已经出现在您现代移动浏览器中的新技术(对不起,不是您的 Safari),系好安全带,享受动感十足、充满乐趣的嬉戏:
- 我们将使用Web Workers将模拟移动到它自己的 CPU 线程中 - 提高感知和实际性能。
- 我们将使用WebAssembly在该新线程中的高性能(C/C++/Rust/AssemblyScript/等)编译代码中运行 O(n²) 算法。
- 我们将使用Canvas在 2D 中可视化我们的模拟。
- 我们将使用Rollup 和 Gulp作为 Webpack 的轻量级替代品。
- 最后,我们将使用WebVR和Aframe为您的手机创建虚拟现实。
深入研究代码之前的基本架构
我们将从 Canvas 版本开始,因为您可能正在工作中阅读此内容。
在前几篇文章中,我们将使用现有的浏览器 API 来访问创建 CPU 密集型模拟所需的计算资源,而不会降低用户体验。
然后,我们将在浏览器中使用 Canvas 将其可视化,最后将 Canvas 可视化替换为使用 Aframe 的 WebVR。
API 设计和仿真循环
我们的 n 体模拟使用重力预测天体的位置。 我们可以用方程计算两个对象之间的确切力,但要计算三个或更多对象之间的力,我们需要将模拟分解成小的时间段并进行迭代。 我们的目标是 30 帧/秒(电影速度)或 ~33 毫秒/帧。

为了获得指导,这里是代码的快速概述:
- 浏览器 GET 的 index.html
- 它运行
main.js
作为下面的代码。import
由 Rollup 处理,Rollup 是 Webpack 的替代品。 - 它创建了一个新的 nBodySimulator()
- 其中有一个外部 API:
- sim.addVisualization()
- sim.addBody()
- sim.start()
// src/main.js import { nBodyVisPrettyPrint, nBodyVisCanvas } from "./nBodyVisualizer" import { Body, nBodySimulator } from "./nBodySimulator" window.onload = function() { // Create a Simulation const sim = new nBodySimulator() // Add some visualizers sim.addVisualization(new nBodyVisPrettyPrint(document.getElementById("visPrettyPrint"))) sim.addVisualization(new nBodyVisCanvas(document.getElementById("visCanvas"))) // This is a simulation, using opinionated G = 6.674e-11 // So boring values are allowed and create systems that collapse over billions of years. // For spinny, where distance=1, masses of 1e10 are fun. // Set Z coords to 1 for best visualization in overhead 2D Canvas. // lol, making up stable universes is hard // name color xyzm vz vy vz sim.addBody(new Body("star", "yellow", 0, 0, 0, 1e9)) sim.addBody(new Body("hot jupiter", "red", -1, -1, 0, 1e4, .24, -0.05, 0)) sim.addBody(new Body("cold jupiter", "purple", 4, 4, -.1, 1e4, -.07, 0.04, 0)) // A couple far-out asteroids to pin the canvas visualization in place. sim.addBody(new Body("asteroid", "black", -15, -15, 0, 0)) sim.addBody(new Body("asteroid", "black", 15, 15, 0, 0)) // Start simulation sim.start() // Add another sim.addBody(new Body("saturn", "blue", -8, -8, .1, 1e3, .07, -.035, 0)) // That is the extent of my effort to handcraft a stable solar system. // We can now play in that system by throwing debris around (inner plants). // Because that debris will have significantly smaller mass, it won't disturb our stable system (hopefully :-) // This requires we remove bodies that fly out of bounds past our 30x30 space created by the asteroids. // See sim.trimDebris(). It's a bit hacky, but my client (me) doesn't want to pay for it and wants the WebVR version. function rando(scale) { return (Math.random()-.5) * scale } document.getElementById("mayhem").addEventListener('click', () => { for (let x=0; x<10; x++) { sim.addBody(new Body("debris", "white", rando(10), rando(10), rando(10), 1, rando(.1), rando(.1), rando(.1))) } }) }
这两颗小行星的质量为零,因此不受重力影响。 他们将我们的 2D 可视化缩小到至少 30x30。 最后一段代码是我们的“混乱”按钮,用于添加 10 个小内行星以获得一些有趣的乐趣!
接下来是我们的“模拟循环”——每 33 毫秒,重新计算和重新绘制。 如果你玩得开心,我们可以称之为“游戏循环”。 可能实现我们的循环的最简单的事情是setTimeout()
- 这实现了我的目的。 另一种方法是 requestAnimationFrame()。
sim.start()
通过每 33 毫秒(大约每秒 30 帧)调用sim.step()
) 来启动操作。
// Methods from class nBodySimulator // The simulation loop start() { // This is the simulation loop. step() calls visualize() const step = this.step.bind(this) setInterval(step, this.simulationSpeed) } // A step in the simulation loop. async step() { // Skip calculation if worker not ready. Runs every 33ms (30fps), so expect skips. if (this.ready()) { await this.calculateForces() } else { console.log(`Skipping: ${this.workerReady}, ${this.workerCalculating}`) } // Remove any "debris" that has traveled out of bounds - this is for the button this.trimDebris() // Now Update forces. Reuse old forces if we skipped calculateForces() above this.applyForces() // Ta-dah! this.visualize() }
欢呼! 我们正在从设计转向实施。 我们将在 WebAssembly 中实现物理计算,并在单独的 Web Worker 线程中运行它们。
nBodySimulator 包装了该实现的复杂性并将其拆分为几个部分:
-
calculateForces()
承诺计算要应用的力。- 这些主要是浮点运算并在 WebAssembly 中完成。
- 这些计算是 O(n²) 和我们的性能瓶颈。
- 我们使用 Web Worker 将它们移出主线程,以获得更好的感知和实际性能。
-
trimDebris()
删除任何不再有趣的碎片(并且会缩小我们的可视化)。 在) -
applyForces()
将计算的力应用于实体。 在)- 如果我们跳过calculateForces(),我们会重用旧的力量,因为工人已经很忙了。 这以准确性为代价提高了感知性能(消除抖动)。
- 即使计算时间超过 33ms,主 UI 线程也可以绘制旧势力。
-
visualize()
将物体数组传递给每个可视化器进行绘制。 在)
这一切都发生在 33 毫秒内! 我们可以改进这个设计吗? 是的。 好奇或有什么建议? 检查下面的评论。 如果您正在寻找先进的现代设计和实现,请查看开源的 Matter.js。
发射!
我在创作这个作品时玩得很开心,很高兴能与你分享。 跳完后见!
- 简介 - 本页
- Web Workers - 访问多个线程
- WebAssembly - 没有 JavaScript 的浏览器计算
- Rollup 和 Gulp - WebPack 的替代品
- Canvas - 绘制到 Canvas API 和“模拟循环”
- WebVR - 为 WebVR 和 Aframe 交换我们的 Canvas 可视化工具
娱乐将引领虚拟现实(如移动)的内容,但一旦 VR 成为常态(如移动),它将成为预期的消费者和生产力体验(如移动)。
我们从未像现在这样更有能力创造人类体验。 成为一名设计师和开发人员从未像现在这样激动人心。 忘记网页——我们将建立世界。
我们的旅程从不起眼的 Web Worker 开始,敬请期待我们 WebVR 系列的下一部分。