Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

Web &
Réalité Virtuelle

Dominique Hazael‑Massieux

dom@w3.org

@dontcallmeDOM

A dystopian view of VR
Using VR to help shopping
VR reporting on Syria war at the New York Times
Some of the VR headsets on the market

Platforme de Réalité Virtuelle

WebGL
WebVR
Web Audio API

IsWebVRready.org

Dispo par défaut :
Firefox Nightly

Dispo mais désactivé par défaut :
Chrome Samsung Internet Firefox

En cours de développement :
Edge

Gamepad API

      (function () {
      "use strict";
      var PLAYER_HEIGHT = 1.65;
      var vrDisplay = null;
      var projectionMat = mat4.create();
      var viewMat = mat4.create();
      var poseMat = mat4.create();
      var gamepadMat = mat4.create();
      var gamepadColor = vec4.create();
      var standingPosition = vec3.create();
      var vrPresentButton = null;
      var orientation = [0, 0, 0, 1];
      var position = [0, 0, 0];
      // ===================================================
      // WebGL scene setup. This code is not WebVR specific.
      // ===================================================
      // WebGL setup.
      var webglCanvas = document.getElementById("webgl-canvas");
      var gl = null;
      var cubeIsland = null;
      var stats = null;
      var debugGeom = null;
      function initWebGL (preserveDrawingBuffer) {
        var glAttribs = {
          alpha: false,
          antialias: !VRSamplesUtil.isMobile(),
          preserveDrawingBuffer: preserveDrawingBuffer
        };
        gl = webglCanvas.getContext("webgl", glAttribs);
        gl.clearColor(0.1, 0.2, 0.3, 1.0);
        gl.enable(gl.DEPTH_TEST);
        gl.enable(gl.CULL_FACE);
        var textureLoader = new WGLUTextureLoader(gl);
        var texture = textureLoader.loadTexture("media/textures/cube-sea.png");
        cubeIsland = new VRCubeIsland(gl, texture, 2, 2);
        stats = new WGLUStats(gl);
        debugGeom = new WGLUDebugGeometry(gl);
        // Wait until we have a WebGL context to resize and start rendering.
        window.addEventListener("resize", onResize, false);
        onResize();
        window.requestAnimationFrame(onAnimationFrame);
      }
      // ================================
      // WebVR-specific code begins here.
      // ================================
      function onVRRequestPresent () {
        vrDisplay.requestPresent([{ source: webglCanvas }]).then(function () {
        }, function () {
          VRSamplesUtil.addError("requestPresent failed.", 2000);
        });
      }
      function onVRExitPresent () {
        if (!vrDisplay.isPresenting)
          return;
        vrDisplay.exitPresent().then(function () {
        }, function () {
          VRSamplesUtil.addError("exitPresent failed.", 2000);
        });
      }
      function onVRPresentChange () {
        onResize();
        if (vrDisplay.isPresenting) {
          if (vrDisplay.capabilities.hasExternalDisplay) {
            VRSamplesUtil.removeButton(vrPresentButton);
            vrPresentButton = VRSamplesUtil.addButton("Exit VR", "E", "media/icons/cardboard64.png", onVRExitPresent);
          }
        } else {
          if (vrDisplay.capabilities.hasExternalDisplay) {
            VRSamplesUtil.removeButton(vrPresentButton);
            vrPresentButton = VRSamplesUtil.addButton("Enter VR", "E", "media/icons/cardboard64.png", onVRRequestPresent);
          }
        }
      }
      if (navigator.getVRDisplays) {
        navigator.getVRDisplays().then(function (displays) {
          if (displays.length > 0) {
            vrDisplay = displays[0];
            vrDisplay.depthNear = 0.1;
            vrDisplay.depthFar = 1024.0;
            initWebGL(true);
            if (vrDisplay.stageParameters &&
                vrDisplay.stageParameters.sizeX > 0 &&
                vrDisplay.stageParameters.sizeZ > 0) {
              cubeIsland.resize(vrDisplay.stageParameters.sizeX, vrDisplay.stageParameters.sizeZ);
            }
            VRSamplesUtil.addButton("Reset Pose", "R", null, function () { vrDisplay.resetPose(); });
            if (vrDisplay.capabilities.canPresent)
              vrPresentButton = VRSamplesUtil.addButton("Enter VR", "E", "media/icons/cardboard64.png", onVRRequestPresent);
            window.addEventListener('vrdisplaypresentchange', onVRPresentChange, false);
            window.addEventListener('vrdisplayactivate', onVRRequestPresent, false);
            window.addEventListener('vrdisplaydeactivate', onVRExitPresent, false);
          } else {
            initWebGL(false);
            VRSamplesUtil.addInfo("WebVR supported, but no VRDisplays found.", 3000);
          }
        });
      } else if (navigator.getVRDevices) {
        initWebGL(false);
        VRSamplesUtil.addError("Your browser supports WebVR but not the latest version. See <a href='http://webvr.info'>webvr.info</a> for more info.");
      } else {
        initWebGL(false);
        VRSamplesUtil.addError("Your browser does not support WebVR. See <a href='http://webvr.info'>webvr.info</a> for assistance.");
      }
      function onResize () {
        if (vrDisplay && vrDisplay.isPresenting) {
          var leftEye = vrDisplay.getEyeParameters("left");
          var rightEye = vrDisplay.getEyeParameters("right");
          webglCanvas.width = Math.max(leftEye.renderWidth, rightEye.renderWidth) * 2;
          webglCanvas.height = Math.max(leftEye.renderHeight, rightEye.renderHeight);
        } else {
          webglCanvas.width = webglCanvas.offsetWidth * window.devicePixelRatio;
          webglCanvas.height = webglCanvas.offsetHeight * window.devicePixelRatio;
        }
      }
      function getStandingViewMatrix (out, view) {
        if (vrDisplay.stageParameters) {
          mat4.invert(out, vrDisplay.stageParameters.sittingToStandingTransform);
          mat4.multiply(out, view, out);
        } else {
          mat4.identity(out);
          mat4.translate(out, out, [0, PLAYER_HEIGHT, 0]);
          mat4.invert(out, out);
          mat4.multiply(out, view, out);
        }
      }
      function getPoseMatrix (out, pose, isGamepad) {
        orientation = pose.orientation;
        position = pose.position;
        if (!orientation) { orientation = [0, 0, 0, 1]; }
        if (!position) {
          // If this is a gamepad without a pose set it out in front of us so
          // we can see it.
          position = isGamepad ? [0.1, -0.1, -0.5] : [0, 0, 0];
        }
        if (vrDisplay.stageParameters) {
          mat4.fromRotationTranslation(out, orientation, position);
          mat4.multiply(out, vrDisplay.stageParameters.sittingToStandingTransform, out);
        } else {
          vec3.add(standingPosition, position, [0, PLAYER_HEIGHT, 0]);
          mat4.fromRotationTranslation(out, orientation, standingPosition);
        }
      }
      function renderSceneView (projection, view, gamepads) {
        cubeIsland.render(projection, view, stats);
        debugGeom.bind(projection, view);
        // Render every gamepad with a pose we found
        for (var i = 0; i < gamepads.length; ++i) {
          var gamepad = gamepads[i];
          // Because this sample is done in standing space we need to apply
          // the same transformation to the gamepad pose as we did the
          // VRDisplay's pose.
          getPoseMatrix(gamepadMat, gamepad.pose, true);
          // Scaled down to from 1 meter to be something closer to the size of
          // a hand.
          mat4.scale(gamepadMat, gamepadMat, [0.1, 0.1, 0.1]);
          // Loop through all the gamepad's axes and rotate the cube by their
          // value.
          for (var j = 0; j < gamepad.axes.length; ++j) {
            switch (j%3) {
              case 0:
                mat4.rotateX(gamepadMat, gamepadMat, gamepad.axes[j] * Math.PI);
                break;
              case 1:
                mat4.rotateY(gamepadMat, gamepadMat, gamepad.axes[j] * Math.PI);
                break;
              case 2:
                mat4.rotateZ(gamepadMat, gamepadMat, gamepad.axes[j] * Math.PI);
                break;
            }
          }
          // Show the gamepad's cube as red if any buttons are pressed, blue
          // otherwise.
          vec4.set(gamepadColor, 0, 0, 1, 1);
          for (var j = 0; j < gamepad.buttons.length; ++j) {
            if (gamepad.buttons[j].pressed) {
              vec4.set(gamepadColor, gamepad.buttons[j].value, 0, 0, 1);
              break;
            }
          }
          debugGeom.drawBoxWithMatrix(gamepadMat, gamepadColor);
        }
      }
      var frameData = new VRFrameData();
      function onAnimationFrame (t) {
        stats.begin();
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        if (vrDisplay) {
          vrDisplay.requestAnimationFrame(onAnimationFrame);
          // Loop over every gamepad and if we find any that have a pose use it.
          var vrGamepads = [];
          var gamepads = navigator.getGamepads();
          for (var i = 0; i < gamepads.length; ++i) {
            var gamepad = gamepads[i];
            // The array may contain undefined gamepads, so check for that as
            // well as a non-null pose.
            if (gamepad) {
              if (gamepad.pose)
                vrGamepads.push(gamepad);
              if ("haptics" in gamepad && gamepad.haptics.length > 0) {
                for (var j = 0; j < gamepad.buttons.length; ++j) {
                  if (gamepad.buttons[j].pressed) {
                    // Vibrate the gamepad using to the value of the button as
                    // the vibration intensity.
                    gamepad.haptics[0].vibrate(gamepad.buttons[j].value, 100);
                    break;
                  }
                }
              }
            }
          }
          vrDisplay.getFrameData(frameData);
          if (vrDisplay.isPresenting) {
            gl.viewport(0, 0, webglCanvas.width * 0.5, webglCanvas.height);
            getStandingViewMatrix(viewMat, frameData.leftViewMatrix);
            renderSceneView(frameData.leftProjectionMatrix, viewMat, vrGamepads);
            gl.viewport(webglCanvas.width * 0.5, 0, webglCanvas.width * 0.5, webglCanvas.height);
            getStandingViewMatrix(viewMat, frameData.rightViewMatrix);
            renderSceneView(frameData.rightProjectionMatrix, viewMat, vrGamepads);
            vrDisplay.submitFrame();
          } else {
            gl.viewport(0, 0, webglCanvas.width, webglCanvas.height);
            mat4.perspective(projectionMat, Math.PI*0.4, webglCanvas.width / webglCanvas.height, 0.1, 1024.0);
            getStandingViewMatrix(viewMat, frameData.leftViewMatrix);
            renderSceneView(projectionMat, viewMat, frameData.pose);
            stats.renderOrtho();
          }
        } else {
          window.requestAnimationFrame(onAnimationFrame);
          // No VRDisplay found.
          gl.viewport(0, 0, webglCanvas.width, webglCanvas.height);
          mat4.perspective(projectionMat, Math.PI*0.4, webglCanvas.width / webglCanvas.height, 0.1, 1024.0);
          mat4.identity(viewMat);
          mat4.translate(viewMat, viewMat, [0, -PLAYER_HEIGHT, 0]);
          cubeIsland.render(projectionMat, viewMat, stats);
          stats.renderOrtho();
        }
        stats.end();
      }
      })();
// https://github.com/toji/webvr-samples/blob/master/XX-vr-controllers.html

A-Frame


  <a-scene>
     <a-sphere position="0 1.25 -1"
                  radius="1.25" color="#EF2D5E">
     </a-sphere>
     <a-plane rotation="-90 0 0" width="4" height="4" 
                 color="#7BC8A4"></a-plane>
     <a-sky color="#ECECEC"></a-sky>
  </a-scene>
      
Example of an AR frame scene
3D camera & computer vision

Et plus encore…

  • Intégration video 360° en HTML
  • Transformer les pages Web en espace de réalité virtuelle
  • streaming 3D temps-réel
  • Performance
  • Réalité Augmentée…

Pour en savoir+

Use a spacebar or arrow keys to navigate