Babylon.js+WebXR(一)WebXR介绍

WebXR

前言

W3C出了一套包括但不限于WebVR/WebAR的规范。这个W3C组织名叫Immersive Web,即沉浸式网络,非常确切的概括了WebXR想要实现的效果。组织的目标是通过API将高性能虚拟现实(VR)和增强现实(AR)(统称为XR)引入开放式Web,以便与Web中的传感器和XR设备进行交互。

当前状态

WebXR W3C提案目前处于草案阶段。 然而,它已经在 Chrome 中实现了(查看 caniuse.com 可了解其他浏览器的WebXR实现情况)。 从Chrome的79版本开始,WebVR已被弃用,默认情况下启用WebXR。 早期的Chrome浏览器版本在配置选项里有WebXR选项,可选择打开。 其目标之一是弃用WebVR和其他AR实现,并提供单一的VR和AR API。

由于WebXR API还在不断变化和更新中,所以babylon.js很难跟上其功能和特性的变化。最新的chrome浏览器金丝雀版本(canary)是目前XR功能最完整的浏览器,而且谷歌也在不断持续的更新WebXR新功能。 这是我们引入功能管理器(Features Manager)的主要原因,它允许Babylon.js通过内部版本控制功能去实现WebXR最新版本的官方功能,而不会破坏向后兼容性。

请注意,大多数时候当我们说WebXR时,我们实际上是指VR沉浸式模式(VR immersive mode)下的WebXR。 这是目前WebXR最常用的模式。

设备和浏览器支持

PC

所有微软的混合现实设备使用的windows系统的Chrome 79版本浏览器都官方支持WebXR。 非官方的,WebXR 与 oculus SDK(Rift、Rift S 和 Quest with Link)配合良好。 在撰写本文时,对Oculus的官方支持仍未公布。

手机和Quest

Google Daydream设备使用Chrome浏览器可支持WebXR。

Android 的 Chrome 浏览器(Stable 和 Canary版本)上的 WebXR AR 功能可以在 chrome://flags 打开相应的功能配置标志后启用,包括平面检测、命中测试和锚点等 AR 功能。 请注意,AR功能的架构在不断变化,因此预计不同版本的结果会有所不同。

Oculus Quest设备在最新的oculus浏览器中支持 WebXR(在 VR 模式下)。 Babylon.js的规范实现在quest设备中运行良好。

目前没有正式的 iOS/iPhone 支持计划。 Mozilla 已经构建了一个在IOS系统能够运行的WebXR浏览器(WebXR iOS Viewer),这是一个(非常)受限的面向 AR 的浏览器。

Polyfill

对于支持 WebVR 但不支持 WebXR 的旧浏览器,您可以使用 WebXR Polyfill,它是使用WebXR API接口去实现了WebVR的功能 。 某些功能将无法运行(或将直接返回而不进行任何操作),但基本功能运行OK。

Babylon.js不打算将 polyfill 集成到框架本身或Playground中。 我们鼓励开发人员向那些不使用支持 WebXR 的浏览器的用户提供 polyfill。

要在 Playground 中使用 polyfill,请将以下内容添加到您的 Playground中(在 'createScene'函数之前):

1 const xrPolyfillPromise = new Promise((resolve) => {
2   if (navigator.xr) {
3     return resolve();
4   }
5   define("polyfill", ["https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js"], (polyfill) => {
6     new polyfill();
7     resolve();
8   });
9 });

 然后,确保在初始化 WebXR 之前等待该函数执行完成:

const xrPolyfillPromise = new Promise((resolve) => {
  if (navigator.xr) {
    return resolve();
  }
  define("polyfill", ["https://cdn.jsdelivr.net/npm/webxr-polyfill@latest/build/webxr-polyfill.js"], (polyfill) => {
    new polyfill();
    resolve();
  });
});

var createScene = async function () {
  // wait for the polyfill to kick in
  await xrPolyfillPromise;
  console.log(navigator.xr); // should be there!
  console.log(await BABYLON.WebXRSessionManager.IsSessionSupportedAsync("immersive-vr")); // should be true
  // create your scene
  var scene = new BABYLON.Scene(engine);
  var camera = new BABYLON.DeviceOrientationCamera("DevOr_camera", new BABYLON.Vector3(-30, -30, -30), scene);
  camera.setTarget(BABYLON.Vector3.Zero());
  camera.attachControl(canvas, true);
  var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 0, 0), scene);
  scale = 100;
  // initialize XR
  var xr = await scene.createDefaultXRExperienceAsync();

  return scene;
};

 如果在使用 polyfill 时遇到低分辨率,需要将画布大小调整为更高的分辨率。 这是WebVR的限制(需要调整画布大小),我们集成的WebXR没有这个限制 。

WebXR模拟器

如果您正在开发并且不想经常在真实设备上进行测试,请使用 mozilla 的 WebXR 模拟器,它适用于 chrome 和 firefox浏览器。 我们支持它并在开发过程中实际使用了它。 强烈推荐!

快速入门

最简单的入门方法是使用支持 WebXR 的浏览器并将一行代码添加到您的场景中:

const xr = scene.createDefaultXRExperienceAsync();

