Start · Sprachen · JavaScript · Referenz · XRFrame.fillJointRadii()

XRFrame.fillJointRadii()

Methode

Schreibt die Radien mehrerer Handgelenk-Räume (<code>XRJointSpace</code>) in ein <code>Float32Array</code> und gibt <code>true</code> zurück, wenn alle Radien ermittelt werden konnten.

seit JavaScript WebXR Hand Input Module Level 1 – experimentell (C Kategorie: webapi

Signatur

XRFrame.prototype.fillJointRadii(jointSpaces, radii)

Beschreibung

Mit fillJointRadii() fragen Sie in einem Aufruf die Radien vieler Gelenke einer getrackten Hand ab. Jedes Gelenk eines XRHand-Objekts ist ein XRJointSpace. Der Radius gibt in Metern ungefähr an, wie dick die Hand an dieser Stelle ist. So können Sie Kugeln oder Kapseln passend groß darstellen oder eine genaue Kollisionserkennung umsetzen, zum Beispiel für das Antippen virtueller Knöpfe mit der Fingerspitze.

Die Methode ist das Gegenstück zu XRFrame.getJointPose() für viele Gelenke auf einmal. Einzelaufrufe liefern jeweils ein neues XRJointPose-Objekt. fillJointRadii() schreibt die Werte dagegen in ein Array, das Sie selbst vorab anlegen. In der Render-Schleife vermeiden Sie damit Speicherzuweisungen und Garbage Collection pro Frame. Zusammen mit XRFrame.fillPoses(), das die Transformationsmatrizen liefert, bekommen Sie alle Daten, die Sie zum Darstellen einer Hand brauchen.

Der Radius an Position i im Array gehört zum Gelenk an Position i in jointSpaces. Kann für ein Gelenk kein Radius ermittelt werden (etwa weil die Hand gerade verdeckt ist), gibt die Methode false zurück. Die betroffenen Einträge sind dann nicht verlässlich; laut Spezifikation werden sie auf NaN gesetzt.

Hand-Tracking funktioniert nur, wenn Sie beim Start der Sitzung das Feature "hand-tracking" anfordern, z. B. über optionalFeatures in navigator.xr.requestSession().

Parameter

Name Typ Default Beschreibung
$jointSpaces Pflicht Array<XRJointSpace> | Iterable<XRJointSpace> Liste der Gelenk-Räume, deren Radien abgefragt werden, z. B. [...inputSource.hand.values()].
$radii Pflicht Float32Array Ziel-Array, in das die Radien in Metern geschrieben werden. Es muss mindestens so viele Einträge haben wie jointSpaces.

Rückgabewert

Typ
boolean
Beschreibung
true, wenn für alle übergebenen Gelenke ein gültiger Radius ermittelt wurde. Sonst false; die betroffenen Einträge in radii sind dann ungültig (NaN).

Beispiele

Sitzung mit Hand-Tracking starten

// Muss durch eine Nutzeraktion ausgelöst werden, z. B. einen Button-Klick
const startXR = async () => {
  if (!navigator.xr || !(await navigator.xr.isSessionSupported('immersive-vr'))) {
    console.log('WebXR (immersive-vr) wird nicht unterstützt');
    return;
  }
  const session = await navigator.xr.requestSession('immersive-vr', {
    optionalFeatures: ['hand-tracking']
  });
  const refSpace = await session.requestReferenceSpace('local-floor');
  console.log('Sitzung gestartet', session.enabledFeatures);
  return { session, refSpace };
};

document.querySelector('#enter-vr').addEventListener('click', startXR);
Sitzung gestartet ['viewer', 'local', 'local-floor', 'hand-tracking']

Gelenkradien pro Frame ohne Speicherzuweisungen abfragen

// Einmal anlegen: 25 Gelenke pro Hand (WebXR Hand Input)
const radii = new Float32Array(25);

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

  for (const source of session.inputSources) {
    if (!source.hand) continue; // Controller statt Hand

    const joints = [...source.hand.values()];
    const ok = frame.fillJointRadii(joints, radii);

    if (ok) {
      const tipIndex = [...source.hand.keys()].indexOf('index-finger-tip');
      console.log(`${source.handedness}: Zeigefingerspitze r = ${(radii[tipIndex] * 1000).toFixed(1)} mm`);
    } else {
      console.log(`${source.handedness}: Hand nicht vollständig getrackt`);
    }
  }
};

// session.requestAnimationFrame(onXRFrame);
left: Zeigefingerspitze r = 7.8 mm right: Zeigefingerspitze r = 7.9 mm

Kombination mit fillPoses() zum Zeichnen der Hand

const JOINTS = 25;
const radii = new Float32Array(JOINTS);
const matrices = new Float32Array(JOINTS * 16); // 4x4-Matrix je Gelenk

const drawHand = (frame, hand, refSpace, drawSphere) => {
  const joints = [...hand.values()];
  const posesOk = frame.fillPoses(joints, refSpace, matrices);
  const radiiOk = frame.fillJointRadii(joints, radii);
  if (!posesOk || !radiiOk) return false;

  for (let i = 0; i < joints.length; i++) {
    // Translation steht in den Einträgen 12–14 der Spalten-Matrix
    const x = matrices[i * 16 + 12];
    const y = matrices[i * 16 + 13];
    const z = matrices[i * 16 + 14];
    drawSphere(x, y, z, radii[i]);
  }
  return true;
};

// Wichtig · Fallstricke

  • TypeError: Die Methode wirft einen TypeError, wenn radii weniger Einträge hat als jointSpaces.
  • InvalidStateError: Außerhalb des requestAnimationFrame-Callbacks ist ein XRFrame nicht aktiv. Rufen Sie die Methode trotzdem auf, wird ein DOMException vom Typ InvalidStateError geworfen. Frames dürfen also nicht für später gespeichert werden.
  • Prüfen Sie den Rückgabewert immer. Bei false sind einzelne Einträge NaN. Ungeprüft weiterverwendet, erzeugen sie unsichtbare oder fehlerhafte Geometrie.
  • Legen Sie Arrays nicht in jedem Frame neu an, denn gerade das soll die Methode vermeiden. Das gilt auch für das Gelenk-Array ([...hand.values()]); in performancekritischem Code sollte es zwischengespeichert werden.
  • Radien sind Näherungswerte in Metern und können je nach Gerät und Nutzer schwanken.
  • Kompatibilität: Die Methode ist experimentell und nur in einer sicheren Umgebung (HTTPS) verfügbar. Unterstützt wird sie vor allem in Chromium-basierten XR-Browsern wie dem Meta Quest Browser und in Chrome/Edge mit passender Hardware. Firefox und Safari auf dem Desktop unterstützen sie nicht. Fragen Sie das Feature "hand-tracking" an und prüfen Sie vorher, ob es verfügbar ist (inputSource.hand).