Start · Sprachen · JavaScript · Referenz · XRLightEstimate

XRLightEstimate

Schnittstelle

Liefert die geschätzten Lichtwerte (Hauptlichtrichtung, -intensität und sphärische Harmonische) einer <code>XRLightProbe</code> für ein bestimmtes <code>XRFrame</code>.

seit JavaScript Experimentell: Chrome/Edge für Android 90+ (WebXR Kategorie: webapi

Signatur

interface XRLightEstimate

Beschreibung

XRLightEstimate gehört zum Modul WebXR Lighting Estimation der WebXR Device API. In AR-Sitzungen schätzt das Gerät (z. B. über ARCore) aus dem Kamerabild, wie die reale Umgebung beleuchtet ist. Mit diesen Werten lassen sich virtuelle Objekte so beleuchten, dass sie zur echten Umgebung passen: Schatten fallen in die richtige Richtung, Farbstich und Helligkeit stimmen.

Es gibt keinen Konstruktor. Ein Objekt erhältst du pro Frame über XRFrame.getLightEstimate(lightProbe). Die dafür nötige XRLightProbe forderst du einmal mit XRSession.requestLightProbe() an. Die Sitzung muss mit dem Feature "light-estimation" gestartet worden sein.

Die Schnittstelle hat drei schreibgeschützte Eigenschaften:

  • primaryLightDirection (DOMPointReadOnly): Richtung zur stärksten Lichtquelle im Referenzraum der Light Probe. w ist immer 0.
  • primaryLightIntensity (DOMPointReadOnly): RGB-Intensität dieses Hauptlichts in x, y und z. w ist immer 1.
  • sphericalHarmonicsCoefficients (Float32Array): 27 Werte, also 9 sphärische Harmonische 2. Ordnung mit je 3 Werten für R, G und B. Sie beschreiben das diffuse Umgebungslicht aus allen Richtungen.

Typischer Ablauf: Du steuerst ein gerichtetes Licht (Directional Light) über Richtung und Intensität und füllst ein Umgebungslicht (z. B. THREE.LightProbe) mit den Koeffizienten.

Parameter

Name Typ Default Beschreibung
$lightProbe Pflicht XRLightProbe Argument von XRFrame.getLightEstimate(), über das man ein XRLightEstimate erhält. Die Probe kommt aus XRSession.requestLightProbe().

Beispiele

AR-Sitzung mit Lichtschätzung starten und Light Probe anfordern

const startAR = async () => {
  if (!navigator.xr || !(await navigator.xr.isSessionSupported('immersive-ar'))) {
    console.warn('Immersive AR wird nicht unterstützt.');
    return;
  }

  const session = await navigator.xr.requestSession('immersive-ar', {
    requiredFeatures: ['local'],
    optionalFeatures: ['light-estimation']
  });

  if (!session.enabledFeatures?.includes('light-estimation')) {
    console.warn('Lichtschätzung ist nicht verfügbar.');
  }

  // Probe einmalig anfordern (optional mit Reflexionsformat)
  const lightProbe = await session.requestLightProbe({ reflectionFormat: 'srgba8' });
  console.log('Light Probe bereit:', lightProbe);
  return { session, lightProbe };
};

// Muss aus einer Benutzeraktion heraus aufgerufen werden
document.querySelector('#ar-button').addEventListener('click', startAR);
Light Probe bereit: XRLightProbe {probeSpace: XRSpace, onreflectionchange: null}

Lichtwerte pro Frame auslesen

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

  const estimate = frame.getLightEstimate(lightProbe);
  if (!estimate) return; // noch keine Schätzung vorhanden

  const { x: dx, y: dy, z: dz } = estimate.primaryLightDirection;
  const { x: r, y: g, z: b } = estimate.primaryLightIntensity;

  console.log(`Richtung: (${dx.toFixed(2)}, ${dy.toFixed(2)}, ${dz.toFixed(2)})`);
  console.log(`Intensität RGB: ${r.toFixed(2)}, ${g.toFixed(2)}, ${b.toFixed(2)}`);
  console.log('SH-Koeffizienten:', estimate.sphericalHarmonicsCoefficients.length);
};

session.requestAnimationFrame(onXRFrame);
Richtung: (0.31, 0.89, -0.33) Intensität RGB: 1.42, 1.35, 1.20 SH-Koeffizienten: 27

Schätzung auf three.js-Lichter übertragen

import * as THREE from 'three';

const directionalLight = new THREE.DirectionalLight();
const ambientProbe = new THREE.LightProbe();
scene.add(directionalLight, ambientProbe);

const applyLightEstimate = (estimate) => {
  const dir = estimate.primaryLightDirection;
  const int = estimate.primaryLightIntensity;

  // Das Licht sitzt in der Richtung, aus der es kommt
  directionalLight.position.set(dir.x, dir.y, dir.z).multiplyScalar(10);

  // Stärkster Kanal = Intensität, die normierten Werte = Farbe
  const max = Math.max(int.x, int.y, int.z, 0.0001);
  directionalLight.intensity = max;
  directionalLight.color.setRGB(int.x / max, int.y / max, int.z / max);

  // 9 × RGB = 27 Werte direkt übernehmen
  ambientProbe.sh.fromArray(estimate.sphericalHarmonicsCoefficients);
};

renderer.setAnimationLoop((time, frame) => {
  const estimate = frame?.getLightEstimate(lightProbe);
  if (estimate) applyLightEstimate(estimate);
  renderer.render(scene, camera);
});