以下是一些基本概念,可協助你準備好迎接各種沉浸式體驗,包括虛擬實境、擴增實境,以及介於兩者之間的體驗。
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 相容的 WebGLRenderingContext 或 WebGL2RenderingContext。所有繪圖作業都是使用這些 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 個影格。程式碼不應假設任何特定影格率。
影格迴圈的基本流程如下:
- 呼叫
XRSession.requestAnimationFrame()。使用者代理程式會呼叫您定義的XRFrameRequestCallback。 - 在回呼函式中:
- 再次撥打
XRSession.requestAnimationFrame()。 - 取得觀眾的姿勢。
- 將
XRWebGLLayer中的WebGLFramebuffer傳遞 (「繫結」) 至WebGLRenderingContext。 - 疊代每個
XRView物件,從XRWebGLLayer擷取XRViewport,並傳遞至WebGLRenderingContext。 - 將內容繪製到訊框緩衝區。
- 再次撥打
本文的其餘部分將說明步驟 1 和步驟 2 的一部分,也就是設定及呼叫 XRFrameRequestCallback。步驟 2 的其餘項目將在第 II 部分中說明。
XRFrameRequestCallback
XRFrameRequestCallback 由您定義。這個函式會採用兩個參數:DOMHighResTimeStamp 和 XRFrame 執行個體。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 應用程式所需的一切內容。 希望我提供的資訊足以協助您自行解讀程式碼,並開始進行實驗。在下一篇文章中,我將說明影格迴圈,也就是內容繪製到畫面的位置。