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

XRFrame.getHitTestResults()

Methode

Gibt für den aktuellen XR-Frame ein Array von <code>XRHitTestResult</code>-Objekten zurück, also die Schnittpunkte des Strahls einer <code>XRHitTestSource</code> mit realen Oberflächen.

seit JavaScript WebXR Hit Test Module – Chromium-Browser (Chrome/E Kategorie: webapi

Signatur

XRFrame.prototype.getHitTestResults(hitTestSource)

Beschreibung

Mit getHitTestResults() fragt man in einer AR-Sitzung (immersive-ar) ab, wo ein Strahl auf die reale Umgebung trifft, z. B. auf den Boden, einen Tisch oder eine Wand. Den Strahl legt vorher eine XRHitTestSource fest, die man mit XRSession.requestHitTestSource() erzeugt, meist ausgehend vom viewer-Referenzraum (Bildschirmmitte) oder von einem Eingabestrahl.

Die Methode ruft man bei jedem Frame im Callback von XRSession.requestAnimationFrame() auf. Sie liefert die Treffer nach Entfernung sortiert, der nächste steht also an Index 0. Aus einem XRHitTestResult holt man mit getPose(referenceSpace) die Position und Ausrichtung des Treffers. Damit platziert man zum Beispiel ein Fadenkreuz (Reticle) oder ein virtuelles Objekt.

Typischer Ablauf: Sitzung mit requiredFeatures: ['hit-test'] anfordern → Hit-Test-Quelle anlegen → in jedem Frame frame.getHitTestResults(source) abfragen → die Pose des ersten Treffers verwenden.

Parameter

Name Typ Default Beschreibung
$hitTestSource Pflicht XRHitTestSource Die Hit-Test-Quelle, die mit XRSession.requestHitTestSource() erzeugt wurde und den Strahl (Ursprung, Richtung, Entitätstypen) festlegt.

Rückgabewert

Typ
Array<XRHitTestResult>
Beschreibung
Ein Array mit Treffern, sortiert nach Entfernung zum Strahlursprung (nächster zuerst). Ohne Treffer ist das Array leer.

Beispiele

AR-Sitzung mit Hit-Test starten und Quelle anlegen

const startAR = async () => {
  if (!(await navigator.xr?.isSessionSupported('immersive-ar'))) {
    console.log('AR wird nicht unterstützt');
    return;
  }
  const session = await navigator.xr.requestSession('immersive-ar', {
    requiredFeatures: ['hit-test', 'local']
  });

  const viewerSpace = await session.requestReferenceSpace('viewer');
  const localSpace = await session.requestReferenceSpace('local');

  // Strahl aus der Bildschirmmitte nach vorne
  const hitTestSource = await session.requestHitTestSource({ space: viewerSpace });

  const onFrame = (time, frame) => {
    session.requestAnimationFrame(onFrame);
    const results = frame.getHitTestResults(hitTestSource);
    if (results.length > 0) {
      const pose = results[0].getPose(localSpace);
      const { x, y, z } = pose.transform.position;
      console.log(`Treffer bei ${x.toFixed(2)}, ${y.toFixed(2)}, ${z.toFixed(2)}`);
    }
  };
  session.requestAnimationFrame(onFrame);
};

// Muss durch eine Nutzeraktion ausgelöst werden
document.querySelector('#ar-button').addEventListener('click', startAR);
Treffer bei 0.03, -1.21, -0.87

Fadenkreuz (Reticle) auf erkannter Fläche platzieren

// reticle: z. B. ein three.js-Mesh mit matrixAutoUpdate = false
const updateReticle = (frame, hitTestSource, refSpace, reticle) => {
  const [nearest] = frame.getHitTestResults(hitTestSource);
  if (!nearest) {
    reticle.visible = false;
    return;
  }
  const pose = nearest.getPose(refSpace);
  reticle.visible = true;
  // 4x4-Matrix direkt übernehmen (Position + Ausrichtung der Fläche)
  reticle.matrix.fromArray(pose.transform.matrix);
};

Objekt beim Tippen platzieren und Quelle aufräumen

let lastHit = null;

const onFrame = (time, frame) => {
  frame.session.requestAnimationFrame(onFrame);
  const results = frame.getHitTestResults(hitTestSource);
  lastHit = results.length ? results[0].getPose(localSpace) : null;
};

session.addEventListener('select', () => {
  if (lastHit) {
    placeModelAt(lastHit.transform); // eigene Funktion
  }
});

session.addEventListener('end', () => {
  hitTestSource.cancel(); // Ressourcen freigeben
});

// Wichtig · Fallstricke

  • Nur mit aktivem Frame: Die Methode funktioniert nur innerhalb des requestAnimationFrame-Callbacks der Sitzung. Speichert man den XRFrame und ruft sie später auf, wird ein InvalidStateError geworfen.
  • Feature anfordern: Ohne 'hit-test' in requiredFeatures oder optionalFeatures schlägt requestHitTestSource() fehl, weil das Feature nicht aktiviert ist.
  • Die Quelle muss zur Sitzung passen: Stammt die XRHitTestSource aus einer anderen Sitzung oder wurde sie mit cancel() beendet, führt der Aufruf zu einem Fehler.
  • Direkt nach dem Start ist das Array oft einige Frames lang leer, solange das Gerät die Umgebung noch erfasst. Den Fall results.length === 0 immer abfangen.
  • Für transiente Eingaben wie Bildschirm-Taps gibt es die eigene Methode getHitTestResultsForTransientInput().
  • Kompatibilität: Die Methode ist experimentell und vor allem in Chromium auf Android (ARCore) und im Meta Quest Browser verfügbar. Firefox und Safari unterstützen sie nicht. Sie braucht einen sicheren Kontext (HTTPS) und in iframes die Permissions Policy xr-spatial-tracking.