Créer une session de RA immersive à l'aide de WebXR

Cette page vous explique comment créer une application de RA immersive simple à l'aide de WebXR.

Pour commencer, vous devez disposer d'un environnement de développement compatible avec WebXR.

Créer une page HTML

WebXR nécessite une interaction de l'utilisateur pour pouvoir démarrer une session. Créez un bouton qui appelle activateXR(). Lors du chargement de la page, l'utilisateur peut utiliser ce bouton pour lancer l'expérience de RA.

Créez un fichier nommé index.html et ajoutez-y le code HTML suivant:

<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <title>Hello WebXR!</title>

  <!-- three.js -->
  <script src="https://unpkg.com/three@0.126.0/build/three.js"></script>
</head>
<body>

<!-- Starting an immersive WebXR session requires user interaction.
    We start this one with a simple button. -->
<button onclick="activateXR()">Start Hello WebXR</button>
<script>
async function activateXR() {
  // Add a canvas element and initialize a WebGL context that is compatible with WebXR.
  const canvas = document.createElement("canvas");
  document.body.appendChild(canvas);
  const gl = canvas.getContext("webgl", {xrCompatible: true});

  // To be continued in upcoming steps.
}
</script>
</body>
</html>

Initialiser three.js

Il ne se passe pas grand-chose lorsque vous appuyez sur le bouton "Démarrer". Pour configurer un environnement 3D, vous pouvez utiliser une bibliothèque de rendu pour afficher une scène.

Dans cet exemple, vous allez utiliser three.js, une bibliothèque de rendu 3D JavaScript qui fournit un moteur de rendu WebGL. Three.js gère le rendu, les caméras et les graphiques de scène, ce qui facilite l'affichage de contenu 3D sur le Web.

Créer une scène

Un environnement 3D est généralement modélisé sous la forme d'une scène. Créez un THREE.Scene contenant des éléments de RA. Le code suivant vous permet d'examiner un cadre coloré non éclairé en RA.

Ajoutez le code suivant en bas de la fonction activateXR():

const scene = new THREE.Scene();

// The cube will have a different color on each side.
const materials = [
  new THREE.MeshBasicMaterial({color: 0xff0000}),
  new THREE.MeshBasicMaterial({color: 0x0000ff}),
  new THREE.MeshBasicMaterial({color: 0x00ff00}),
  new THREE.MeshBasicMaterial({color: 0xff00ff}),
  new THREE.MeshBasicMaterial({color: 0x00ffff}),
  new THREE.MeshBasicMaterial({color: 0xffff00})
];

// Create the cube and add it to the demo scene.
const cube = new THREE.Mesh(new THREE.BoxBufferGeometry(0.2, 0.2, 0.2), materials);
cube.position.set(1, 1, 1);
scene.add(cube);

Configurer le rendu à l'aide de three.js

Pour voir cette scène en RA, vous devez disposer d'un moteur de rendu et d'une caméra. Le moteur de rendu utilise WebGL pour dessiner votre scène à l'écran. La caméra décrit la fenêtre d'affichage à partir de laquelle la scène est vue.

Ajoutez le code suivant en bas de la fonction activateXR():

// Set up the WebGLRenderer, which handles rendering to the session's base layer.
const renderer = new THREE.WebGLRenderer({
  alpha: true,
  preserveDrawingBuffer: true,
  canvas: canvas,
  context: gl
});
renderer.autoClear = false;

// The API directly updates the camera matrices.
// Disable matrix auto updates so three.js doesn't attempt
// to handle the matrices independently.
const camera = new THREE.PerspectiveCamera();
camera.matrixAutoUpdate = false;

Créer une XRSession

Le point d'entrée vers WebXR passe par XRSystem.requestSession(). Utilisez le mode immersive-ar pour pouvoir visualiser le rendu dans un environnement réel.

Un XRReferenceSpace décrit le système de coordonnées utilisé pour les objets du monde virtuel. Le mode 'local' convient mieux à une expérience de RA, car il offre un suivi stable et un espace de référence initial proche du spectateur.

Pour créer XRSession et XRReferenceSpace, ajoutez le code suivant en bas de la fonction activateXR():

// Initialize a WebXR session using "immersive-ar".
const session = await navigator.xr.requestSession("immersive-ar");
session.updateRenderState({
  baseLayer: new XRWebGLLayer(session, gl)
});

// A 'local' reference space has a native origin that is located
// near the viewer's position at the time the session was created.
const referenceSpace = await session.requestReferenceSpace('local');

Rendre la scène

Vous pouvez maintenant effectuer le rendu de la scène. XRSession.requestAnimationFrame() planifie un rappel qui s'exécute lorsque le navigateur est prêt à dessiner un frame.

Lors du rappel de l'image de l'animation, appelez XRFrame.getViewerPose() pour obtenir la pose de l'utilisateur par rapport à l'espace de coordonnées local. Cela permet de mettre à jour la caméra de la scène, en modifiant la façon dont l'utilisateur voit le monde virtuel avant que le moteur de rendu ne dessine la scène à l'aide de la caméra mise à jour.

Ajoutez le code suivant en bas de la fonction activateXR():

