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 系列的下一部分。