Start · Sprachen · JavaScript · Referenz · XRJointPose

XRJointPose

Schnittstelle

Ein <code>XRPose</code> für ein Handgelenk (Knochen/Fingerglied) beim WebXR-Hand-Tracking, ergänzt um den Radius des Gelenks in Metern.

seit JavaScript WebXR Hand Input Module Level 1 – Chromium-basiert Kategorie: webapi

Signatur

interface XRJointPose extends XRPose

Beschreibung

XRJointPose gehört zum WebXR Hand Input Module und beschreibt Position und Ausrichtung eines einzelnen Skelettgelenks einer getrackten Hand. Die Schnittstelle erbt von XRPose und bietet damit transform (ein XRRigidTransform), emulatedPosition und ggf. linearVelocity/angularVelocity. Zusätzlich liefert sie die Eigenschaft radius.

Man erzeugt ein XRJointPose nie selbst, man erhält es aus XRFrame.getJointPose(jointSpace, baseSpace). jointSpace ist ein XRJointSpace aus XRInputSource.hand (einer XRHand-Map, z. B. hand.get("index-finger-tip")). Für viele Gelenke auf einmal gibt es die effizientere Variante XRFrame.fillPoses() zusammen mit XRFrame.fillJointRadii().

Typische Anwendungen sind die Darstellung virtueller Hände, Gestenerkennung (z. B. Pinch zwischen Daumen- und Zeigefingerspitze), Kollisionserkennung mit UI-Elementen und physikbasierte Interaktion. Mit dem radius lassen sich Kugeln oder Kapseln passend zur Fingerdicke rendern oder Kollisionsvolumen bestimmen.

  • XRJointPose.radius (nur lesbar, number): Radius des Gelenks in Metern, gemessen um die Achse des zugehörigen Knochens.
  • Geerbt: transform, emulatedPosition, linearVelocity, angularVelocity.

Rückgabewert

Typ
XRJointPose | null
Beschreibung
Wird von XRFrame.getJointPose() geliefert; null, wenn das Gelenk im aktuellen Frame nicht relativ zum Basis-Raum bestimmt werden kann (z. B. Hand nicht sichtbar).

Beispiele

Session mit Hand-Tracking anfordern und Pose der Zeigefingerspitze lesen

const session = await navigator.xr.requestSession('immersive-vr', {
  optionalFeatures: ['hand-tracking']
});
const refSpace = await session.requestReferenceSpace('local-floor');

const onFrame = (time, frame) => {
  for (const source of session.inputSources) {
    if (!source.hand) continue; // nur getrackte Hände
    const tipSpace = source.hand.get('index-finger-tip');
    const pose = frame.getJointPose(tipSpace, refSpace); // XRJointPose | null
    if (pose) {
      const { x, y, z } = pose.transform.position;
      console.log(`${source.handedness}: (${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)}), Radius ${(pose.radius * 1000).toFixed(1)} mm`);
    }
  }
  session.requestAnimationFrame(onFrame);
};
session.requestAnimationFrame(onFrame);
right: (0.12, 1.05, -0.31), Radius 8.5 mm

Pinch-Geste unter Berücksichtigung der Gelenkradien erkennen

const isPinching = (frame, hand, refSpace) => {
  const thumb = frame.getJointPose(hand.get('thumb-tip'), refSpace);
  const index = frame.getJointPose(hand.get('index-finger-tip'), refSpace);
  if (!thumb || !index) return false;

  const a = thumb.transform.position;
  const b = index.transform.position;
  const dist = Math.hypot(a.x - b.x, a.y - b.y, a.z - b.z);
  // Oberflächen berühren sich, wenn Abstand ≤ Summe der Radien (+ Toleranz)
  return dist <= thumb.radius + index.radius + 0.005;
};

// Im Frame-Callback:
// if (source.hand && isPinching(frame, source.hand, refSpace)) console.log('Pinch!');
Pinch!

Alle 25 Gelenke effizient mit fillPoses() und fillJointRadii() lesen

const onFrame = (time, frame) => {
  for (const source of frame.session.inputSources) {
    if (!source.hand) continue;
    const joints = [...source.hand.values()];
    const matrices = new Float32Array(joints.length * 16);
    const radii = new Float32Array(joints.length);

    const posesOk = frame.fillPoses(joints, refSpace, matrices);
    const radiiOk = frame.fillJointRadii(joints, radii);
    if (posesOk && radiiOk) {
      // matrices[i*16 .. i*16+15] = Transformationsmatrix von Gelenk i
      console.log(`${joints.length} Gelenke, Handgelenk-Radius: ${radii[0].toFixed(3)} m`);
    }
  }
  frame.session.requestAnimationFrame(onFrame);
};
25 Gelenke, Handgelenk-Radius: 0.021 m

// Wichtig · Fallstricke

  • Feature anfordern: Ohne 'hand-tracking' in requiredFeatures/optionalFeatures ist XRInputSource.hand immer null.
  • Null-Prüfung: getJointPose() kann in jedem Frame null liefern, etwa wenn die Hand verdeckt ist oder das Tracking abreißt. Immer prüfen.
  • Einheiten: radius und Positionen werden in Metern angegeben. Die Pose ist nur innerhalb des jeweiligen XRFrame gültig und sollte nicht über Frames hinweg zwischengespeichert werden.
  • Leistung: Für 25 Gelenke pro Hand ist fillPoses()/fillJointRadii() deutlich schneller als 50 einzelne getJointPose()-Aufrufe pro Frame.
  • Kompatibilität: Nur in sicheren Kontexten (HTTPS) und in WebXR-fähigen Browsern mit Hand-Tracking verfügbar, vor allem Meta Quest Browser und Chromium-basierte Browser auf passenden Geräten. Firefox und Safari (Desktop) unterstützen die Schnittstelle nicht; Apple Vision Pro unterstützt Hand-Tracking in Safari. Vorher mit Feature-Erkennung prüfen ('XRHand' in window).
  • Datenschutz: Handdaten können biometrische Rückschlüsse zulassen. Browser fragen daher ggf. nach einer Berechtigung und können die Daten ungenauer machen.