// Create a render loop that allows us to draw on the AR view.
const onXRFrame = (time, frame) => {
  // Queue up the next draw request.
  session.requestAnimationFrame(onXRFrame);

  // Bind the graphics framebuffer to the baseLayer's framebuffer
  gl.bindFramebuffer(gl.FRAMEBUFFER, session.renderState.baseLayer.framebuffer)

  // Retrieve the pose of the device.
  // XRFrame.getViewerPose can return null while the session attempts to establish tracking.
  const pose = frame.getViewerPose(referenceSpace);
  if (pose) {
    // In mobile AR, we only have one view.
    const view = pose.views[0];

    const viewport = session.renderState.baseLayer.getViewport(view);
    renderer.setSize(viewport.width, viewport.height)

    // Use the view's transform matrix and projection matrix to configure the THREE.camera.
    camera.matrix.fromArray(view.transform.matrix)
    camera.projectionMatrix.fromArray(view.projectionMatrix);
    camera.updateMatrixWorld(true);

    // Render the scene with THREE.WebGLRenderer.
    renderer.render(scene, camera)
  }
}
session.requestAnimationFrame(onXRFrame);

Exécuter Hello WebXR

Accédez au fichier WebXR sur votre appareil. Vous devriez voir un cube coloré de tous les côtés.

Ajouter un test de positionnement

Un moyen courant d'interagir avec le monde de la RA consiste à effectuer un test de positionnement, qui identifie l'intersection entre un rayon et une géométrie réelle. Dans Hello WebXR, vous allez utiliser un test de positionnement pour placer un tournesol dans le monde virtuel.

Supprimer le cube de démonstration

Supprimez le cube non allumé et remplacez-le par une scène comportant un éclairage:

const scene = new THREE.Scene();

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.3);
directionalLight.position.set(10, 15, 10);
scene.add(directionalLight);

Utiliser la fonctionnalité hit-test

Pour initialiser la fonctionnalité de test de positionnement, demandez une session avec la fonctionnalité hit-test. Recherchez le fragment requestSession() précédent et ajoutez-y hit-test:

const session = await navigator.xr.requestSession("immersive-ar", {requiredFeatures: ['hit-test']});

Ajouter un chargeur de modèle

Actuellement, la scène ne contient qu'un cube coloré. Pour rendre l'expérience plus intéressante, ajoutez un chargeur de modèle, qui permet de charger des modèles GLTF.

Dans la balise <head> de votre document, ajoutez three.js' GLTFLoader.

<!-- three.js -->
<script src="https://unpkg.com/three@0.126.0/build/three.js"></script>

<script src="https://unpkg.com/three@0.126.0/examples/js/loaders/GLTFLoader.js"></script>

Charger des modèles glTF

Utilisez le chargeur de modèle de l'étape précédente pour charger un réticule de ciblage et un tournesol depuis le Web.

Ajoutez ce code au-dessus de onXRFrame:

const loader = new THREE.GLTFLoader();
let reticle;
loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/reticle/reticle.gltf", function(gltf) {
  reticle = gltf.scene;
  reticle.visible = false;
  scene.add(reticle);
})

let flower;
loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/sunflower/sunflower.gltf", function(gltf) {
  flower = gltf.scene;
});

// Create a render loop that allows us to draw on the AR view.
const onXRFrame = (time, frame) => {

Créer une source de test de positionnement

Pour calculer des intersections avec des objets du monde réel, créez un XRHitTestSource à l'aide de XRSession.requestHitTestSource(). Le rayon utilisé pour les tests de positionnement a comme origine l'espace de référence viewer, ce qui signifie que le test de positionnement est effectué à partir du centre de la fenêtre d'affichage.

Pour créer une source de test de positionnement, ajoutez le code suivant après avoir créé l'espace de référence local:

// A 'local' reference space has a native origin that is located
// near the viewer's position at the time the session was created.
const referenceSpace = await session.requestReferenceSpace('local');

// Create another XRReferenceSpace that has the viewer as the origin.
const viewerSpace = await session.requestReferenceSpace('viewer');
// Perform hit testing using the viewer as origin.
const hitTestSource = await session.requestHitTestSource({ space: viewerSpace });

Dessiner un réticule de ciblage

Pour que l'emplacement du tournesol soit clair, ajoutez un réticule de ciblage à la scène. Ce réticule semble adhérer aux surfaces réelles, indiquant où le tournesol sera ancré.

XRFrame.getHitTestResults renvoie un tableau de XRHitTestResult et expose les intersections avec une géométrie réelle. Utilisez ces intersections pour positionner le réticule de ciblage sur chaque image.

camera.projectionMatrix.fromArray(view.projectionMatrix);
camera.updateMatrixWorld(true);

const hitTestResults = frame.getHitTestResults(hitTestSource);
if (hitTestResults.length > 0 && reticle) {
  const hitPose = hitTestResults[0].getPose(referenceSpace);
  reticle.visible = true;
  reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z)
  reticle.updateMatrixWorld(true);
}

Ajouter des interactions en appuyant sur l'écran

XRSession reçoit des événements select lorsque l'utilisateur effectue une action principale. Dans une session de RA, cela correspond à une pression sur l'écran.

Ajoutez ce code lors de l'initialisation pour faire apparaître un tournesol lorsque l'utilisateur appuie sur l'écran:

let flower;
loader.load("https://immersive-web.github.io/webxr-samples/media/gltf/sunflower/sunflower.gltf", function(gltf) {
  flower = gltf.scene;
});

session.addEventListener("select", (event) => {
  if (flower) {
    const clone = flower.clone();
    clone.position.copy(reticle.position);
    scene.add(clone);
  }
});

Tester le test de positionnement

Utilisez votre appareil mobile pour accéder à la page. Une fois que WebXR a compris l'environnement, le réticule devrait apparaître sur des surfaces réelles. Tapotez sur l'écran pour placer un tournesol, qui peut être vu sous tous les côtés.

Étapes suivantes