所謂的沉浸式網頁,是指透過瀏覽器代管的虛擬世界體驗。整個虛擬實境體驗會在瀏覽器或支援 VR 的頭戴式裝置中顯示。
沉浸式網頁是指透過瀏覽器代管的虛擬世界體驗。這項政策涵蓋在瀏覽器或支援 VR 的頭戴式裝置 (例如 Google Daydream、Oculus Rift、Samsung Gear VR、HTC Vive 和 Windows Mixed Reality 頭戴式裝置) 中顯示的完整虛擬實境 (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 和其他公司的貢獻者。「X」在 XR 中是一種代數變數,代表沉浸式體驗的範圍。這項功能可在前述的來源試用中使用,也可以透過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 情境中,「框架」一詞有幾種含義。第一個是參考架構,用來定義座標系統的起點,以及裝置移動時該起點會發生什麼事。(在使用者移動時,畫面是否會保持不變,還是會像在現實生活中一樣移動?)
第二種影格是呈現影格,由 XRFrame
物件代表。這個物件包含將 AR/VR 場景的單一影格算繪至裝置所需的資訊。這會造成一點混淆,因為系統會透過呼叫 requestAnimationFrame()
擷取呈現頁框。這會讓它與 window.requestAnimationFrame()
相容。
在提供更多內容供您消化之前,我會先提供一些程式碼。以下範例說明如何啟動及維護轉譯迴圈。請注意「frame」一詞的雙重用法。請注意 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 命中測試。您也可以追蹤 WebXR 錨點,這麼做可以改善姿勢追蹤。