Crea una sessione AR immersiva con WebXR

Questa pagina ti guiderà nella creazione di una semplice applicazione AR immersiva utilizzando WebXR.

Per iniziare, devi disporre di un ambiente di sviluppo compatibile con WebXR.

Creare una pagina HTML

WebXR richiede l'interazione dell'utente per poter avviare una sessione. Crea un pulsante che chiami activateXR(). Al caricamento della pagina, l'utente può utilizzare questo pulsante per avviare l'esperienza AR.

Crea un nuovo file denominato index.html e aggiungi il seguente codice HTML:

<!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>

Inizializzare three.js

Non succederà molto quando premi il tasto di avvio. Per configurare un ambiente 3D, puoi utilizzare una libreria di rendering per visualizzare una scena.

In questo esempio utilizzerai three.js, una libreria di rendering 3D JavaScript che fornisce un renderer WebGL. Three.js gestisce il rendering, le videocamere e le scene grafiche, semplificando la visualizzazione di contenuti 3D sul web.

Creare una scena

Un ambiente 3D viene generalmente modellato come una scena. Crea un THREE.Scene contenente elementi AR. Il seguente codice ti consente di guardare una scatola colorata non illuminata in AR.

Aggiungi questo codice alla fine della funzione 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);

Configurare il rendering utilizzando three.js

Per poter visualizzare questa scena in AR, hai bisogno di un renderizzatore e di una videocamera. Il renderer utilizza WebGL per disegnare la scena sullo schermo. La videocamera descrive l'area visibile da cui viene visualizzata la scena.

Aggiungi questo codice alla fine della funzione 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;

Creare un'XRSession

L'entry point per WebXR è tramite XRSystem.requestSession(). Utilizza la modalità immersive-ar per consentire la visualizzazione dei contenuti visualizzati in un ambiente reale.

Un XRReferenceSpace descrive il sistema di coordinate utilizzato per gli oggetti all'interno del mondo virtuale. La modalità 'local' è ideale per un'esperienza AR, con uno spazio di riferimento che ha un'origine vicino allo spettatore e un rilevamento stabile.

Per creare un XRSession e un XRReferenceSpace, aggiungi questo codice alla fine della funzione 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');

Esegui il rendering della scena

Ora puoi eseguire il rendering della scena. XRSession.requestAnimationFrame() pianifica un callback che viene eseguito quando il browser è pronto a disegnare un frame.

Durante il callback del frame dell'animazione, chiama XRFrame.getViewerPose() per ottenere la posa dello spettatore rispetto allo spazio delle coordinate locali. Viene utilizzato per aggiornare la fotocamera in scena, modificando il modo in cui l'utente visualizza il mondo virtuale prima che il renderer disegni la scena utilizzando la fotocamera aggiornata.

Aggiungi questo codice alla fine della funzione 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);

Esegui Hello WebXR

Vai al file WebXR sul tuo dispositivo. Dovresti riuscire a vedere un cubo colorato da tutti i lati.

Aggiungere un test di hit

Un modo comune per interagire con il mondo AR è tramite un test di hit, che individua un'intersezione tra un raggio e la geometria del mondo reale. In Hello WebXR, utilizzerai un test di hit per posizionare un girasole nel mondo virtuale.

Rimuovi il cubo dimostrativo

Rimuovi il cubo non illuminato e sostituiscilo con una scena che includa l'illuminazione:

const scene = new THREE.Scene();

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

Utilizzare la funzionalità hit-test

Per inizializzare la funzionalità di test degli hit, richiedi una sessione con la funzionalità hit-test. Individua il frammento requestSession() precedente e aggiungi hit-test:

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

Aggiungere un caricatore di modelli

Al momento, la scena contiene solo un cubo colorato. Per rendere l'esperienza più interessante, aggiungi un caricatore di modelli, che consente di caricare i modelli GLTF.

Nel tag <head> del documento, aggiungi GLTFLoader di three.js.

<!-- 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>

Carica modelli GLTF

Utilizza il caricatore del modello del passaggio precedente per caricare un mirino di targeting e un girasole dal web.

Aggiungi questo codice sopra 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) => {

Creare un'origine di test degli hit

Per calcolare le intersezioni con oggetti reali, crea un XRHitTestSource utilizzando XRSession.requestHitTestSource(). Il raggio utilizzato per il test di hit ha come origine lo spazio di riferimento viewer, il che significa che il test di hit viene eseguito dal centro della visualizzazione.

Per creare un'origine di test di hit, aggiungi questo codice dopo aver creato lo spazio di riferimento 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 });

Disegno di un mirino per il targeting

Per chiarire dove verrà posizionato il girasole, aggiungi un mirino di targeting alla scena. Questo mirino sembrerà aderire alle superfici reali, indicando dove verrà ancorato il girasole.

XRFrame.getHitTestResults restituisce un array di XRHitTestResult ed espone le intersezioni con la geometria del mondo reale. Utilizza queste intersezioni per posizionare il mirino del targeting su ogni fotogramma.

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);
}

Aggiunta di interazioni al tocco

XRSession riceve eventi select quando l'utente completa un'azione principale. In una sessione AR, corrisponde a un tocco sullo schermo.

Fai apparire un nuovo girasole quando l'utente tocca lo schermo aggiungendo questo codice durante l'inizializzazione:

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);
  }
});

Testare il test di corrispondenza

Usa il tuo dispositivo mobile per accedere alla pagina. Dopo che WebXR ha acquisito una comprensione dell'ambiente, il mirino dovrebbe apparire sulle superfici del mondo reale. Tocca lo schermo per posizionare un girasole, che può essere visualizzato da tutti i lati.

Passaggi successivi