这将在 VR 沉浸式模式下(VR immersive mode)启用 WebXR,包括会话初始化、输入源、相机、隐形传态和场景交互。 全部使用我们的 WebXR 默认体验助手(WebXR Default Experience Helper)。

请注意, xr 变量是一个 Promise。 使用 async/await 模式会更简单、更直观。 定义地板网格(floor meshes)也很有意义,这样我们就可以定义我们的地面并在上面移动。 这是 XR 中的一个球体:

var createScene = async function () {
  var scene = new BABYLON.Scene(engine);
  var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
  camera.setTarget(BABYLON.Vector3.Zero());
  camera.attachControl(canvas, true);
  var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
  light.intensity = 0.7;
  var sphere = BABYLON.Mesh.CreateSphere("sphere1", 16, 2, scene);
  sphere.position.y = 1;

  const env = scene.createDefaultEnvironment();

  // here we add XR support
  const xr = await scene.createDefaultXRExperienceAsync({
    floorMeshes: [env.ground],
  });

  return scene;
};

 Demo链接

这样就可以了,是不是很简单?

请务必阅读有关WebXR Experience Helper的更多信息以获取更多提示和技巧,并查看我们的演示和示例

从WebVR迁移到WebXR

 WebVR 已被弃用,并且将很快在大多数浏览器中结束其生命周期。 强烈建议将所有 WebVR 实现移植到 WebXR。

从VR Experience helper迁移

如果您使用了我们的 VR 体验助手(WebVR experience helper),请移除 VR 初始化程序并添加 XR 体验助手(WebXR experience helper)。 所以代码由:

var scene = new BABYLON.Scene(engine);
var vrHelper = scene.createDefaultVRExperience();

转变成:

var scene = new BABYLON.Scene(engine);
var xrHelper = scene.createDefaultXRExperienceAsync();

默认情况下,WebXR助手具有完整的控制器支持,包括与场景网格、指针事件等的交互。 阅读有关WebXR体验助手的更多信息。

迁移控制器支持

由于 WebXR 控制器不再被视为游戏手柄,因此架构略有不同。

添加的最重要的功能是对控制器的完整指针事件支持。 控制器支持所有指针事件,因此您可以使用指针交互(Pointer interactions),就像在场景中控制鼠标交互一样。

同样需要注意的是,现在可以查询控制器具有哪些功能及其相关的操作。

以下是 VR 控制器最常用的功能,以及如何让它们在 XR 中工作:

 1 // On new controller attached:
 2 
 3 // WebVR:
 4 webvrCamera.onControllersAttached = (vrController) => {
 5   // fun with the new controller, which is a gamepad!
 6 };
 7 
 8 // WebXR:
 9 const webXRInput = xr.input; // if using the experience helper, otherwise, an instance of WebXRInput
10 input.onControllerAddedObservable.add((xrController /* WebXRInputSource instance */) => {
11   // more fun with the new controller, since we are in XR!
12   inputSource.onMotionControllerInitObservable.add((motionController) => {
13     // get the motionController, which is similar to but NOT a gamepad:
14   });
15   // xr supports all types of inputs, so some won't have a motion controller
16   if (!xrController.gamepad) {
17     // using touch, hands, gaze, something else?
18   }
19 });
20 
21 // From this point we assume we have two variables: vrController and xrController.
22 // We also assume motionController is present!
23 
24 // main button
25 
26 // WebVR:
27 controller.onMainButtonStateChangedObservable.add((newState /* ExtendedGamepadButton */) => {
28   // is the button pressed?
29   if (newState.pressed) {
30     // Do something
31   }
32 });
33 
34 // WebXR:
35 // get the main component (decided by the controller's vendor!)
36 const mainComponent = xrController.motionController.getMainComponent();
37 // or get the trigger component, if present:
38 const mainTrigger = xrController.motionController.getComponent(WebXRControllerComponent.TRIGGER);
39 mainComponent.onButtonStateChanged.add((component /* WebXRControllerComponent */) => {
40   // check for changes:
41   // pressed changed?
42   if (component.changes.pressed) {
43     // is it pressed?
44     if (component.changes.pressed.current === true) {
45       // pressed
46     }
47     // or a different way:
48     if (component.pressed) {
49       // component is pressed.
50     }
51   }
52 });
53 
54 // thumbpad / touchpad
55 
56 // in WebVR - you had to check what controller is being used, but in general this would work:
57 vrController.onPadValuesChangedObservable.add(function (stateObject) {
58   console.log(stateObject); // {x: 0.1, y: -0.3}
59 });
60 
61 // in webXR you can check if it is present and work accordingly:
62 const thumbstick = xrController.motionController.getComponent(WebXRControllerComponent.THUMBSTICK);
63 if (thumbstick) {
64   // Huzza! we have a thumbstick:
65   thumbstick.onButtonStateChanged; // will trigger when the thumbstick is PRESSED or touched!
66 
67   thumbstick.onAxisValueChanged; // will trigger when axes of the thumbstick changed
68 }
69 
70 // touchpad
71 
72 // in WebVR we had "pad" concept which was for both thumbstick and touchpad
73 controller.onPadValuesChangedObservable.add(function (stateObject) {
74   console.log(stateObject); // {x: 0.1, y: -0.3}
75 });
76 
77 // in WebXR, it is much much better:
78 const touchpad = xrController.motionController.getComponent(WebXRControllerComponent.TOUCHPAD);
79 if (touchpad) {
80   // Finally, a controller with a touchpad
81   touchpad.onButtonStateChanged; // will trigger when the touchpad is touched or pressed
82 
83   touchpad.onAxisValueChanged; // will trigger when axes of the touchpad changed
84 }

