虛擬實境上線

幾項基本概念,讓您瞭解虛擬實境、擴增實境和其他相關技術的各種沉浸式體驗。

Joe Medley
Joe Medley

在 Chrome 79 版中,我們為網路帶來了沉浸式體驗。WebXR Device API 可提供虛擬實境,而 Chrome 81 則支援擴增實境。而 GamePad API 的更新則可將控制項的進階用途擴展至 VR。其他瀏覽器很快也會支援這些規格,包括 Firefox Reality、Oculus Browser、Edge 和 Magic Leap 的 Helio 瀏覽器等。

本文是一系列有關沉浸式網路的文章的第一篇。本篇文章將說明如何設定基本 WebXR 應用程式,以及進入和退出 XR 工作階段。後續文章將介紹影格迴圈 (WebXR 體驗的核心)、擴增實境的具體資訊,以及 WebXR 命中測試 API,這是用於在 AR 工作階段中偵測表面的工具。除非另有說明,否則我在本篇和後續文章中介紹的所有內容都同樣適用於 AR 和 VR。

雖然我們使用兩個詞彙來描述沉浸式體驗 (擴增實境和虛擬實境),但許多人認為這兩者之間的差異,是從完全真實到完全虛擬的光譜,而沉浸式體驗則介於兩者之間。XR 中的「X」是為了反映這種想法,它是一種代數變數,代表沉浸式體驗範圍內的任何事物。

圖表說明從完全現實到完全沉浸式視覺體驗的範圍。
身歷其境的體驗範圍

沉浸式體驗的例子包括:

  • 遊戲
  • 360 度影片
  • 以身歷其境的方式呈現傳統 2D (或 3D) 影片
  • 購屋
  • 購買產品前,先在家中查看產品
  • 沉浸式藝術
  • 有什麼有趣的東西,是目前還沒有人想到的

概念和用法

我將說明使用 WebXR Device API 的幾項基本概念。如果您需要更深入的資訊,請參閱 Immersive Web Working Group 的 WebXR 範例,或是 MDN 不斷增加的參考資料。如果您熟悉 WebXR Device API 的早期版本,建議您瀏覽所有這類內容。已進行變更。

本文的程式碼是以 Immersive Web Working Group 的簡易範例為基礎 (demosource),但已經過編輯,以便清楚且簡單地說明。

在制定 WebXR 規格時,我們也著重於強化安全性和隱私權措施,以保護使用者。因此,實作方式必須遵守特定規定。網頁或應用程式必須處於啟用和聚焦狀態,才能向觀眾要求任何機密資訊。網頁或應用程式必須透過 HTTPS 提供。API 本身的設計目的,是保護從感應器和相機取得的資訊,這些資訊是 API 運作所需的資訊。

要求工作階段

使用者必須用手勢才能進入 XR 工作階段。如要取得該值,請使用功能偵測功能測試 XRSystem (透過 navigator.xr),然後呼叫 XRSystem.isSessionSupported()。請注意,在 Chrome 79 和 80 版中,XRSystem 物件稱為 XR

在下方範例中,我已表示希望使用 'immersive-vr' 工作階段類型的虛擬實境工作階段。其他工作階段類型'immersive-ar''inline'。內嵌式工作階段用於在 HTML 中呈現內容,主要用於預告片內容。沉浸式 AR 工作階段範例就是這麼做的。我會在後續文章中說明。

確認支援虛擬實境工作階段後,我會啟用按鈕,讓我取得使用者手勢。

if (navigator.xr) {
 
const supported = await navigator.xr.isSessionSupported('immersive-vr');
 
if (supported) {
    xrButton
.addEventListener('click', onButtonClicked);
    xrButton
.textContent = 'Enter VR';
    xrButton
.enabled = supported; // supported is Boolean
 
}
}

啟用按鈕後,我會等待點擊事件,然後要求工作階段。

let xrSession = null;
function onButtonClicked() {
 
if (!xrSession) {
    navigator
.xr.requestSession('immersive-vr')
   
.then((session) => {
      xrSession
= session;
      xrButton
.textContent = 'Exit XR';
      onSessionStarted
(xrSession);
   
});
 
} else {
    xrSession
.end();
 
}
}

請注意這段程式碼中的物件階層。從 navigator 移至 xr,再移至 XRSession 例項。在早期版本的 API 中,指令碼必須先要求裝置,才能要求工作階段。裝置現在會隱含取得。

進入工作階段

取得工作階段後,我需要啟動並進入工作階段。不過,我需要先設定一些項目。工作階段需要 onend 事件處理常式,才能在使用者離開時重設應用程式或網頁。

我還需要一個 <canvas> 元素來繪製場景。必須是與 XR 相容的 WebGLRenderingContextWebGL2RenderingContext。所有繪圖作業都是使用這些函式或以 WebGL 為基礎的架構 (例如 Three.js) 完成。

