Start · Sprachen · JavaScript · Referenz · XRSubImage

XRSubImage

Schnittstelle

Beschreibt über <code>viewport</code>, in welchen Bildausschnitt (Viewport) einer GPU-Textur für eine WebXR-Ebene (Layer) gerendert werden soll.

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

Signatur

interface XRSubImage

Beschreibung

XRSubImage gehört zum Modul WebXR Layers der WebXR Device API. Ein Objekt dieser Schnittstelle gibt an, welchen Ausschnitt einer Textur die Anwendung für eine bestimmte Ebene (z. B. XRProjectionLayer oder XRQuadLayer) und ein bestimmtes Auge bemalen soll. Die Schnittstelle hat nur eine Eigenschaft: viewport vom Typ XRViewport mit x, y, width und height in Pixeln.

Direkt benutzt man XRSubImage in der Praxis kaum. Man bekommt fast immer die abgeleitete Schnittstelle XRWebGLSubImage. Sie ergänzt unter anderem colorTexture, depthStencilTexture, imageIndex, textureWidth und textureHeight. Solche Objekte liefern die Methoden XRWebGLBinding.getSubImage(layer, frame, eye) und XRWebGLBinding.getViewSubImage(layer, view).

Man braucht XRSubImage immer dann, wenn man mit Ebenen statt mit dem klassischen XRWebGLLayer rendert. In jedem Frame holt man sich für jede Ansicht das passende Sub-Image, bindet dessen Textur an einen Framebuffer und setzt mit gl.viewport() den Ausschnitt aus subImage.viewport. Mehrere Augen können sich eine Textur teilen, etwa bei layout: "stereo-left-right". Deshalb darf man den Viewport nie selbst ausrechnen, sondern muss immer den gelieferten Wert verwenden.

Rückgabewert

Typ
XRViewport
Beschreibung
Die einzige Eigenschaft viewport (schreibgeschützt) liefert ein XRViewport mit x, y, width und height. Diese Werte gibt man direkt an gl.viewport() weiter.

Beispiele

Projektionsebene: Sub-Image pro Auge holen und Viewport setzen

const session = await navigator.xr.requestSession("immersive-vr", {
  requiredFeatures: ["layers"]
});
const canvas = document.createElement("canvas");
const gl = canvas.getContext("webgl2", { xrCompatible: true });

const xrGlBinding = new XRWebGLBinding(session, gl);
const projectionLayer = xrGlBinding.createProjectionLayer({ textureType: "texture" });
session.updateRenderState({ layers: [projectionLayer] });

const refSpace = await session.requestReferenceSpace("local");
const framebuffer = gl.createFramebuffer();

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

  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  for (const view of pose.views) {
    // XRWebGLSubImage erbt von XRSubImage
    const subImage = xrGlBinding.getViewSubImage(projectionLayer, view);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
      gl.TEXTURE_2D, subImage.colorTexture, 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);
    // ... Szene für view.projectionMatrix / view.transform zeichnen
  }
};
session.requestAnimationFrame(onXRFrame);

Quad-Ebene: Sub-Image mit getSubImage() nur bei Bedarf neu zeichnen

const refSpace = await session.requestReferenceSpace("local");
const quadLayer = xrGlBinding.createQuadLayer({
  space: refSpace,
  viewPixelWidth: 512,
  viewPixelHeight: 512,
  layout: "mono"
});
quadLayer.transform = new XRRigidTransform({ x: 0, y: 1.5, z: -2 });
quadLayer.width = 1;
quadLayer.height = 1;
session.updateRenderState({ layers: [projectionLayer, quadLayer] });

const drawQuad = (frame) => {
  if (!quadLayer.needsRedraw) return; // Inhalt ist noch gültig
  const subImage = xrGlBinding.getSubImage(quadLayer, frame);
  gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
    gl.TEXTURE_2D, subImage.colorTexture, 0);
  const vp = subImage.viewport;
  gl.viewport(vp.x, vp.y, vp.width, vp.height);
  gl.clearColor(1, 1, 1, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  // ... UI-Inhalt in die Quad-Textur zeichnen
};

Viewport eines Sub-Images zur Fehlersuche ausgeben

const logSubImage = (subImage, label) => {
  console.log(subImage instanceof XRSubImage); // true, auch für XRWebGLSubImage
  const { x, y, width, height } = subImage.viewport;
  console.log(`${label}: x=${x}, y=${y}, ${width}x${height}`);
};

// Im Frame-Callback:
for (const view of pose.views) {
  logSubImage(xrGlBinding.getViewSubImage(projectionLayer, view), view.eye);
}
true left: x=0, y=0, 1832x1920 true right: x=0, y=0, 1832x1920