WebXR的AR功能特性
關於增強現實(AR)
增強現實(AR)背后的想法很簡單——展示真實世界,但可以在真實世界中添加信息。
這點與虛擬現實(VR)不同,虛擬現實讓你完全沉浸在不同的場景中,與現實世界沒有實際接觸,增強現實讓你與現實世界互動。
快速入門
WebXR和AR
使用Babylon.js構建增強現實(AR)將大量使用WebXR,所以我建議您首先開始使用WebXR入門指南。
大多數對沉浸式VR會話有效的信息也對沉浸式AR會話有效。這里將解釋兩者之間的幾個主要區別。
支持設備
沉浸式AR會話(目前)在兩種類型的設備上得到支持——手機和Hololens上的firefox reality瀏覽器。
使用android的手機支持chrome(穩定/金絲雀版本)上的沉浸式AR會話。請注意,您將需要安裝AR Core,否則這將是一個非常短暫的體驗。
Hololens 2在使用Firefox Reality for Hololens時支持WebXR和沉浸式AR會話。
要在桌面上檢查場景,可以使用WebXR emulator,它支持AR的一組功能,並允許您在選擇移動模式時進入沉浸式AR會話。
沉浸式AR(immersive AR)中的簡單場景示例
最簡單的沉浸式AR示例如下所示:
1 var createScene = async function () { 2 var scene = new BABYLON.Scene(engine); 3 var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene); 4 camera.setTarget(BABYLON.Vector3.Zero()); 5 camera.attachControl(canvas, true); 6 var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene); 7 light.intensity = 0.7; 8 var sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene); 9 sphere.position.y = 2; 10 sphere.position.z = 5; 11 12 const xr = await scene.createDefaultXRExperienceAsync({ 13 // ask for an ar-session 14 uiOptions: { 15 sessionMode: "immersive-ar", 16 }, 17 }); 18 19 return scene; 20 };
官方Playground的簡單AR示例鏈接:simple immersive AR scene
我們可以注意到,AR示例中沒有創建任何環境。
與沉浸式VR會話相反,AR不需要天空盒或地面。
如果您想在進入AR模式時將已經定義好的地面移除(例如,如果您開發的應用同時支持桌面和AR兩種模式),您可以使用本頁后面描述的背景移除功能。
AR特性
一些AR的功能特性需要在最新的chrome canary版本中啟動相關配置。訪問chrome://flags/瀏覽器配置頁面,並啟用WebXR incubation配置選項。
然后,您需要在您的代碼中讓WebXR啟用這些功能特性。這可以使用default experience helper中的optionalFeature參數完成:
1 const xr = await scene.createDefaultXRExperienceAsync({ 2 uiOptions: { 3 sessionMode: "immersive-ar", 4 }, 5 optionalFeatures: true, 6 });
上述代碼將啟用我們支持的所有可選功能。您也可以使用數組來定制添加自己需要的功能特性:
1 const xr = await scene.createDefaultXRExperienceAsync({ 2 uiOptions: { 3 sessionMode: "immersive-ar", 4 }, 5 optionalFeatures: ["hit-test", "anchors"], 6 });
Hit test
Hit-test(命中測試)用於向現實世界發送光線,並接收有關空間相交的信息。您可以在hit test w3c draft中了解它。
想象一條光線從您的手機屏幕向您正在尋找的物體發射。如果設備的AR功能允許,您將知道相交點相對於您的位置和方向。
啟用hit-testing,需要在初始化XR后添加此如下代碼:
1 // featuresManager from the base webxr experience helper 2 const hitTest = featuresManager.enableFeature(BABYLON.WebXRHitTest, "latest");
在typescript中,您還可以獲取正確設置的類型:
1 // featuresManager from the base webxr experience helper 2 const hitTest = featuresManager.enableFeature(BABYLON.WebXRHitTest, 'latest') as BABYLON.WebXRHitTest;
啟用hit-testing后的默認行為,即在每一幀上從顯示器中心向前發送一條hit-test ray。
如下接口描述了您可以傳遞的可選參數:
1 export interface IWebXRHitTestOptions { 2 /** 3 * Do not create a permanent hit test. Will usually be used when only 4 * transient inputs are needed. 5 */ 6 disablePermanentHitTest?: boolean; 7 /** 8 * Enable transient (for example touch-based) hit test inspections 9 */ 10 enableTransientHitTest?: boolean; 11 /** 12 * Offset ray for the permanent hit test 13 */ 14 offsetRay?: Vector3; 15 /** 16 * Offset ray for the transient hit test 17 */ 18 transientOffsetRay?: Vector3; 19 /** 20 * Instead of using viewer space for hit tests, use the reference space defined in the session manager 21 */ 22 useReferenceSpace?: boolean; 23 }
disablePermanentHitTest選項將禁用持續hit-testing,而enableTransientHitTest選項將在觸摸屏幕時啟用hit-testing。offsets幾個參數代表偏移,定義光線發射起點的偏移量(相對於設備視圖的中心)。
啟用hit-testing功能后,您可以注冊OnHitTestResultoServable函數以獲取hit-testing的更新數據:
1 // a dot to show in the found position 2 const dot = BABYLON.SphereBuilder.CreateSphere( 3 "dot", 4 { 5 diameter: 0.05, 6 }, 7 scene, 8 ); 9 dot.isVisible = false; 10 hitTest.onHitTestResultObservable.add((results) => { 11 if (results.length) { 12 dot.isVisible = true; 13 results[0].transformationMatrix.decompose(dot.scaling, dot.rotationQuaternion, dot.position); 14 } else { 15 dot.isVisible = false; 16 } 17 });
上述代碼實現了功能:在hit-test有效時顯示圓點,如果無效時,則將隱藏圓點。圓點通過使用系統提供的信息,被投影到現實世界中。
基於Babylon.js的WebXR hit-test的一個簡單示例:WebXR Hit-Test Using Babylon.js
用你的AR設備(可能是你的android智能手機)打開它,點擊設備中的一個紋理平面(比如你的地板或門)。如果/當系統正確掃描平面時,標記將顯示在正確位置。
瞄點(Anchors)
瞄點是空間中的跟蹤點,當您掃描環境時,系統將不斷更新這些點。點的轉換將由底層系統不斷更新。
您可以在WebXR anchors module w3c proposal中閱讀更多關於瞄點的信息。
使用以下命令啟用瞄點系統:
1 // featuresManager from the base webxr experience helper 2 const anchorSystem = featuresManager.enableFeature(BABYLON.WebXRAnchorSystem, "latest");
或對於typescript:
1 // featuresManager from the base webxr experience helper 2 const anchorSystem = featuresManager.enableFeature(BABYLON.WebXRAnchorSystem, 'latest') as BABYLON.WebXRAnchorSystem;
一些選項可以傳遞給錨點系統:
1 export interface IWebXRAnchorSystemOptions { 2 /** 3 * a node that will be used to convert local to world coordinates 4 */ 5 worldParentNode?: TransformNode; 6 7 /** 8 * If set to true a reference of the created anchors will be kept until the next session starts 9 * If not defined, anchors will be removed from the array when the feature is detached or the session ended. 10 */ 11 doNotRemoveAnchorsOnSessionEnded?: boolean; 12 }
退出XR會話時會默認刪除錨點(請注意,只刪除錨點,連接到錨點的數據不會被刪除),這是推薦的行為,因為在會話之間無法相互引用錨點。
如果要防止這種情況發生,請在初始化錨定系統時設置DonotRemoveAnchorSessionEnded:
const anchorSystem = featuresManager.enableFeature(BABYLON.WebXRAnchorSystem, "latest", { doNotRemoveAnchorsOnSessionEnded: true });
當您要在hit test位置添加錨點時會發現,瞄點系統和hit-test特性匹配的非常完美。使用addAnchorPointUsingHitTestResultAsync函數完成這個任務:
1 const arTestResult = getMeTheResultINeed(); 2 const anchorPromise = anchorSystem.addAnchorPointUsingHitTestResultAsync(lastHitTest);
要在場景中的任何位置和旋轉中添加錨點,請使用AddAnchorPositionAndRotationAsync函數:
1 const { position, rotationQuaternion } = anyRandomMesh; 2 const anchorPromise = anchorSystem.addAnchorAtPositionAndRotationAsync(position, rotationQuaternion);
注意到,anchorPromise將在完成時返回一個內部的XRAnchor對象,它將為您提供瀏覽器返回的內容。
為了使用babylon錨點,我們使用瞄點模塊中定義的observables:
1 anchorSystem.onAnchorAddedObservable.add((anchor) => { 2 // ... do what you want with the anchor after it was added 3 }); 4 5 anchorSystem.onAnchorRemovedObservable.add((anchor) => { 6 // ... do what you want with the anchor after it was removed 7 }); 8 9 anchorSystem.onAnchorUpdatedObservable.add((anchor) => { 10 // ... do what you want with the anchor after it was updated 11 });
瞄點類型為IWebXRAnchor:
1 export interface IWebXRAnchor { 2 /** 3 * A babylon-assigned ID for this anchor 4 */ 5 id: number; 6 /** 7 * Transformation matrix to apply to an object attached to this anchor 8 */ 9 transformationMatrix: Matrix; 10 /** 11 * The native anchor object 12 */ 13 xrAnchor: XRAnchor; 14 15 /** 16 * if defined, this object will be constantly updated by the anchor's position and rotation 17 */ 18 attachedNode?: TransformNode; 19 }
要將錨點附着到一個節點上(例如,節點為始終位於場景中某位置的模型對象),請使用attachedNode變量。當瞄點更新時,其附加的模型對象的變換也會更新:
1 const mesh = anchorSystem.onAnchorAddedObservable.add((anchor) => { 2 //... 3 anchor.attachedNode = mesh; 4 });
mesh模型對象現在將由系統跟蹤,並將放置在指定的位置。
您可能會問自己,為什么要使用帶有hit-test結果的瞄點系統,當hit-test結果由系統根據設備定義的一個位置返回。
在hit-test的位置放置mesh模型對象將非常有效。
不同的是,系統可能會更新關於這個位置的信息——可能它發現平面處於不同的變換,也可能它更新了它在空間中的位置。
使用瞄點系統將保持變換更新,即使系統更新了其空間知識。
平面檢測
您的設備(通常)能夠檢測現實世界中的平面幾何圖形。要了解更多有關平面檢測的信息,請訪問平面檢測說明。
Babylon有一個實驗性的平面探測模塊,與底層系統一起工作。要啟用它,請執行以下操作:
1 // featuresManager from the base webxr experience helper 2 const planeDetector = featuresManager.enableFeature(BABYLON.WebXRPlaneDetector, "latest");
與任何模塊一樣,您可以使用選項對象(屬於以下類型)對其進行配置:
1 export interface IWebXRPlaneDetectorOptions { 2 /** 3 * The node to use to transform the local results to world coordinates 4 */ 5 worldParentNode?: TransformNode; 6 7 /** 8 * If set to true a reference of the created planes will be kept until the next session starts 9 * If not defined, planes will be removed from the array when the feature is detached or the session ended. 10 */ 11 doNotRemovePlanesOnSessionEnded?: boolean; 12 }
與瞄點系統類似,平面不會在會話之間停留。如果要保留本地的XRPlane對象,請將doNotRemovePlanesOnSessionEnded設置為true,Babylon將不會刪除它們。
平面探測器是自動工作的,並提供三個observables供您使用:
1 planeDetector.onPlaneAddedObservable.add((plane) => { 2 // ... do what you want with the plane after it was added 3 }); 4 5 planeDetector.onPlaneRemovedObservable.add((plane) => { 6 // ... do what you want with the plane after it was removed 7 }); 8 9 planeDetector.onPlaneUpdatedObservable.add((plane) => { 10 // ... do what you want with the plane after it was updated 11 });
平面對象的類型為IWebXRPlane:
1 export interface IWebXRPlane { 2 /** 3 * a babylon-assigned ID for this polygon 4 */ 5 id: number; 6 /** 7 * an array of vector3 points in babylon space. right/left hand system is taken into account. 8 */ 9 polygonDefinition: Array<Vector3>; 10 /** 11 * A transformation matrix to apply on the mesh that will be built using the polygonDefinition 12 * Local vs. World are decided if worldParentNode was provided or not in the options when constructing the module 13 */ 14 transformationMatrix: Matrix; 15 /** 16 * the native xr-plane object 17 */ 18 xrPlane: XRPlane; 19 }
要根據平面信息創建Babylon多邊形,請執行以下操作:
1 const plane = // a reference to an added plane 2 // add the starting point, so the polygon will close 3 plane.polygonDefinition.push(plane.polygonDefinition[0]); 4 // create a polygon mesh builder for the polygons returned from the system 5 var polygon_triangulation = new BABYLON.PolygonMeshBuilder( 6 "name", 7 plane.polygonDefinition.map((p) => new BABYLON.Vector2(p.x, p.z)), 8 scene, 9 ); 10 // build the plane with specific thickness 11 var polygon = polygon_triangulation.build(false, 0.01);
平面的一個簡單用例是使用多邊形表示平面,在場景中中進行展示。在WebXR平面檢測演示中可以找到一個示例:
背景移除
在AR模式下,應避免使用像天空框和地面這樣的環境網格對象(除非您的目標是保留它們)。
如果您正在創建一個場景,該場景既能在常規設備模式下,又能在AR模式下工作,則您將希望能夠在進入AR模式時禁用某些網格對象,並在離開AR模式時重新啟用它們。
此模塊正是這樣做的。它接收網格對象列表,並在需要時禁用/啟用網格對象。
當使用babylon環境助手(babylon environment helper)時,模塊可以自動為您完成工作。在這種情況下,如果啟用該功能,skybox和ground將自動刪除。
要啟用它,請使用:
1 const xrBackgroundRemover = featuresManager.enableFeature(BABYLON.WebXRBackgroundRemover);
要自定義模塊的工作方式,請使用以下配置對象:
1 export interface IWebXRBackgroundRemoverOptions { 2 /** 3 * Further background meshes to disable when entering AR 4 */ 5 backgroundMeshes?: AbstractMesh[]; 6 /** 7 * flags to configure the removal of the environment helper. 8 * If not set, the entire background will be removed. If set, flags should be set as well. 9 */ 10 environmentHelperRemovalFlags?: { 11 /** 12 * Should the skybox be removed (default false) 13 */ 14 skyBox?: boolean, 15 /** 16 * Should the ground be removed (default false) 17 */ 18 ground?: boolean, 19 }; 20 /** 21 * don't disable the environment helper 22 */ 23 ignoreEnvironmentHelper?: boolean; 24 }
例如,如果希望模塊僅移除skybox而不移除地面,則在使用環境輔助對象時,請通過以下方式啟用該功能:
1 const xrBackgroundRemover = featuresManager.enableFeature(BABYLON.WebXRBackgroundRemover, "latest", { 2 environmentHelperRemovalFlags: { 3 skyBox: true, 4 ground: false, 5 }, 6 });
DOM覆蓋
在AR模式下,可能需要顯示DOM元素。
啟用DOM覆蓋時,功能元素是唯一必需的選項,可以是DOM元素或字符串(使用傳遞到document.querySelector時返回的第一個元素)。
enableFeature的最后一個參數可能對您很重要,可以將此功能設置為可選。
1 const featuresManager = xr.baseExperience.featuresManager; 2 const domOverlayFeature = featuresManager.enableFeature(BABYLON.WebXRDomOverlay, "latest", { element: ".dom-overlay-container" }, undefined, false); 3 4 xr.baseExperience.onStateChangedObservable.add((webXRState) => { 5 switch (webXRState) { 6 case BABYLON.WebXRState.ENTERING_XR: 7 case BABYLON.WebXRState.IN_XR: 8 // domOverlayType will be null when not supported. 9 console.log("overlay type:", domOverlayFeature.domOverlayType); 10 break; 11 } 12 });
當進入AR模式后,可以檢查DOM覆蓋類型的特征;如果瀏覽器中支持該功能,domOverlayType將為非空。
最新的選項可以在WebXR DOM overlay功能的源代碼中找到。