如果您已使用 WebXR Device API,就已完成大部分工作。
WebXR Device API 已在去年秋季的 Chrome 79 版推出。如先前所述,Chrome 的 API 實作功能仍在開發中。部分 Chrome 作業已完成 Chrome 很高興在此宣布Chrome 第 81 版推出了兩項新功能:
本文將說明擴增實境。如果您已用過 WebXR Device API,知道沒什麼新知。進入 WebXR 工作階段的步驟大致相同。執行影格迴圈的做法大致相同。差別在於允許在擴增實境中正確顯示內容的設定。如果您不熟悉 WebXR 的基本概念,請參閱我先前發布的 WebXR Device API 相關文章,或至少熟悉其中的內容。您應該瞭解如何要求及進入工作階段,以及如何執行影格迴圈。
如要進一步瞭解命中測試,請參閱相關文章「在實際檢視畫面中定位虛擬物件」。本文的程式碼是根據 Immersive Web Working Group 的 WebXR Device API 範例,採用沉浸式 AR 工作階段範例 (示範來源) 所編寫。
開始編寫程式碼前,請至少使用一次沉浸式 AR 工作階段範例。你必須使用搭載 Chrome 81 以上版本的新型 Android 手機。
這項功能有什麼用途?
擴增實境將成為許多現有或新網頁的寶貴附加功能,讓這些網頁不必離開瀏覽器就能實作 AR 用途。舉例來說,這項功能可協助使用者在教育網站上學習,也能讓潛在買家在購物時,將家中的物品以圖像呈現。
請考慮第二種用途。想像一下,在真實場景中模擬放置虛擬物件的真實大小。放置後,圖片會停留在所選表面上,並顯示實際物品在該表面上的大小,讓使用者可以移動圖片,以及靠近或遠離圖片。比起使用 2D 圖片,這樣可讓觀眾更深入瞭解物件。
我好像講得太快了。如要實際執行上述說明的操作,您需要 AR 功能和一些偵測表面的方式。本文將說明前者。關於 WebXR 命中測試 API 的隨附文章 (上方已提供連結) 將說明後者。
要求工作階段
要求工作階段的流程與您先前看到的非常相似。首先,請呼叫 xr.isSessionSupported()
,瞭解目前裝置是否支援所需的工作階段類型。您應要求 'immersive-ar'
,而非像之前一樣要求 'immersive-vr'
。
if (navigator.xr) {
const supported = await navigator.xr.isSessionSupported('immersive-ar');
if (supported) {
xrButton.addEventListener('click', onButtonClicked);
xrButton.textContent = 'Enter AR';
xrButton.enabled = supported; // supported is Boolean
}
}
這會啟用「進入 AR」按鈕。使用者點選時,請呼叫 xr.requestSession()
,並傳遞 'immersive-ar'
。
let xrSession = null;
function onButtonClicked() {
if (!xrSession) {
navigator.xr.requestSession('immersive-ar')
.then((session) => {
xrSession = session;
xrSession.isImmersive = true;
xrButton.textContent = 'Exit AR';
onSessionStarted(xrSession);
});
} else {
xrSession.end();
}
}
方便使用的屬性
您可能已經注意到,我在上一個程式碼範例中醒目顯示兩行程式碼。XRSession
物件似乎含有名為 isImmersive
的屬性。這是我自行建立的便利性屬性,並非規格規定的一部分。我會在稍後使用這項屬性,決定要向觀眾顯示哪些內容。為什麼這個屬性不是 API 的一部分?因為您的應用程式可能需要以不同的方式追蹤這項屬性,因此規格作者決定讓 API 保持簡潔。
進入工作階段
請回想先前文章中的 onSessionStarted()
長什麼樣子:
function onSessionStarted(xrSession) {
xrSession.addEventListener('end', onSessionEnded);
let canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
xrSession.requestReferenceSpace('local-floor')
.then((refSpace) => {
xrRefSpace = refSpace;
xrSession.requestAnimationFrame(onXRFrame);
});
}
我需要新增一些內容,才能算出擴增實境的算繪結果。關閉背景。首先,我要判斷是否需要背景。這是我要使用便利性屬性的第一個地方。
function onSessionStarted(xrSession) {
xrSession.addEventListener('end', onSessionEnded);
if (session.isImmersive) {
removeBackground();
}
let canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
xrSession.requestAnimationFrame(onXRFrame);
});
}
參考空間
我在先前的文章中略微提及參照空間。我要介紹的範例使用了兩個,因此我要修正這個遺漏。
參考空間描述虛擬世界與使用者的實體環境之間的關係。運作方式如下:
- 指定用於在虛擬世界中表示位置的座標系統原點。
- 指定使用者是否會在該座標系統中移動。
- 該座標系統是否已預先建立邊界。(這裡顯示的範例並未使用預先建立邊界的座標系統)。
對於所有參考空間,X 座標表示左右,Y 座標表示上下,Z 座標則表示前後。正值分別是向右、向上和向後。
XRFrame.getViewerPose()
傳回的座標取決於所要求的參考空間類型。進一步說明影格迴圈時目前,我們需要選取適合擴增實境的參照類型。這段程式碼同樣會使用我的便利性屬性。
let refSpaceType
function onSessionStarted(xrSession) {
xrSession.addEventListener('end', onSessionEnded);
if (session.isImmersive) {
removeBackground();
}
let canvas = document.createElement('canvas');
gl = canvas.getContext('webgl', { xrCompatible: true });
xrSession.updateRenderState({
baseLayer: new XRWebGLLayer(session, gl)
});
refSpaceType = xrSession.isImmersive ? 'local' : 'viewer';
xrSession.requestReferenceSpace(refSpaceType).then((refSpace) => {
xrSession.requestAnimationFrame(onXRFrame);
});
}
如果您曾查看沉浸式 AR 工作階段範例,就會發現一開始的場景是靜態的,並非完全的擴增實境。您可以用手指拖曳和滑動,在場景中四處移動。點選「START AR」後,背景就會消失,您可以移動裝置在場景中移動。這些模式使用不同的參照空間類型。上方醒目顯示的文字顯示了如何選取這項選項。使用以下參照類型:
local
:原點位於建立工作階段時的觀眾位置。這表示體驗不一定有明確的底部,且來源的確切位置可能因平台而異。雖然空間並未預先建立邊界,但預期內容可在旋轉以外不移動的情況下觀看。如同我們自己的 AR 範例所示
在空間中可能有部分動作
viewer
:最常用於在網頁中內嵌的內容,此空格會依據觀看裝置而定。傳遞至 getViewerPose 時,它不會提供追蹤功能,因此除非應用程式使用 XRReferenceSpace.getOffsetReferenceSpace()
修改,否則一律會回報原點的姿勢。這個範例會用這個方式啟用相機的觸控平移功能。
執行影格迴圈
從概念上來說,這與我在先前文章中所述的 VR 工作階段中所做的一切都沒有任何不同。將參照空間類型傳遞至 XRFrame.getViewerPose()
。傳回的 XRViewerPose
將適用於目前的參照空間類型。使用 viewer
做為預設值,可讓網頁在要求使用者同意使用 AR 或 VR 前,先顯示內容預覽畫面。這說明了一個重要重點:內嵌內容會與沉浸式內容使用相同的框架迴圈,因此可減少需要維護的程式碼數量。
function onXRFrame(hrTime, xrFrame) {
let xrSession = xrFrame.session;
xrSession.requestAnimationFrame(onXRFrame);
let xrViewerPose = xrFrame.getViewerPose(refSpaceType);
if (xrViewerPose) {
// Render based on the pose.
}
}
結論
這一系列文章僅涵蓋在網路上實作沉浸式內容的基本知識。Immersive Web Working Group 的 WebXR Device API 範例提供了更多功能和用途。我們也剛發布一篇命中測試文章,說明用於偵測表面並在實際相機檢視畫面中放置虛擬項目的 API。歡迎前往查看,並觀看 web.dev 網誌,瞭解今年還會提供哪些文章。
David Grandmougin 拍攝的 Unsplash 相片