Start · Sprachen · JavaScript · Referenz · XRSession

XRSession

Schnittstelle

Steht für eine laufende WebXR-Sitzung zwischen einer Webanwendung und einem XR-Gerät (VR-Headset, AR-fähiges Smartphone) und steuert Render-Schleife, Referenzräume und Eingaben.

seit JavaScript WebXR Device API – Chromium 79+ (Chrome, Edge, Sam Kategorie: webapi

Signatur

interface XRSession extends EventTarget

Beschreibung

XRSession ist das zentrale Objekt der WebXR Device API. Eine Instanz erhältst du nicht über einen Konstruktor, sondern über navigator.xr.requestSession(mode, options). Der Modus ist "inline" (Darstellung im Seitenelement), "immersive-vr" (vollständige VR) oder "immersive-ar" (Überlagerung der realen Umgebung). Die Sitzung läuft, bis du sie mit end() beendest oder der Benutzer bzw. das System sie beendet.

Wichtige Bestandteile:

  • Eigenschaften: renderState (aktuelle Render-Konfiguration, z. B. baseLayer, depthNear, depthFar), inputSources (verbundene Controller und Hände als XRInputSourceArray), visibilityState ("visible", "visible-blurred", "hidden"), environmentBlendMode, interactionMode, frameRate und supportedFrameRates.
  • Methoden: requestAnimationFrame(callback) (Render-Schleife mit XRFrame), cancelAnimationFrame(handle), requestReferenceSpace(type), updateRenderState(state), updateTargetFrameRate(rate), requestHitTestSource(options) (AR) und end().
  • Events: end, inputsourceschange, select/selectstart/selectend, squeeze/squeezestart/squeezeend, visibilitychange und frameratechange.

Der typische Ablauf: Unterstützung mit navigator.xr.isSessionSupported() prüfen, Sitzung per Benutzergeste anfordern, einen WebGL-Kontext mit xrCompatible: true anlegen, per updateRenderState() einen XRWebGLLayer setzen, einen Referenzraum anfordern und dann in session.requestAnimationFrame() für jede Ansicht (XRView) der Betrachterpose rendern.

Parameter

Name Typ Default Beschreibung
$mode Pflicht "inline" | "immersive-vr" | "immersive-ar" Sitzungsmodus, den du an navigator.xr.requestSession() übergibst, um eine XRSession zu erhalten.
$options XRSessionInit {} Optionales Objekt mit requiredFeatures und optionalFeatures (z. B. "local-floor", "hand-tracking", "hit-test", "dom-overlay") sowie ggf. domOverlay: { root }.

Rückgabewert

Typ
Promise<XRSession>
Beschreibung
navigator.xr.requestSession() gibt ein Promise zurück, das mit der neuen XRSession erfüllt wird. Ist der Modus oder eine Pflichtfunktion nicht verfügbar, wird es mit NotSupportedError abgelehnt; ohne Benutzergeste mit SecurityError.

Beispiele

Immersive VR-Sitzung starten und Render-Schleife aufsetzen

const button = document.querySelector('#enter-vr');
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2', { xrCompatible: true });

button.addEventListener('click', async () => {
  if (!navigator.xr || !(await navigator.xr.isSessionSupported('immersive-vr'))) {
    console.log('Immersive VR wird nicht unterstützt');
    return;
  }

  // Muss innerhalb einer Benutzergeste angefordert werden
  const session = await navigator.xr.requestSession('immersive-vr', {
    optionalFeatures: ['local-floor']
  });

  await session.updateRenderState({
    baseLayer: new XRWebGLLayer(session, gl)
  });

  const refSpace = await session.requestReferenceSpace('local-floor')
    .catch(() => session.requestReferenceSpace('local'));

  const onFrame = (time, frame) => {
    const s = frame.session;
    s.requestAnimationFrame(onFrame);

    const pose = frame.getViewerPose(refSpace);
    if (!pose) return;

    const layer = s.renderState.baseLayer;
    gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);
    gl.clearColor(0.1, 0.1, 0.2, 1);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

    for (const view of pose.views) {
      const vp = layer.getViewport(view);
      gl.viewport(vp.x, vp.y, vp.width, vp.height);
      // Szene hier mit view.projectionMatrix und view.transform zeichnen
    }
  };

  session.requestAnimationFrame(onFrame);

  session.addEventListener('end', () => {
    console.log('XR-Sitzung beendet');
  });
});
XR-Sitzung beendet (sobald der Benutzer die Sitzung verlässt)

Controller-Eingaben über select- und inputsourceschange-Events

const setupInput = (session) => {
  session.addEventListener('inputsourceschange', (event) => {
    for (const source of event.added) {
      console.log(`Neu: ${source.handedness} (${source.profiles[0] ?? 'unbekannt'})`);
    }
    for (const source of event.removed) {
      console.log(`Entfernt: ${source.handedness}`);
    }
  });

  session.addEventListener('select', (event) => {
    // event.frame ist nur während des Handlers gültig
    console.log(`Auswahl mit ${event.inputSource.handedness}-Controller`);
  });

  session.addEventListener('squeeze', () => {
    console.log('Greiftaste gedrückt');
  });
};

// Nutzung: setupInput(session) direkt nach requestSession() aufrufen
Neu: left (oculus-touch-v3) Neu: right (oculus-touch-v3) Auswahl mit right-Controller

Sitzung sauber beenden und Sichtbarkeit beachten

let paused = false;

const watchSession = (session) => {
  session.addEventListener('visibilitychange', () => {
    // z. B. Systemmenü des Headsets geöffnet
    paused = session.visibilityState !== 'visible';
    console.log(`Sichtbarkeit: ${session.visibilityState}`);
  });

  document.querySelector('#exit-vr').addEventListener('click', async () => {
    await session.end();
    console.log('Sitzung per Button beendet');
  }, { once: true });
};
Sichtbarkeit: visible-blurred Sichtbarkeit: visible Sitzung per Button beendet

AR-Sitzung mit Hit-Test (Oberflächen erkennen)

const startAR = async (gl) => {
  const session = await navigator.xr.requestSession('immersive-ar', {
    requiredFeatures: ['hit-test']
  });
  await session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });

  const localSpace = await session.requestReferenceSpace('local');
  const viewerSpace = await session.requestReferenceSpace('viewer');
  const hitTestSource = await session.requestHitTestSource({ space: viewerSpace });

  console.log(`Blend-Modus: ${session.environmentBlendMode}`);

  session.requestAnimationFrame(function onFrame(t, frame) {
    frame.session.requestAnimationFrame(onFrame);
    const [hit] = frame.getHitTestResults(hitTestSource);
    if (hit) {
      const pose = hit.getPose(localSpace);
      // Fadenkreuz an pose.transform.position platzieren
    }
  });
};
Blend-Modus: alpha-blend