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開發基礎。

翻譯若有不當之處,請指正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM