虛擬實境上線

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

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 移至 xrXRSession 執行個體。在早期版本的 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.
}

此時,您可以為觀眾繪製圖像。這部分將在第 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 應用程式時需要的所有資訊。希望我提供的資訊足以讓您開始瞭解程式碼,並開始進行實驗。在下一篇文章中,我將說明影格迴圈,也就是將內容繪製至螢幕的過程。

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