Babylon.js+WebXR(二)WebXR的AR功能特性

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功能的源代码中找到。

示例

AR测量      AR对象

posted @ 2021-08-18 11:40  慕流  阅读(1241)  评论(0编辑  收藏  举报