Start · Sprachen · JavaScript · Referenz · XRWebGLDepthInformation.texture

XRWebGLDepthInformation.texture

Eigenschaft

Schreibgeschützte Eigenschaft mit einem <code>WebGLTexture</code>, das die Tiefeninformationen des aktuellen XR-Frames als undurchsichtige Textur für Shader bereitstellt.

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

Signatur

XRWebGLDepthInformation.texture

Beschreibung

Die schreibgeschützte Eigenschaft texture eines XRWebGLDepthInformation-Objekts enthält ein WebGLTexture mit dem Tiefenpuffer, den das Gerät für die aktuelle Ansicht (XRView) ermittelt hat. Das Objekt bekommst du über XRWebGLBinding.getDepthInformation(view). Das geht nur, wenn die Sitzung mit dem Feature "depth-sensing" und der Nutzungspräferenz "gpu-optimized" gestartet wurde.

Die Textur ist undurchsichtig (opaque): Du kannst sie binden und im Shader abtasten, aber nicht mit gl.deleteTexture() löschen, nicht per texImage2D verändern und ihre Daten nicht direkt auf die CPU zurücklesen. Typische Einsatzzwecke sind Verdeckung (virtuelle Objekte verschwinden hinter echten Gegenständen), physikbasierte Effekte und die Visualisierung von Tiefe, alles direkt auf der GPU ohne Umweg über JavaScript-Arrays.

Welches Format die Rohwerte haben, legt session.depthDataFormat fest, z. B. "luminance-alpha" (2 Byte pro Texel in den Kanälen L und A), "float32" oder "unsigned-short". Um daraus Meter zu berechnen, multiplizierst du den Rohwert im Shader mit rawValueToMeters. Die Texturkoordinaten rechnest du mit normDepthBufferFromNormView vom normalisierten Ansichtsraum in den Tiefenpuffer um. Beide Werte erbt das Objekt von XRDepthInformation.

Rückgabewert

Typ
WebGLTexture
Beschreibung
Eine undurchsichtige WebGL-Textur mit den Tiefendaten der Ansicht, width × height Texel groß. Sie gilt nur innerhalb des requestAnimationFrame-Callbacks, in dem sie abgerufen wurde.

Beispiele

Sitzung mit GPU-optimierter Tiefenerkennung starten

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2', { xrCompatible: true });

const session = await navigator.xr.requestSession('immersive-ar', {
  requiredFeatures: ['depth-sensing'],
  depthSensing: {
    usagePreference: ['gpu-optimized'],
    dataFormatPreference: ['luminance-alpha', 'float32']
  }
});

console.log('Nutzung:', session.depthUsage);
console.log('Format:', session.depthDataFormat);

const glBinding = new XRWebGLBinding(session, gl);
session.updateRenderState({ baseLayer: new XRWebGLLayer(session, gl) });
Nutzung: gpu-optimized Format: luminance-alpha

Tiefentextur im Render-Loop an einen Shader binden

// Voraussetzungen: session, gl, glBinding, program und refSpace sind bereits angelegt
const uDepth = gl.getUniformLocation(program, 'uDepthTexture');
const uUvTransform = gl.getUniformLocation(program, 'uUvTransform');
const uRawToMeters = gl.getUniformLocation(program, 'uRawValueToMeters');

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

  for (const view of pose.views) {
    const depthInfo = glBinding.getDepthInformation(view);
    if (!depthInfo) continue; // Noch keine Tiefendaten vorhanden

    gl.useProgram(program);
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, depthInfo.texture);
    gl.uniform1i(uDepth, 0);
    gl.uniformMatrix4fv(uUvTransform, false,
      depthInfo.normDepthBufferFromNormView.matrix);
    gl.uniform1f(uRawToMeters, depthInfo.rawValueToMeters);
    // ... Szene für diese Ansicht zeichnen
  }
};
session.requestAnimationFrame(onXRFrame);

Fragment-Shader: Rohwert (luminance-alpha) in Meter umrechnen

const depthFragmentShader = `#version 300 es
precision highp float;
uniform sampler2D uDepthTexture;
uniform mat4 uUvTransform;
uniform float uRawValueToMeters;
in vec2 vUv; // normalisierte Ansichtskoordinaten 0..1
out vec4 fragColor;

float depthInMeters(vec2 uv) {
  vec2 depthUv = (uUvTransform * vec4(uv, 0.0, 1.0)).xy;
  // luminance-alpha: zwei Bytes (L = niederwertig, A = höherwertig)
  vec2 packed = texture(uDepthTexture, depthUv).ra;
  return dot(packed, vec2(255.0, 256.0 * 255.0)) * uRawValueToMeters;
}

void main() {
  float d = depthInMeters(vUv);
  // Tiefe als Graustufen darstellen (0–5 m)
  fragColor = vec4(vec3(clamp(d / 5.0, 0.0, 1.0)), 1.0);
}`;

const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, depthFragmentShader);
gl.compileShader(shader);
console.log(gl.getShaderParameter(shader, gl.COMPILE_STATUS));
true

// Wichtig · Fallstricke

  • Nur im Frame gültig: Die Textur und das XRWebGLDepthInformation-Objekt gelten nur im aktuellen requestAnimationFrame-Callback. Speicherst du sie für spätere Frames, liest du ungültige Daten oder bekommst WebGL-Fehler.
  • Nur bei "gpu-optimized": Hat der Browser "cpu-optimized" gewählt, wirft XRWebGLBinding.getDepthInformation() einen InvalidStateError. In diesem Fall nutzt du XRFrame.getDepthInformation() und XRCPUDepthInformation.data. Prüfe deshalb session.depthUsage.
  • Format beachten: Wie du im Shader dekodierst, hängt von session.depthDataFormat ab. Bei "float32" genügt texture(...).r * rawValueToMeters. Bei "luminance-alpha" musst du zwei Kanäle kombinieren. Float-Texturen setzen unter Umständen WebGL2 bzw. passende Erweiterungen voraus.
  • Undurchsichtig: gl.deleteTexture(), texImage2D und das Anhängen an einen Framebuffer zum Zurücklesen sind nicht erlaubt.
  • Neuere Spezifikation: Neuere Entwürfe führen textureType ("texture" oder "texture-array") und imageIndex ein. Bei einem Array bindest du die Textur an gl.TEXTURE_2D_ARRAY.
  • Kompatibilität: Die Eigenschaft ist experimentell und setzt einen sicheren Kontext (HTTPS) voraus. Unterstützt wird sie derzeit nur von Chromium-Browsern auf Android-Geräten mit ARCore-Tiefenunterstützung, nicht von Firefox und Safari.