虛擬實境上線

以下是一些基本概念,可協助你準備好迎接各種沉浸式體驗,包括虛擬實境、擴增實境,以及介於兩者之間的體驗。

Joe Medley
Joe Medley

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

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

什麼是沉浸式網路?

雖然我們使用擴增實境和虛擬實境這兩個詞彙來描述沉浸式體驗,但許多人認為這兩者是從完全真實到完全虛擬的連續體,中間有不同程度的沉浸感。XR 中的「X」代表代數變數,可指沉浸式體驗範圍內的任何事物,反映出上述想法。

圖表:顯示從完全真實到完全沉浸的視覺體驗光譜。
沉浸式體驗的範圍

沉浸式體驗的例子包括:

  • 遊戲
  • 360 度影片
  • 在沉浸式環境中呈現傳統 2D (或 3D) 影片
  • 購屋
  • 購買產品前,先在家中檢視產品
  • 沉浸式藝術
  • 目前還沒有人想到的酷炫功能

概念和用法

我將說明使用 WebXR Device API 的幾個基本概念。如果需要比我提供的更深入資訊,請參閱 Immersive Web Working Group 的 WebXR 範例MDN 越來越豐富的參考資源。如果您熟悉 WebXR 裝置 API 的早期版本,建議您瀏覽所有這些資料。有變更。

本文中的程式碼是以 Immersive Web Working Group 的基本範例 (試用版來源) 為基礎,但經過編輯,為求明確且更清楚易懂。

在制定 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。所有繪圖作業都是使用這些 API 或以 WebGL 為基礎的架構 (例如 Three.js) 完成。

現在我有了繪圖位置,需要內容來源來繪製。為此,我會建立 XRWebGLLayer 的執行個體。我會呼叫 XRSession.updateRenderState(),將其與畫布建立關聯。

進入工作階段後,我需要判斷虛擬實境中的物體位置。我需要參考空間。'local-floor' 參考空間是指原點位於檢視者附近,且 y 軸位於地板層級,不會移動。還有其他類型的參照空間,但這比我在此說明的內容更複雜。我將參照空間儲存至變數,因為在繪製到畫面上時需要用到。

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()。 這是呈現虛擬內容的開端,會在影格迴圈中完成。

執行影格迴圈

影格迴圈是使用者代理程式控制的無限迴圈,內容會重複繪製到畫面上。內容會繪製在稱為影格的獨立區塊中。連續的影格會營造出移動的錯覺。如果是 VR 應用程式,每秒影格數可介於 60 到 144 之間。Android 適用的 AR 執行速度為每秒 30 個影格。程式碼不應假設任何特定影格率。

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

  1. 呼叫 XRSession.requestAnimationFrame()。使用者代理程式會呼叫您定義的 XRFrameRequestCallback
  2. 在回呼函式中:
    1. 再次撥打 XRSession.requestAnimationFrame()
    2. 取得觀眾的姿勢。
    3. XRWebGLLayer 中的 WebGLFramebuffer 傳遞 (「繫結」) 至 WebGLRenderingContext
    4. 疊代每個 XRView 物件,從 XRWebGLLayer 擷取 XRViewport,並傳遞至 WebGLRenderingContext
    5. 將內容繪製到訊框緩衝區。

本文的其餘部分將說明步驟 1 和步驟 2 的一部分,也就是設定及呼叫 XRFrameRequestCallback。步驟 2 的其餘項目將在第 II 部分中說明。

XRFrameRequestCallback

XRFrameRequestCallback 由您定義。這個函式會採用兩個參數:DOMHighResTimeStampXRFrame 執行個體。XRFrame 物件提供將單一影格算繪至螢幕所需的資訊。DOMHighResTimeStamp 引數供日後使用。

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

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

此時,你就可以為觀眾畫些東西。這是第 II 部分的討論內容。前往該處之前,請先瞭解如何結束工作階段。

結束工作階段

沉浸式工作階段可能因多種原因而結束,包括透過呼叫 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 應用程式所需的一切內容。 希望我提供的資訊足以協助您自行解讀程式碼,並開始進行實驗。在下一篇文章中,我將說明影格迴圈,也就是內容繪製到畫面的位置。