Créer une application de réalité augmentée à l'aide de l'API WebXR Device

1. Avant de commencer

Cet atelier de programmation propose un exemple de création d'une application Web de réalité augmentée (RA). Il utilise JavaScript pour générer des modèles 3D qui semblent exister dans le monde réel.

Vous allez utiliser l'API WebXR Device qui combine des fonctionnalités de RA et de réalité virtuelle (RV). Vous vous concentrerez sur les extensions de RA utilisées dans l'API WebXR Device afin de créer une application de RA simple qui s'exécute sur le Web interactif.

En quoi consiste la réalité augmentée ?

La RA désigne généralement la combinaison de graphismes générés par ordinateur avec le monde réel. Dans le cas de la RA basée sur les téléphones, des éléments graphiques réalisés par ordinateur sont placés de manière convaincante par dessus le flux de la caméra. Pour que l'effet reste réaliste à mesure que le téléphone se déplace, l'appareil compatible RA doit comprendre l'environnement qu'il traverse et déterminer sa pose (position et orientation) dans l'espace 3D. Cela peut inclure la détection des surfaces et une estimation de l'éclairage de l'environnement.

Depuis le lancement des solutions Google ARCore et ARKit d'Apple, la RA est désormais largement utilisée dans les applications, que ce soit pour les filtres de selfie ou les jeux en RA.

Objectifs de l'atelier

Dans cet atelier de programmation, vous allez créer une application Web qui place un modèle dans le monde réel à l'aide de la réalité augmentée. Cette appli pourra :

  1. Utiliser les capteurs de l'appareil cible pour déterminer et suivre sa position et son orientation dans le monde
  2. Afficher un modèle 3D composite au-dessus d'une prise de vue en direct
  3. Exécuter des tests de positionnement pour placer des objets sur des surfaces découvertes dans le monde réel

Points abordés

  • Utiliser l'API WebXR Device
  • Configurer une scène de RA de base
  • Détecter une surface à l'aide de tests de positionnement de RA
  • Charger et afficher un modèle 3D synchronisé avec le flux réel de la caméra
  • Effectuer le rendu des ombres en fonction du modèle 3D

Cet atelier de programmation est consacré aux API de RA. Les concepts et les blocs de code non pertinents ne sont pas abordés, et sont fournis dans le code du dépôt correspondant.

Prérequis

Cliquez sur Essayer sur votre appareil de RA pour effectuer la première étape de cet atelier. Si le message "Votre navigateur ne dispose pas des fonctionnalités de RA" s'affiche à l'écran, vérifiez que les Services Google Play pour la RA sont installés sur votre appareil Android.

2. Configurer l'environnement de développement

Télécharger le code

  1. Cliquez sur le lien suivant pour télécharger l'ensemble du code de cet atelier de programmation sur votre poste de travail :

Télécharger le code source

  1. Décompressez le fichier ZIP téléchargé. Cette action décompresse le dossier racine (ar-with-webxr-master), qui contient les répertoires de plusieurs étapes de cet atelier de programmation, ainsi que toutes les ressources dont vous avez besoin.

Les dossiers step-03 et step-04 contiennent l'état final souhaité des troisième et quatrième étapes de cet atelier de programmation, ainsi que le résultat final. Ils sont fournis à titre de référence.

Tout votre travail de codage est effectué dans le répertoire work.

Installer le serveur Web

  1. Vous êtes libre d'utiliser votre propre serveur Web. Si ce n'est déjà fait, cette section explique comment configurer un serveur Web pour Chrome.
    Si l'application n'est pas encore installée sur votre poste de travail, vous pouvez le faire depuis le Chrome Web Store.

  1. Après avoir installé l'application de serveur Web pour Chrome, accédez à chrome://apps, puis cliquez sur l'icône du serveur Web :

Icône du serveur Web

La boîte de dialogue suivante s'affiche. Elle vous permet de configurer votre serveur Web local :

