Start · Sprachen · JavaScript · Referenz · XRViewerPose

XRViewerPose

Schnittstelle

Das Interface <code>XRViewerPose</code> beschreibt Position und Orientierung des Betrachters in einem Referenzraum und enthält pro Auge bzw. Anzeige eine <code>XRView</code>.

seit JavaScript WebXR Device API – Chromium-Browser (Chrome/Edge 7 Kategorie: webapi

Signatur

interface XRViewerPose extends XRPose

Beschreibung

XRViewerPose gehört zur WebXR Device API und erweitert XRPose. Es beschreibt, wo sich der Betrachter (typischerweise der Kopf bzw. das Headset) in einem bestimmten XRReferenceSpace befindet und wohin er blickt. Direkt erzeugen kann man ein solches Objekt nicht. Man erhält es in jedem Animations-Frame über XRFrame.getViewerPose(referenceSpace).

Wichtiger als die geerbte Eigenschaft transform ist das schreibgeschützte Array views. Es enthält für jede Ansicht, die gerendert werden muss, ein XRView-Objekt: bei einem VR-Headset meist eines pro Auge, bei einer AR-Sitzung auf dem Smartphone nur eines. Jede XRView liefert eine eigene projectionMatrix und eine eigene transform, aus der man mit transform.inverse.matrix die View-Matrix gewinnt. Über XRWebGLLayer.getViewport(view) bekommt man den Bildbereich (Viewport) im Framebuffer, in den diese Ansicht gezeichnet werden soll.

Von XRPose geerbt werden:

  • transform (XRRigidTransform): Position und Orientierung des Betrachters als Ganzes, etwa der Punkt zwischen den Augen.
  • emulatedPosition (boolean): true, wenn die Position nur geschätzt wird, zum Beispiel bei Geräten, die nur die Drehung erfassen (3DoF), oder wenn das Tracking gerade verloren gegangen ist.
  • linearVelocity / angularVelocity (DOMPointReadOnly | null): optionale Geschwindigkeiten, sofern der Browser sie liefert.

Typische Aufgabe: In der Render-Schleife holt man die Pose, prüft, ob sie null ist, und zeichnet die Szene einmal pro Eintrag in views.

Rückgabewert

Typ
XRViewerPose
Beschreibung
Erhältlich über XRFrame.getViewerPose(referenceSpace). Die Methode gibt null zurück, wenn die Pose gerade nicht bestimmbar ist. Wichtige Eigenschaften: views (ReadonlyArray<XRView>), transform (XRRigidTransform), emulatedPosition (boolean).

Beispiele

Betrachter-Pose im Animations-Frame abrufen

// Voraussetzung: HTTPS und ein WebXR-fähiger Browser
const startXR = async () => {
  if (!navigator.xr || !(await navigator.xr.isSessionSupported('immersive-vr'))) {
    console.log('immersive-vr wird nicht unterstützt');
    return;
  }
  const session = await navigator.xr.requestSession('immersive-vr');
  const refSpace = await session.requestReferenceSpace('local');

  const onFrame = (time, frame) => {
    session.requestAnimationFrame(onFrame);
    const pose = frame.getViewerPose(refSpace); // XRViewerPose | null
    if (!pose) return; // Tracking gerade nicht verfügbar

    const { x, y, z } = pose.transform.position;
    console.log(`Kopf bei ${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)} – Ansichten: ${pose.views.length}`);
  };
  session.requestAnimationFrame(onFrame);
};

// requestSession braucht eine Nutzeraktion
document.querySelector('#enter-vr').addEventListener('click', startXR);
Kopf bei 0.00, 0.00, 0.00 – Ansichten: 2

Stereo-Rendering: ein Durchlauf pro Ansicht

// gl: WebGL2-Kontext, erstellt mit { xrCompatible: true }
const setupRendering = async (session, gl, drawScene) => {
  session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
  const refSpace = await session.requestReferenceSpace('local-floor');

  const onFrame = (time, frame) => {
    session.requestAnimationFrame(onFrame);
    const pose = frame.getViewerPose(refSpace);
    if (!pose) return;

    const layer = session.renderState.baseLayer;
    gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);
    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);
      // Projektions- und View-Matrix je Auge
      drawScene(view.projectionMatrix, view.transform.inverse.matrix, view.eye);
    }
  };
  session.requestAnimationFrame(onFrame);
};

Emulierte Position erkennen (3DoF-Geräte)

const describePose = (pose) => {
  if (pose.emulatedPosition) {
    return 'Nur Drehung erfasst – Position ist geschätzt';
  }
  const o = pose.transform.orientation;
  return `6DoF-Tracking, Quaternion: ${[o.x, o.y, o.z, o.w].map(n => n.toFixed(2)).join(', ')}`;
};

// im Frame-Callback:
// const pose = frame.getViewerPose(refSpace);
// if (pose) console.log(describePose(pose));
6DoF-Tracking, Quaternion: 0.00, 0.00, 0.00, 1.00

// Wichtig · Fallstricke

  • Null-Prüfung ist Pflicht: XRFrame.getViewerPose() gibt null zurück, wenn das Tracking verloren gegangen ist oder die Sitzung pausiert. Ohne Prüfung bricht die Render-Schleife mit einem Fehler ab.
  • Nur innerhalb des Frame-Callbacks gültig: XRFrame und die daraus gewonnenen Posen sind nur während des jeweiligen requestAnimationFrame-Callbacks aktiv. Aufrufe außerhalb davon werfen einen InvalidStateError.
  • Nicht fest von zwei Ansichten ausgehen: Die Zahl der views hängt vom Gerät ab (AR auf dem Smartphone: 1, VR-Headset: 2, manche Geräte liefern mehr). Deshalb immer über das Array iterieren.
  • View-Matrix ≠ transform.matrix: Für die Kamera-Matrix braucht man view.transform.inverse.matrix, nicht die Pose-Matrix selbst.
  • Kompatibilität: Unterstützt in Chromium-Browsern (Chrome, Edge, Samsung Internet) und im Meta Quest Browser. In Firefox und Safari auf dem Desktop fehlt die Unterstützung; Safari unter visionOS unterstützt WebXR. Die API steht nur in sicheren Kontexten (HTTPS) zur Verfügung und kann per Permissions-Policy (xr-spatial-tracking) gesperrt sein. Für die Entwicklung ohne Headset eignet sich der WebXR API Emulator als Browser-Erweiterung.
  • Datenschutz: Posen-Daten sind sensible Bewegungsdaten. Browser verlangen eine Nutzeraktion und gegebenenfalls eine Zustimmung, bevor eine immersive Sitzung starten darf.