所謂的沉浸式網路,是指透過瀏覽器代管的虛擬世界體驗。無論是在瀏覽器中或支援 VR 的頭戴式裝置,你都會看到整場虛擬實境體驗。
沉浸式網路意味著透過瀏覽器託管的虛擬世界體驗。這涵蓋了瀏覽器或支援 VR 功能的頭戴式裝置 (例如 Google 的 Daydream、Oculus Rift、Samsung Gear VR、HTC Vive 和 Windows 混合實境頭戴式裝置) 所呈現的完整虛擬實境 (VR) 體驗,以及為支援 AR 的行動裝置開發的擴增實境體驗。
雖然我們使用兩個詞彙來描述沉浸式體驗,但應將其視為從完全現實到完全沉浸式 VR 環境的光譜,其中包含各種程度的 AR。
沉浸式體驗的範例包括:
- 沉浸式 360 度影片
- 沉浸式環境中呈現的傳統 2D (或 3D) 影片
- 資料示意圖
- 居家購物
- 藝術
- 酷,好記
該怎麼到那裡?
沉浸式網路的雛形已推出將近一年。這項功能是透過 WebVR 1.1 API 實現,該 API 自 Chrome 62 版起已在來源試用中提供。Firefox 和 Edge 也支援該 API,Safari 則支援用於填補的 API。
但現在是時候繼續前進了。
來源試用已於 2018 年 7 月 24 日結束,且 WebXR Device API 和新的來源試用已取代規格。
WebVR 1.1 發生什麼事?
我們從 WebVR 1.1 中獲益良多,但隨著時間過去,我們發現必須進行幾項重大變更,才能支援開發人員想建構的應用程式開發人員類型。學習的完整課程清單太長,無法完整呈現,但包括 API 明確與主要 JavaScript 執行緒關聯的問題、開發人員設定明顯錯誤的設定的機會,以及魔術視窗是一種副作用等常見用途,而非蓄意功能。(魔幻視窗是一種技術,可讓使用者在沒有頭戴式裝置的情況下觀看沉浸式內容,應用程式會根據裝置的方向感應器算繪單一檢視畫面)。
這項新設計可簡化導入程序,並大幅提升效能。與此同時,AR 和其他用途也開始出現,因此 API 必須能夠擴充,以便日後支援這些用途。
WebXR Device API 的設計和命名皆以這些擴充用途為考量,可提供更優質的發展方向。WebVR 實作人員已承諾遷移至 WebXR Device API。
什麼是 WebXR Device API?
與先前的 WebVR 規格一樣,WebXR Device API 也是 Immersive Web Community Group 的產品,後者有來自 Google、Microsoft、Mozilla 和其他公司的貢獻者。XR 中的「X」是屬於一種代數變數,代表各種沉浸式體驗中的任何項目。此功能在前述的來源試用和 polyfill 中皆可使用。
本文最初是在 Chrome 67 Beta 版期間發布時,並未啟用 VR 功能。擴增實境功能已在 Chrome 69 推出。如要瞭解相關資訊,請參閱「適用於網頁的擴增實境」一文。
這個新 API 還有很多,在以下文章中可以找到。我想為您提供足夠的資訊,以便開始瞭解 WebXR 範例。如要進一步瞭解,請參閱原始說明文件和沉浸式網頁早期採用者指南。隨著來源試驗的進展,我會擴大後者的範圍。歡迎您提出問題或提交拉取要求。
在本文中,我將說明如何啟動、停止及執行 XR 工作階段,以及處理輸入內容的幾個基本概念。
我不會介紹如何在螢幕上繪製 AR/VR 內容。WebXR Device API 不提供圖片算繪功能。您可以自行決定。繪製作業是透過 WebGL API 完成。但只要你勇於嘗試,就能夠辦到。 不過,我們建議您使用架構。沉浸式網頁範例使用專為這類示範所建立的 Cottontail。Three.js 自 5 月起就支援 WebXR。我沒有聽說過 A-Frame。
啟動及執行應用程式
基本流程如下:
- 索取 XR 裝置。
- 如果可用,請要求 XR 工作階段。如果您希望使用者將手機放入耳機,這稱為沉浸式工作階段,需要使用手勢才能進入。
- 使用工作階段執行轉譯迴圈,每秒提供 60 個圖片影格。在每個影格中繪製適當的內容。
- 執行轉譯迴圈,直到使用者決定結束為止。
- 結束 XR 工作階段。
讓我們進一步瞭解這項功能,並附上一些程式碼。您將無法透過我即將展示的內容執行應用程式。但這只是讓我瞭解一下
要求 XR 裝置
您會在這裡看到標準功能偵測程式碼。您可以將這個函式包裝在 checkForXR()
之類的函式中。
如果您未使用沉浸式工作階段,可以略過宣傳功能和取得使用者手勢,直接要求工作階段。沉浸式工作階段需要使用頭戴式裝置。非沉浸式工作階段只會在裝置螢幕上顯示內容。大多數人提到虛擬實境或擴增實境時,想到的都是前者。後者有時稱為「魔法視窗」。
if (navigator.xr) {
navigator.xr.requestDevice()
.then(xrDevice => {
// Advertise the AR/VR functionality to get a user gesture.
})
.catch(err => {
if (err.name === 'NotFoundError') {
// No XRDevices available.
console.error('No XR devices available:', err);
} else {
// An error occurred while requesting an XRDevice.
console.error('Requesting XR device failed:', err);
}
})
} else{
console.log("This browser does not support the WebXR API.");
}
要求 XR 工作階段
有了裝置和使用者手勢後,現在就可以取得工作階段。如要建立工作階段,瀏覽器需要用來繪圖的畫布。
xrPresentationContext = htmlCanvasElement.getContext('xrpresent');
let sessionOptions = {
// The immersive option is optional for non-immersive sessions; the value
// defaults to false.
immersive: false,
outputContext: xrPresentationContext
}
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Use a WebGL context as a base layer.
xrSession.baseLayer = new XRWebGLLayer(session, gl);
// Start the render loop
})
執行轉譯迴圈
這個步驟的程式碼需要稍微整理一下。為了釐清這個問題,我要向你拋出一些字詞。如果您想先查看最終程式碼,請提前快速查看,再回來查看完整說明。你可能無法推測出許多資訊。
轉譯迴圈的基本流程如下:
- 要求動畫影格。
- 查詢裝置的位置。
- 根據裝置的位置繪製內容。
- 執行輸入裝置所需的工作。
- 每秒重複 60 次,直到使用者決定退出為止。
要求呈現頁框
在 Web XR 環境中,「Frame」一詞有多種意義。第一個是參考架構,用來定義座標系統的起點,以及裝置移動時該起點會發生什麼事。(在使用者移動時,畫面是否會保持不變,還是會像在現實生活中一樣移動?)
第二種框架是「呈現框架」,以 XRFrame
物件表示。這個物件包含將 AR/VR 場景的單一影格算繪至裝置所需的資訊。這是因為呼叫 requestAnimationFrame()
會擷取簡報影格,所以這有點令人困惑。這會讓它與 window.requestAnimationFrame()
相容。
在提供更多內容供您消化之前,我會提供一些程式碼。以下範例說明如何啟動及維護轉譯迴圈。請注意出現文字框的雙重使用情況。請注意 requestAnimationFrame()
的遞迴呼叫。這個函式將每秒呼叫 60 次。
xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
// The time argument is for future use and not implemented at this time.
// Process the frame.
xrFrame.session.requestAnimationFrame(onFrame);
}
});
姿勢
在畫面中繪製任何內容之前,您必須知道顯示裝置指向的位置,而且需要存取螢幕。一般來說,AR/VR 中物件的方向和位置稱為姿勢。觀眾和輸入裝置都有姿勢。(稍後會說明輸入裝置)。觀看者和輸入裝置姿勢都定義為 4 x 4 矩陣,以列為主序儲存在 Float32Array
中。您可以針對目前的動畫影格物件呼叫 XRFrame.getDevicePose()
,藉此取得檢視器的姿勢。請務必測試是否收到回傳的姿勢。如果發生錯誤,您不想在畫面上繪製。
let pose = xrFrame.getDevicePose(xrFrameOfRef);
if (pose) {
// Draw something to the screen.
}
觀看次數
確認姿勢後,就可以開始繪製圖片了。您繪製的物件稱為檢視區塊 (XRView
)。這時就需要使用會話類型。系統會從 XRFrame
物件擷取檢視畫面,做為陣列。如果您處於非沉浸式工作階段,陣列就會有一個檢視畫面。如果您在沉浸式工作階段中,陣列中會有兩張,分別代表兩眼。
for (let view of xrFrame.views) {
// Draw something to the screen.
}
這是 WebXR 與其他沉浸式系統的重要差異。雖然逐一檢視一個檢視畫面似乎沒有意義,但這麼做可讓您為各種裝置提供單一轉譯路徑。
整個轉譯迴圈
如果我將所有程式碼一起處理,就會收到下方的程式碼。我已為輸入裝置留下預留位置,稍後會說明。
xrSession.requestFrameOfReference('eye-level')
.then(xrFrameOfRef => {
xrSession.requestAnimationFrame(onFrame(time, xrFrame) {
// The time argument is for future use and not implemented at this time.
let pose = xrFrame.getDevicePose(xrFrameOfRef);
if (pose) {
for (let view of xrFrame.views) {
// Draw something to the screen.
}
}
// Input device code will go here.
frame.session.requestAnimationFrame(onFrame);
}
}
結束 XR 工作階段
XR 工作階段可能會因多種原因結束,包括透過呼叫 XRSession.end()
結束您自己的程式碼。其他原因包括耳機中斷連線,或其他應用程式控制耳機。因此,良好的應用程式應監控結束事件,並在發生時捨棄工作階段和轉譯器物件。XR 工作階段一旦結束,就無法恢復。
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Create a WebGL layer and initialize the render loop.
xrSession.addEventListener('end', onSessionEnd);
});
// Restore the page to normal after immersive access has been released.
function onSessionEnd() {
xrSession = null;
// Ending the session stops executing callbacks passed to the XRSession's
// requestAnimationFrame(). To continue rendering, use the window's
// requestAnimationFrame() function.
window.requestAnimationFrame(onDrawFrame);
}
互動功能如何運作?
和應用程式生命週期一樣,我將大致說明如何在 AR 或 VR 中與物件互動。
WebXR Device API 採用「指向及點選」方式處理使用者輸入內容。採用這種方法後,每個輸入來源都會有一個定義的指標光線,用來指出輸入裝置指向的位置,以及事件,用來指出何時選取某個項目。應用程式會繪製指標光線,並顯示指標所指向的位置。當使用者按一下輸入裝置時,系統會觸發事件,具體來說是 select
、selectStart
和 selectEnd
。應用程式會判斷點選了哪個項目,並做出適當回應。
輸入裝置和指標光線
對使用者而言,指標光線只是控制器與所指向物件之間的淡淡線條。但應用程式必須繪製出來。也就是說,您必須取得輸入裝置的姿勢,並從其位置繪製一條線,連到 AR/VR 空間中的物件。大致流程如下:
let inputSources = xrSession.getInputSources();
for (let xrInputSource of inputSources) {
let inputPose = frame.getInputPose(inputSource, xrFrameOfRef);
if (!inputPose) {
continue;
}
if (inputPose.gripMatrix) {
// Render a virtual version of the input device
// at the correct position and orientation.
}
if (inputPose.pointerMatrix) {
// Draw a ray from the gripMatrix to the pointerMatrix.
}
}
這是 Immersive Web Community Group 提供的輸入追蹤範例的簡化版本。如同影格算繪,您可以自行決定繪製指標光線和裝置。如先前所述,此程式碼必須在轉譯迴圈中執行。
在虛擬空間中選取項目
僅在 AR/VR 中指向物品並無太大用處。如要執行任何有用的操作,使用者必須能夠選取項目。WebXR Device API 提供三個事件,用於回應使用者互動:select
、selectStart
和 selectEnd
。它們有一個我沒想到的怪異之處:只會告訴您有輸入裝置被點選。無從得知環境中點選了哪些項目。事件處理常式會新增至 XRSession
物件,且應在可用時立即新增。
xrDevice.requestSession(sessionOptions)
.then(xrSession => {
// Create a WebGL layer and initialize the render loop.
xrSession.addEventListener('selectstart', onSelectStart);
xrSession.addEventListener('selectend', onSelectEnd);
xrSession.addEventListener('select', onSelect);
});
如需更多背景資訊,請參閱這段程式碼的輸入選取範例。
如要找出點選的項目,請使用姿勢。(你是否感到驚訝?我不這麼認為。)這類詳細資料會因您使用的應用程式或架構而異,因此不在本文討論範圍內。請參閱輸入選取範例,瞭解 Cottontail 的做法。
function onSelect(ev) {
let inputPose = ev.frame.getInputPose(ev.inputSource, xrFrameOfRef);
if (!inputPose) {
return;
}
if (inputPose.pointerMatrix) {
// Figure out what was clicked and respond.
}
}
結論:展望未來
如同先前所述,Chrome 69 (Canary 版,預計於 2018 年 6 月推出) 將支援擴增實境。不過,我們建議您試試目前的解決方案。我們需要意見回饋來改善這項功能。如要瞭解進度,請查看 ChromeStatus.com 的 WebXR Hit Test。您也可以追蹤 WebXR 錨點,這麼做可以改善姿勢追蹤。