Configurer le serveur Web Chrome

  1. Cliquez sur Choose folder (Choisir un dossier), puis sélectionnez le dossier ar-with-webxr-master. Cela vous permet de diffuser votre travail en cours via l'URL indiquée dans la boîte de dialogue du serveur Web (dans la section Web Server URL(s)).
  2. Sous Options (needs restart) (Options (redémarrage nécessaire)), cochez la case Automatically show index.html (Afficher automatiquement l'index.html).
  3. Définissez le champ Web server sur Stop (Arrêté), puis sur Started (Démarré). Redémarrer le serveur Web Chrome
  4. Vérifiez qu'au moins une URL de serveur Web apparaît : http://127.0.0.1:8887 (URL localhost par défaut).

Configurer le transfert de port

Configurez votre appareil de RA pour qu'il accède au même port sur votre poste de travail lorsque vous accédez à localhost:8887.

  1. Sur votre poste de travail de développement, accédez à chrome://inspect, puis cliquez sur Port forwarding (Transfert de port) : chrome://inspect
  2. Utilisez la boîte de dialogue Port forwarding settings (Paramètres de transfert de port) pour transférer le port 8887 vers localhost:8887.
  3. Cochez la case Enable port forwarding (Activer le transfert de port) :

Configurer le transfert de port

Valider votre configuration

Testez votre connexion :

  1. Connectez votre appareil de RA à votre poste de travail avec un câble USB.
  2. Sur votre appareil de RA dans Chrome, saisissez http://localhost:8887 dans la barre d'adresse. Votre appareil de RA doit transférer cette requête au serveur Web de votre poste de travail de développement. Un répertoire de fichiers doit s'afficher.
  3. Sur votre appareil de RA, cliquez sur step-03 pour charger le fichier step-03/index.html dans votre navigateur.

Une page contenant un bouton Start augmented reality (Démarrer la réalité augmentée) doit s'afficher.

Toutefois, si une page d'erreur Unsupported browser (Navigateur non pris en charge) s'affiche, votre appareil n'est probablement pas compatible.

ARCore est pris en charge

ARCore n'est pas pris en charge

La connexion à votre serveur Web devrait maintenant fonctionner avec votre appareil de RA.

  1. Cliquez sur Start augmented reality (Démarrer la réalité augmentée). Vous serez peut-être invité à installer ARCore.

Invite "Installer ARCore"

La première fois que vous exécutez une application de RA, une invite d'autorisation d'accès à la caméra s'affiche.

Chrome demandant les autorisations d'accès à la caméra → Boîte de dialogue des autorisations

Une fois que tout est prêt, vous devriez voir une scène de cubes superposés sur le flux de la caméra. La compréhension de la scène s'améliore à mesure que la caméra analyse l'environnement. Déplacez-vous pour stabiliser la prise de vue.

3. Configurer WebXR

Dans cette étape, vous allez apprendre à configurer une session WebXR et une scène de RA de base. La page HTML est fournie avec un style CSS et du code JavaScript pour permettre l'activation de la fonctionnalité de RA de base. Cela accélère le processus de configuration et permet à l'atelier de programmation de se concentrer sur les fonctionnalités de RA.

Page HTML

Vous allez créer une expérience de RA dans une page Web classique à l'aide des technologies Web existantes. Dans cette expérience, vous utilisez une toile de rendu en plein écran de façon à alléger le fichier HTML.

Comme les fonctionnalités de RA nécessitent un geste de la part de l'utilisateur, l'application intègre certains composants Material Design qui permettent d'afficher le bouton Démarrer la RA et le message de navigateur non compatible.

Le fichier index.html qui se trouve déjà dans votre répertoire work doit se présenter comme ceci : Il s'agit d'un sous-ensemble du contenu réel. Ne copiez pas ce code dans votre fichier !

<!-- Don't copy this code into your file! -->
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Building an augmented reality application with the WebXR Device API</title>
    <link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
    <script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>

    <!-- three.js -->
    <script src="https://unpkg.com/three@0.123.0/build/three.js"></script>
    <script src="https://unpkg.com/three@0.123.0/examples/js/loaders/GLTFLoader.js"></script>

    <script src="../shared/utils.js"></script>
    <script src="app.js"></script>
  </head>
  <body>
  <!-- Information about AR removed for brevity. -->

  <!-- Starting an immersive WebXR session requires user interaction. Start the WebXR experience with a simple button. -->
  <a onclick="activateXR()" class="mdc-button mdc-button--raised mdc-button--accent">
    Start augmented reality
  </a>

</body>
</html>

Ouvrir le code clé JavaScript

Le point de départ de votre application est app.js. Ce fichier fournit un code récurrent permettant de configurer une expérience de RA.

Votre répertoire de travail inclut déjà le code de l'application (app.js).

Vérifier la compatibilité WebXR et RA

Pour qu'un utilisateur puisse travailler avec la RA, vérifiez l'existence de navigator.xr et des fonctionnalités XR nécessaires. L'objet navigator.xr est le point d'entrée de l'API WebXR Device. Il doit donc exister si l'appareil est compatible. Vérifiez également que le mode de session "immersive-ar" est compatible.

Si tout fonctionne correctement, cliquez sur le bouton Passer en réalité augmentée pour créer une session XR. Sinon, onNoXRDevice() est appelé (dans shared/utils.js), qui affiche un message indiquant une non-compatibilité avec la RA.

Ce code existe déjà dans app.js. Vous n'avez donc aucune modification à effectuer.

(async function() {
  if (navigator.xr && await navigator.xr.isSessionSupported("immersive-ar")) {
    document.getElementById("enter-ar").addEventListener("click", activateXR)
  } else {
    onNoXRDevice();
  }
})();

Demander une XRSession

Lorsque vous cliquez sur Passer en réalité augmentée, le code appelle activateXR(). Cela permet de démarrer l'expérience de RA.

  1. Recherchez la fonction activateXR() dans app.js. Du code a été omis :
activateXR = async () => {
  // Initialize a WebXR session using "immersive-ar".
  this.xrSession = /* TODO */;

  // Omitted for brevity
}

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.

  1. Initialisez this.xrSession à l'aide du mode "immersive-ar" :
activateXR = async () => {
  // Initialize a WebXR session using "immersive-ar".
  this.xrSession = await navigator.xr.requestSession("immersive-ar");

  // ...
}

Initialiser un XRReferenceSpace

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.

Initialisez this.localReferenceSpace dans onSessionStarted() avec le code suivant :

this.localReferenceSpace = await this.xrSession.requestReferenceSpace("local");

Définir une boucle d'animation

  1. Utilisez requestAnimationFrame de XRSession pour démarrer une boucle de rendu, semblable à window.requestAnimationFrame.

Sur chaque image, onXRFrame est appelé avec un horodatage et une valeur XRFrame.

  1. Terminez l'implémentation de onXRFrame. Lorsqu'une image s'affiche, mettez en file d'attente la requête suivante en ajoutant ce code :
// Queue up the next draw request.
this.xrSession.requestAnimationFrame(this.onXRFrame);
  1. Ajoutez du code pour configurer l'environnement graphique. Ajoutez ces lignes en bas de onXRFrame :
// Bind the graphics framebuffer to the baseLayer's framebuffer.
const framebuffer = this.xrSession.renderState.baseLayer.framebuffer;
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
this.renderer.setFramebuffer(framebuffer);
  1. Pour déterminer la posture du spectateur, utilisez XRFrame.getViewerPose(). Ce XRViewerPose décrit la position et l'orientation de l'appareil dans l'espace. Il contient également un tableau de XRView, qui décrit chaque panorama à partir duquel la scène doit être présentée pour s'afficher correctement sur l'appareil actuel. Alors que la RV stéréoscopique génère deux vues (une pour chaque œil), les appareils de RA n'en offrent qu'une seule.
    Les informations du fichier pose.views sont généralement utilisées pour configurer la matrice d'affichage et la matrice de projection de la caméra virtuelle. Cela affecte la disposition de la scène en 3D. La scène peut être affichée une fois la caméra configurée.
  2. Ajoutez ces lignes en bas de onXRFrame :
// Retrieve the pose of the device.
// XRFrame.getViewerPose can return null while the session attempts to establish tracking.
const pose = frame.getViewerPose(this.localReferenceSpace);
if (pose) {
  // In mobile AR, we only have one view.
  const view = pose.views[0];

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

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

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

Tester

Exécutez l'application. Sur votre appareil de développement, accédez à work/index.html. Vous devriez voir le flux de votre caméra avec des cubes flottant dans l'espace dont la perspective change lorsque vous déplacez l'appareil. Le suivi s'améliorant à mesure que vous vous déplacez, testez ce qui fonctionne le mieux pour vous et votre appareil.

Si vous ne parvenez pas à exécuter l'application, consultez les sections Introduction et Configurer votre environnement de développement.

4. Ajouter un réticule de mise au point

Une fois la scène de RA de base configurée, vous pouvez commencer à interagir avec la réalité à l'aide d'un test de positionnement. Dans cette section, vous allez programmer un test de positionnement et l'utiliser pour détecter une surface réelle.

Comprendre un test de positionnement

Un test de positionnement permet généralement de tracer une ligne droite depuis un point dans l'espace dans une certaine direction et de déterminer s'il présente une intersection avec des objets d'intérêt. Dans cet exemple, vous allez orienter l'appareil en direction d'un endroit précis. Imaginez qu'un rayon de soleil est projeté par la caméra dans l'espace environnant.

Les capacités de RA sous-jacentes et de compréhension de la réalité de l'API WebXR Device vous permettent de savoir si des objets réels se trouvent sur la trajectoire de ce rayon.

Explicatif du test de positionnement

Demander une XRSession avec fonctionnalités supplémentaires

Pour effectuer des tests de positionnement, vous devez demander des fonctionnalités supplémentaires lorsque vous envoyez la requête de XRSession.

  1. Dans app.js, localisez navigator.xr.requestSession.
  2. Ajoutez les fonctionnalités "hit-test" et "dom-overlay" en tant que requiredFeature comme suit :
this.xrSession = await navigator.xr.requestSession("immersive-ar", {
  requiredFeatures: ["hit-test", "dom-overlay"]
});
  1. Configurez la superposition DOM. Disposez l'élément document.body sur la vue de la caméra RA comme ceci :
this.xrSession = await navigator.xr.requestSession("immersive-ar", {
  requiredFeatures: ["hit-test", "dom-overlay"],
  domOverlay: { root: document.body }
});

Ajouter une invite de mouvement

ARCore fonctionne mieux lorsqu'une compréhension appropriée de l'environnement a été établie. Pour ce faire, un processus de localisation et de cartographie simultanées (SLAM) utilise des points de caractéristiques visuellement distincts pour calculer un changement dans les caractéristiques de localisation et d'environnement.

Utilisez la fonctionnalité "dom-overlay" de l'étape précédente pour afficher une invite de mouvement en haut du flux de la caméra.

Ajoutez une balise <div> à index.html avec l'ID stabilization. Cette balise <div> présente une animation aux utilisateurs représentant l'état de stabilisation et les invite à se déplacer avec leur appareil pour améliorer le processus de SLAM. Elle s'affiche une fois que l'utilisateur est en RA et disparaît dès la détection d'une surface par le réticule, contrôlée par les classes <body>.

  <div id="stabilization"></div>

</body>
</html>

Ajouter un réticule

Utilisez un réticule pour indiquer l'emplacement visé par l'appareil.

  1. Dans app.js, remplacez l'appel DemoUtils.createCubeScene() dans setupThreeJs() par une valeur Three.Scene() vide.
setupThreeJs() {
  // ...

  // this.scene = DemoUtils.createCubeScene();
  this.scene = DemoUtils.createLitScene();
}
  1. Dans la nouvelle scène, insérez un objet représentant le point de collision. La classe Reticle fournie gère le chargement du modèle de réticule dans shared/utils.js.
  2. Ajoutez le Reticle à la scène dans setupThreeJs() :
setupThreeJs() {
  // ...

  // this.scene = DemoUtils.createCubeScene();
  this.scene = DemoUtils.createLitScene();
  this.reticle = new Reticle();
  this.scene.add(this.reticle);
}

Pour effectuer un test de positionnement, vous devez utiliser un nouveau fichier XRReferenceSpace. Cet espace de référence indique un nouveau système de coordonnées du point de vue du spectateur, afin de créer un rayon aligné sur la direction affichée. Ce système de coordonnées est utilisé dans XRSession.requestHitTestSource(), qui peut effectuer le calcul des tests de positionnement.

  1. Ajoutez le code suivant à onSessionStarted() dans app.js :
async onSessionStarted() {
  // ...

  // Setup an XRReferenceSpace using the "local" coordinate system.
  this.localReferenceSpace = await this.xrSession.requestReferenceSpace("local");

  // Add these lines:
  // Create another XRReferenceSpace that has the viewer as the origin.
  this.viewerSpace = await this.xrSession.requestReferenceSpace("viewer");
  // Perform hit testing using the viewer as origin.
  this.hitTestSource = await this.xrSession.requestHitTestSource({ space: this.viewerSpace });

  // ...
}
  1. À l'aide de ce hitTestSource, effectuez un test de positionnement pour chaque image :
    • Si le test de positionnement ne donne aucun résultat, cela signifie que ARCore n'a pas eu le temps de comprendre l'environnement. Dans ce cas, invitez l'utilisateur à déplacer l'appareil à l'aide de la balise de stabilisation <div>.
    • Si des résultats s'affichent, déplacez le réticule vers cet emplacement.
  2. Modifiez onXRFrame pour déplacer le réticule :
onXRFrame = (time, frame) => {
  // ... some code omitted ...
  this.camera.updateMatrixWorld(true);

  // Add the following:
  const hitTestResults = frame.getHitTestResults(this.hitTestSource);

  if (!this.stabilized && hitTestResults.length > 0) {
    this.stabilized = true;
    document.body.classList.add("stabilized");
  }
  if (hitTestResults.length > 0) {
    const hitPose = hitTestResults[0].getPose(this.localReferenceSpace);

    // update the reticle position
    this.reticle.visible = true;
    this.reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z)
    this.reticle.updateMatrixWorld(true);
  }
  // More code omitted.
}

Ajouter un comportement associé à l'appui sur l'écran

Une XRSession peut émettre des événements en fonction de l'interaction de l'utilisateur via l'événement select, qui représente l'action principale. Dans WebXR sur les appareils mobiles, l'action principale consiste à appuyer sur l'écran.

  1. Ajoutez un écouteur d'événements select en bas de onSessionStarted :
this.xrSession.addEventListener("select", this.onSelect);

Dans cet exemple, le fait d'appuyer sur l'écran entraîne le placement d'un tournesol sur le réticule.

  1. Créez une implémentation pour onSelect dans la classe App :
onSelect = () => {
  if (window.sunflower) {
    const clone = window.sunflower.clone();
    clone.position.copy(this.reticle.position);
    this.scene.add(clone);
  }
}

Tester l'application

Vous avez créé un réticule que vous pouvez diriger avec votre appareil à l'aide de tests de positionnement. En appuyant sur l'écran, vous devriez pouvoir placer un tournesol à l'emplacement désigné par le réticule.

  1. Lorsque vous exécutez votre application, vous devriez voir un réticule se déplacer à la surface du sol. Si ce n'est pas le cas, balayez lentement le sol avec le viseur de votre téléphone.
  2. Une fois le réticule affiché, appuyez dessus. Un tournesol doit apparaître sur le réticule. Vous devrez peut-être vous déplacer légèrement pour faciliter la détection des surfaces réelles par la plate-forme de RA sous-jacente. Une faible luminosité et des surfaces dépourvues de caractéristiques diminuent la qualité de la scène et la possibilité d'effectuer un positionnement. Si vous rencontrez des problèmes, consultez le code step-04/app.js pour voir un exemple concret de cette étape.

5. Ajouter des ombres

La création d'une scène réaliste implique l'utilisation d'éléments comme la luminosité et des ombres appropriées projetées sur les objets numériques qui renforcent le caractère réaliste et immersif.

La luminosité et les ombres sont gérées par three.js. Vous pouvez régler la projection des ombres et leur restitution à l'aide des paramètres de lumière, de matériaux et de mesh. Cette scène de l'application présente une luminosité qui projette une ombre et une surface plane pour le rendu des ombres uniquement.

  1. Activer les ombres sur le moteur de rendu three.js WebGLRenderer. Après avoir créé le moteur de rendu, définissez les valeurs suivantes sur son shadowMap :
setupThreeJs() {
  ...
  this.renderer = new THREE.WebGLRenderer(...);
  ...
  this.renderer.shadowMap.enabled = true;
  this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  ...
}

L'exemple de scène créé dans DemoUtils.createLitScene() contient un objet appelé shadowMesh, une surface plane horizontale qui ne présente que des ombres. La surface comporte initialement 10 000 unités sur la position Y. Une fois le tournesol placé, déplacez le shadowMesh pour qu'il ait la même hauteur que la surface réelle, de sorte que l'ombre de la fleur se présente au-dessus du sol.

  1. Dans onSelect, après avoir ajouté clone à la scène, ajoutez du code pour repositionner la surface ombrée :
onSelect = () => {
  if (window.sunflower) {
    const clone = window.sunflower.clone();
    clone.position.copy(this.reticle.position);
    this.scene.add(clone);

    const shadowMesh = this.scene.children.find(c => c.name === "shadowMesh");
    shadowMesh.position.y = clone.position.y;
  }
}

Tester

Lorsque vous placez un tournesol, vous devriez le voir projeter une ombre. Si vous rencontrez des problèmes, consultez le code final/app.js pour voir un exemple concret de cette étape.

6. Autres ressources

Félicitations ! Vous avez atteint la fin de cet atelier de programmation sur la RA avec WebXR.

En savoir plus