阅读有关XR 控制器系统的更多信息。

兼容性支持

 我们始终鼓励向后兼容,我们建议直接使用 WebXR 并停止使用 WebVR 体验助手(WebVR experience helper)。

当然:

最新的 WebVR 体验助手在其初始化选项中有一个新标志 - useXR。 这将检查是否支持WebXR,如果支持,则会在WebXR中启动VR会话。

一个工作示例参见WebVR demo

源代码如下:

 1 var createScene = function () {
 2   // Create scene
 3   var scene = new BABYLON.Scene(engine);
 4 
 5   // Create simple sphere
 6   var sphere = BABYLON.Mesh.CreateIcoSphere(
 7     "sphere",
 8     {
 9       radius: 0.2,
10       flat: true,
11       subdivisions: 1,
12     },
13     scene,
14   );
15   sphere.position.y = 3;
16   sphere.material = new BABYLON.StandardMaterial("sphere material", scene);
17 
18   // Lights and camera
19   var light = new BABYLON.DirectionalLight("light", new BABYLON.Vector3(0, -0.5, 1.0), scene);
20   light.position = new BABYLON.Vector3(0, 5, -2);
21   var camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 4, 3, new BABYLON.Vector3(0, 3, 0), scene);
22   camera.attachControl(canvas, true);
23   scene.activeCamera.beta += 0.8;
24 
25   // Default Environment
26   var environment = scene.createDefaultEnvironment({
27     enableGroundShadow: true,
28     groundYBias: 2.8,
29   });
30   environment.setMainColor(BABYLON.Color3.FromHexString("#74b9ff"));
31 
32   // Shadows
33   var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
34   shadowGenerator.useBlurExponentialShadowMap = true;
35   shadowGenerator.blurKernel = 32;
36   shadowGenerator.addShadowCaster(sphere, true);
37 
38   // Enable VR, use XR when possible
39   var vrHelper = scene.createDefaultVRExperience({
40     createDeviceOrientationCamera: false,
41     useXR: true, // This will enable XR if supported
42     floorMeshes: [environment.ground],
43   });
44 
45   // Runs every frame to rotate the sphere
46   scene.onBeforeRenderObservable.add(() => {
47     sphere.rotation.y += 0.0001 * scene.getEngine().getDeltaTime();
48     sphere.rotation.x += 0.0001 * scene.getEngine().getDeltaTime();
49   });
50 
51   // GUI
52   var plane = BABYLON.Mesh.CreatePlane("plane", 1);
53   plane.position = new BABYLON.Vector3(0.4, 4, 0.4);
54   var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateForMesh(plane);
55   var panel = new BABYLON.GUI.StackPanel();
56   advancedTexture.addControl(panel);
57   var header = new BABYLON.GUI.TextBlock();
58   header.text = "Color GUI";
59   header.height = "100px";
60   header.color = "white";
61   header.textHorizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
62   header.fontSize = "120";
63   panel.addControl(header);
64   var picker = new BABYLON.GUI.ColorPicker();
65   picker.value = sphere.material.diffuseColor;
66   picker.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
67   picker.height = "350px";
68   picker.width = "350px";
69   // This will work in XR, since we are using native pointer events!
70   picker.onValueChangedObservable.add(function (value) {
71     sphere.material.diffuseColor.copyFrom(value);
72   });
73   panel.addControl(picker);
74 
75   vrHelper.onAfterEnteringVRObservable.add(() => {
76     // This callback will still work! Would be better to use the XR native observables.
77   });
78 
79   return scene;
80 };

颜色选择器(The color picker)使用指针架构(The pointer architecture)来工作,用户点击(point)不同颜色区域,可以选择不用的颜色。 如果存在 XR,则将使用 XR。 否则,它将使用 WebVR 作为后备。

请注意,WebVR的某些功能将无法正常工作或根本无法工作。 例如,相机凝视(camera gaze)功能根本不起作用。 控制器可以工作,但由于交互架构不同,您很可能需要调整一些观察者(observers)才能使其工作,特别是专门针对VR的控制器回调。

我们建议在不支持WebXR的浏览器中,使用WebXR polyfill代替WebVR 体验助手(WebVR experience helper)进行WebVR开发

PS:

1)本文翻译自babylon.js官方WebXR文档,翻译用于自学和巩固知识,也希望能给有需要的人一点帮助;

2)阅读本文,需要具备一定的Web JavaScript,babylon.js,以及WebVR开发基础。

翻译若有不当之处,请指正。

 

posted @ 2021-07-09 16:22  慕流  阅读(3034)  评论(0编辑  收藏  举报