Start · Sprachen · JavaScript · Referenz · XRWebGLBinding.getViewSubImage()

XRWebGLBinding.getViewSubImage()

Methode

Gibt ein <code>XRWebGLSubImage</code> zurück, das die WebGL-Farb- und Tiefentextur samt Viewport enthält, in die eine bestimmte <code>XRView</code> einer Projektionsebene gerendert wird.

seit JavaScript WebXR Layers API (experimentell) – Chromium-basier Kategorie: webapi

Signatur

XRWebGLBinding.prototype.getViewSubImage(layer, view)

Beschreibung

Mit getViewSubImage() fragst du innerhalb eines XR-Animationsframes ab, wohin du eine bestimmte Ansicht (typischerweise das linke oder rechte Auge) rendern sollst. Das zurückgegebene XRWebGLSubImage liefert die vom Compositor bereitgestellte colorTexture, optional eine depthStencilTexture, den zu verwendenden viewport sowie bei Texture-Arrays den imageIndex der Schicht.

Die Methode gehört zur WebXR Layers API und ersetzt bei Projektionsebenen (XRProjectionLayer) das klassische Vorgehen mit XRWebGLLayer und getViewport(). Statt eines einzigen Framebuffers, den der Browser verwaltet, hängst du die gelieferten Texturen selbst an einen eigenen WebGLFramebuffer an. Das erlaubt u. a. Texture-Arrays für Multiview-Rendering und eine bessere Speicher- und Qualitätssteuerung.

Ruf die Methode in jedem Frame neu auf: Der Compositor kann pro Frame eine andere Textur aus seiner Swapchain liefern. Die Objekte sind nur während des aktuellen requestAnimationFrame-Callbacks gültig.

Für Quad-, Zylinder- oder Kubusebenen, die nicht an eine Ansicht gebunden sind, verwendest du stattdessen getSubImage().

Parameter

Name Typ Default Beschreibung
$layer Pflicht XRProjectionLayer Die Projektionsebene, die zuvor mit createProjectionLayer() derselben XRWebGLBinding erzeugt wurde.
$view Pflicht XRView Die Ansicht, für die gerendert werden soll – in der Regel ein Eintrag aus XRViewerPose.views des aktuellen Frames.

Rückgabewert

Typ
XRWebGLSubImage
Beschreibung
Ein Objekt mit colorTexture (WebGLTexture), depthStencilTexture (WebGLTexture | null), motionVectorTexture, imageIndex (Schicht im Texture-Array oder null), colorTextureWidth/colorTextureHeight und viewport (XRViewport).

Beispiele

Session mit Layers starten und Projektionsebene rendern

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

const session = await navigator.xr.requestSession('immersive-vr', {
  requiredFeatures: ['layers']
});

const binding = new XRWebGLBinding(session, gl);
const layer = binding.createProjectionLayer({ textureType: 'texture' });
session.updateRenderState({ layers: [layer] });

const refSpace = await session.requestReferenceSpace('local');
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 sub = binding.getViewSubImage(layer, view);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
      gl.TEXTURE_2D, sub.colorTexture, 0);
    const { x, y, width, height } = sub.viewport;
    gl.viewport(x, y, width, height);
    gl.clearColor(0.1, 0.2, 0.4, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    // drawScene(view.projectionMatrix, view.transform.inverse.matrix);
  }
};
session.requestAnimationFrame(onFrame);

Texture-Array mit imageIndex und Tiefentextur

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

const renderView = (view, fb) => {
  const sub = binding.getViewSubImage(layer, view);
  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  // Jede Ansicht liegt in einer eigenen Schicht des Arrays
  gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
    sub.colorTexture, 0, sub.imageIndex);
  if (sub.depthStencilTexture) {
    gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
      sub.depthStencilTexture, 0, sub.imageIndex);
  }
  const vp = sub.viewport;
  gl.viewport(vp.x, vp.y, vp.width, vp.height);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  console.log(`Auge ${view.eye}: Schicht ${sub.imageIndex}, ${vp.width}x${vp.height}`);
};
Auge left: Schicht 0, 1832x1920 Auge right: Schicht 1, 1832x1920

Fehler abfangen, wenn außerhalb des Frames aufgerufen

let lastView = null;

session.requestAnimationFrame((time, frame) => {
  lastView = frame.getViewerPose(refSpace)?.views[0] ?? null;
});

setTimeout(() => {
  try {
    binding.getViewSubImage(layer, lastView);
  } catch (err) {
    console.warn(err.name); // Frame ist nicht mehr aktiv
  }
}, 1000);
InvalidStateError

// Wichtig · Fallstricke

  • Nur im aktiven Animationsframe: Außerhalb des requestAnimationFrame-Callbacks oder mit einer XRView aus einem alten Frame wird ein InvalidStateError ausgelöst.
  • Die Ebene muss von derselben XRWebGLBinding erzeugt worden sein und zur selben Session gehören wie die Ansicht, sonst ebenfalls InvalidStateError.
  • Das Feature 'layers' muss bei requestSession() angefordert werden; textureType: 'texture-array' setzt einen WebGL2-Kontext voraus.
  • Texturen nicht zwischenspeichern – der Compositor kann pro Frame andere Swapchain-Texturen liefern. Die Texturen gehören dem Browser: nicht löschen und ihre Parameter nicht verändern.
  • Immer den gelieferten viewport verwenden; bei textureType: 'texture' teilen sich mehrere Ansichten ggf. eine Textur (Side-by-Side).
  • Kompatibilität: Experimentell, erfordert einen sicheren Kontext (HTTPS). Unterstützt vor allem in Chromium-basierten XR-Browsern (z. B. Meta Quest Browser); Firefox und Safari bieten die Layers API derzeit nicht an. Vorher per 'XRWebGLBinding' in window prüfen.