Start · Sprachen · JavaScript · Referenz · XRWebGLSubImage.colorTexture

XRWebGLSubImage.colorTexture

Eigenschaft

Liefert das schreibgeschützte Farb-<code>WebGLTexture</code>, in das für den aktuellen Frame der Inhalt einer <code>XRCompositionLayer</code> gerendert wird.

seit JavaScript Experimentell: WebXR Layers API (Chromium-basierte Kategorie: webapi

Signatur

XRWebGLSubImage.colorTexture

Beschreibung

Die Eigenschaft colorTexture eines XRWebGLSubImage-Objekts enthält die Farbtextur, in die eine WebXR-Anwendung den Inhalt einer Kompositionsebene (XRProjectionLayer, XRQuadLayer, XRCylinderLayer, XREquirectLayer oder XRCubeLayer) für den aktuellen Frame zeichnet. Der XR-Compositor liest diese Textur danach aus und stellt sie auf dem Headset dar.

Das Objekt bekommt man pro Frame über XRWebGLBinding.getViewSubImage(layer, view) (bei Projektionsebenen) oder XRWebGLBinding.getSubImage(layer, frame, eye) (bei den übrigen Ebenen). Die Textur hängt man mit framebufferTexture2D() an einen eigenen Framebuffer an. Hat die Ebene den Texturtyp "texture-array", verwendet man stattdessen framebufferTextureLayer() zusammen mit imageIndex. Den Zeichenbereich innerhalb der Textur legt viewport fest.

Die Textur ist undurchsichtig (opaque): Sie gehört dem XR-System und ist nur innerhalb des requestAnimationFrame-Callbacks der XRSession gültig, in dem sie abgerufen wurde. Man darf sie weder löschen noch in ihrer Größe oder ihrem Format ändern und sollte sie nicht über mehrere Frames hinweg zwischenspeichern.

Rückgabewert

Typ
WebGLTexture
Beschreibung
Die undurchsichtige Farbtextur der Ebene für den aktuellen Frame. Ihre Maße stehen in colorTextureWidth und colorTextureHeight.

Beispiele

Projektionsebene pro Auge rendern

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

const session = await navigator.xr.requestSession('immersive-vr', {
  requiredFeatures: ['layers']
});
const refSpace = await session.requestReferenceSpace('local');
const binding = new XRWebGLBinding(session, gl);
const layer = binding.createProjectionLayer({ textureType: 'texture' });
session.updateRenderState({ layers: [layer] });

const fb = gl.createFramebuffer();

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

  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (const view of pose.views) {
    const subImage = binding.getViewSubImage(layer, view);
    // Farbtextur als Renderziel anhängen
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
      gl.TEXTURE_2D, subImage.colorTexture, 0);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
      gl.TEXTURE_2D, subImage.depthStencilTexture, 0);

    const { x, y, width, height } = subImage.viewport;
    gl.viewport(x, y, width, height);
    gl.clearColor(0.1, 0.2, 0.3, 1);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // drawScene(view) …
  }
};
session.requestAnimationFrame(onFrame);

Texture-Array-Ebene mit imageIndex

const layer = binding.createProjectionLayer({ textureType: 'texture-array' });
session.updateRenderState({ layers: [layer] });

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

  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (const view of pose.views) {
    const sub = binding.getViewSubImage(layer, view);
    // Bei Arrays: Schicht über imageIndex auswählen
    gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
      sub.colorTexture, 0, sub.imageIndex);
    const { x, y, width, height } = sub.viewport;
    gl.viewport(x, y, width, height);
    // drawScene(view) …
  }
};
session.requestAnimationFrame(onFrame);

Quad-Ebene nur bei Bedarf neu zeichnen

const quad = binding.createQuadLayer({
  space: refSpace,
  viewPixelWidth: 512,
  viewPixelHeight: 512,
  layout: 'mono'
});
session.updateRenderState({ layers: [quad] });

const onFrame = (time, frame) => {
  session.requestAnimationFrame(onFrame);
  if (!quad.needsRedraw) return;

  const sub = binding.getSubImage(quad, frame);
  console.log(sub.colorTextureWidth, sub.colorTextureHeight);
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
    gl.TEXTURE_2D, sub.colorTexture, 0);
  gl.viewport(0, 0, sub.colorTextureWidth, sub.colorTextureHeight);
  gl.clearColor(1, 1, 1, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
};
session.requestAnimationFrame(onFrame);
512 512

// Wichtig · Fallstricke

  • Nur innerhalb des Frames gültig: Die Textur gilt nur im laufenden XRSession.requestAnimationFrame-Callback. Wer sie außerhalb davon verwendet oder über mehrere Frames hinweg zwischenspeichert, erhält WebGL-Fehler oder ein schwarzes Bild. Das XRWebGLSubImage muss man daher in jedem Frame neu abrufen.
  • Undurchsichtige Textur: gl.deleteTexture(), texImage2D() oder texStorage2D() auf colorTexture sind nicht erlaubt und führen zu einem INVALID_OPERATION-Fehler.
  • Richtige Anhänge-Funktion: Bei textureType: "texture-array" braucht man framebufferTextureLayer() (WebGL 2), bei "texture" dagegen framebufferTexture2D().
  • Der WebGL-Kontext muss mit xrCompatible: true erstellt werden (oder mit makeXRCompatible() nachträglich kompatibel gemacht werden), und die Sitzung muss das Feature "layers" anfordern.
  • Kompatibilität: Die Eigenschaft ist experimentell. Sie ist in Chromium-basierten Browsern mit WebXR-Unterstützung (u. a. Meta Quest Browser) verfügbar, nicht aber in Firefox und Safari. Außerdem ist ein sicherer Kontext (HTTPS) nötig. Vor der Nutzung sollte man mit 'XRWebGLBinding' in window prüfen, ob die API vorhanden ist.