擴增實境:你可能早就知道

如果您已使用過 WebXR Device API,代表您已完成大部分步驟。

Joe Medley
Joe Medley

WebXR Device API 於 Chrome 79 去年上市。如前所述,Chrome 的 API 實作仍在開發階段。Chrome 很高興在此宣布 部分工作已完成Chrome 第 81 版推出了兩項新功能:

本文將介紹擴增實境。如果已經使用過 WebXR Device API,您會發現其實沒有什麼新可以學習的地方。進入 WebXR 工作階段大致相同執行影格迴圈大致相同。兩者的差異在於設定能讓內容以擴增實境正確顯示內容。如果您不熟悉 WebXR 的基本概念,則應閱讀我之前在 WebXR Device API 上的文章,或至少熟悉其中涵蓋的主題。您應知道如何要求及進入工作階段,也應該瞭解如何執行影格迴圈

如要瞭解命中測試,請參閱「在實際檢視畫面中定位虛擬物件」一文。本文中的程式碼是以 Immersive Web Working Group WebXR Device API 範例中的 Immersive AR 工作階段範例 (示範 來源) 為基礎。

在深入探討程式碼前,應至少使用一次 Immersive AR 工作階段範例。請使用搭載 Chrome 81 以上版本的新式 Android 手機。

這項功能的用途為何?

擴增實境技術可讓許多現有或新網頁更加實用,因為讓他們不必離開瀏覽器就能實作 AR 用途。例如幫助使用者學習教育網站,讓潛在買家在購物時以視覺化的方式呈現家中的物品。

請考慮第二個應用實例。設想模擬在真實場景中,以真人大小呈現虛擬物件。放置完成後,圖片會維持在所選途徑上。如果實際項目位於該途徑上,圖片就會依原樣顯示,並能讓使用者在靠近或距離它的地方移動。比起雙維圖片,這種做法能讓檢視者更深入地瞭解物件。

我快要做好準備了。如要執行我所述的操作,您需要 AR 功能以及偵測途徑的一些方法。本文將介紹前者。WebXR Hit Test API (連結至上方) 的隨附的文章涵蓋後者。

要求工作階段

要求課程內容與先前看過的內容非常類似,首先,請呼叫 xr.isSessionSupported(),確認所需的工作階段類型是否適用於目前的裝置。請勿像之前一樣要求 'immersive-vr',而是要求 'immersive-ar'

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 工作階段範例,就會發現一開始的場景是靜態的,而且完全不是擴增實境。用手指拖曳及滑動即可在場景中四處移動。如果按一下「開始 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 上)