有了繪圖位置後,我需要內容來源才能繪製圖片。為此,我建立 XRWebGLLayer 的例項。我會呼叫 XRSession.updateRenderState() 將其與畫布建立關聯。

進入工作階段後,我需要一種方法來判斷虛擬實境中的物件位置。我需要參考空間。'local-floor' 參考空間的來源位於觀眾附近,且 y 軸位於地板層級的 0,且不會移動。當然,還有其他類型的參照空間,但這項主題較為複雜,我無法在此深入探討。我將參照空間儲存至變數,因為我需要在畫到畫面時使用這個空間。

function onSessionStarted(xrSession) {
  xrSession
.addEventListener('end', onSessionEnded);

  let canvas
= document.createElement('canvas');
  webGLRenContext
= canvas.getContext('webgl', { xrCompatible: true });

  xrSession
.updateRenderState({
    baseLayer
: new XRWebGLLayer(xrSession, webGLRenContext)
 
});

  xrSession
.requestReferenceSpace('local-floor')
 
.then((refSpace) => {
    xrRefSpace
= refSpace;
    xrSession
.requestAnimationFrame(onXRFrame);
 
});
}

取得參考空間後,我會呼叫 XRSession.requestAnimationFrame()。這是呈現虛擬內容的開始,會在影格迴圈中執行。

執行影格迴圈

影格迴圈是使用者代理程式控制的無限迴圈,會將內容重複繪製到螢幕上。內容會以稱為影格 (frame) 的獨立區塊繪製。畫格的連續顯示會產生移動的錯覺。對於 VR 應用程式,每秒影格數可介於 60 到 144 之間。Android 版 AR 的執行速度為每秒 30 張影格。程式碼不應假設任何特定的幀率。

影格迴圈的基本流程如下:

  1. 呼叫 XRSession.requestAnimationFrame()。在回應中,使用者代理程式會叫用您定義的 XRFrameRequestCallback
  2. 在回呼函式中:
    1. 再次撥打 XRSession.requestAnimationFrame()
    2. 取得觀眾的姿勢。
    3. WebGLFramebufferXRWebGLLayer 傳遞 ('bind') 至 WebGLRenderingContext
    4. 針對每個 XRView 物件執行疊代,從 XRWebGLLayer 擷取其 XRViewport,並將其傳遞至 WebGLRenderingContext
    5. 將內容繪製至 Framebuffer。

本文的其餘部分將說明步驟 1 和步驟 2 的部分內容,說明如何設定及呼叫 XRFrameRequestCallback。步驟 2 的其餘項目將在第 II 部分說明。

XRFrameRequestCallback

XRFrameRequestCallback 是由您定義。這個方法會使用兩個參數:DOMHighResTimeStampXRFrame 例項。XRFrame 物件會提供將單一影格算繪至螢幕所需的資訊。DOMHighResTimeStamp 引數是供日後使用。

在執行其他操作之前,我要要求下一個動畫影格。如先前所述,使用者代理程式會根據底層硬體決定影格時間。先要求下一個影格,可確保在回呼期間發生錯誤時,影格迴圈會繼續執行。

function onXRFrame(hrTime, xrFrame) {
  let xrSession
= xrFrame.session;
  xrSession
.requestAnimationFrame(onXRFrame);
 
// Render a frame.
}

此時,您可以為觀眾繪製圖像。這部分將在第 2 部分討論。在前往該頁面之前,讓我先示範如何結束工作階段。

結束工作階段

沉浸式工作階段可能會因多種原因結束,包括透過呼叫 XRSession.end() 結束您自己的程式碼。其他原因包括耳機未連線或其他應用程式控制耳機。因此,良好的應用程式應監控 end 事件。發生這種情況時,請捨棄工作階段及其相關的轉譯物件。已結束的沉浸式工作階段無法恢復。如要重新進入沉浸式體驗,我的應用程式需要啟動新的工作階段。

請回想「進入工作階段」一文中提到,我在設定期間新增了 onend 事件處理常式。

function onSessionStarted(xrSession) {
  xrSession
.addEventListener('end', onSessionEnded);
 
// More setup…
}

在事件處理常式中,還原使用者進入工作階段前的應用程式狀態。

function onSessionEnded(event) {
  xrSession
= null;
  xrButton
.textContent = 'Enter VR';
}

結論

我並未說明編寫 Web XR 或 AR 應用程式所需的所有內容。希望我提供的資訊足以讓您開始瞭解程式碼,並開始進行實驗。在下一篇文章中,我將說明影格迴圈,也就是將內容繪製至螢幕的過程。

相片來源:JESHOOTS.COM 發布於 Unsplash