Start · Sprachen · JavaScript · Referenz · XRWebGLLayer.getViewport()

XRWebGLLayer.getViewport()

Methode

Gibt den <code>XRViewport</code> zurück, also den Bereich im Framebuffer der WebGL-Schicht, in den ein bestimmter <code>XRView</code> (z. B. ein Auge) gerendert werden soll.

seit JavaScript WebXR Device API – Chrome/Edge 79+, Opera, Samsung Kategorie: webapi

Signatur

XRWebGLLayer.prototype.getViewport(view)

Beschreibung

Beim Rendern einer WebXR-Szene zeichnet man meist mehrere Ansichten in denselben Framebuffer: bei einem VR-Headset eine für das linke und eine für das rechte Auge. getViewport() sagt dir, welcher rechteckige Bereich (x, y, width, height in Pixeln) des Framebuffers zu einem bestimmten XRView gehört. Diese Werte übergibst du direkt an gl.viewport(), bevor du die Szene für diese Ansicht zeichnest.

Die Methode ruft man in der Render-Schleife auf, also im Callback von XRSession.requestAnimationFrame(). Dort holt man die Pose mit XRFrame.getViewerPose(), geht über pose.views, fragt für jede Ansicht den Viewport ab und rendert mit der Projektions- und View-Matrix der Ansicht.

Die Aufteilung legt der User Agent fest. Sie kann sich zwischen Frames ändern, etwa wenn die Anwendung mit XRView.requestViewportScale() eine dynamische Auflösungsskalierung anfordert. Deshalb solltest du den Viewport in jedem Frame neu abfragen und nicht zwischenspeichern.

Parameter

Name Typ Default Beschreibung
$view Pflicht XRView Die Ansicht (z. B. ein Auge), für die du den Viewport brauchst. Sie muss aus der XRViewerPose des aktuellen, aktiven XRFrame derselben Session stammen.

Rückgabewert

Typ
XRViewport | null
Beschreibung
Ein XRViewport-Objekt mit den Eigenschaften x, y, width und height in Pixeln, bezogen auf den Framebuffer der Schicht. In bestimmten Fällen, etwa bei einer ungültigen Ansicht in manchen Implementierungen, kann null zurückkommen.

Beispiele

Stereo-Rendering in der XR-Render-Schleife

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

const session = await navigator.xr.requestSession('immersive-vr');
const glLayer = new XRWebGLLayer(session, gl);
session.updateRenderState({ baseLayer: glLayer });
const refSpace = await session.requestReferenceSpace('local');

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

  const layer = frame.session.renderState.baseLayer;
  gl.bindFramebuffer(gl.FRAMEBUFFER, layer.framebuffer);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

  for (const view of pose.views) {
    const vp = layer.getViewport(view);
    gl.viewport(vp.x, vp.y, vp.width, vp.height);
    // Eigene Funktion: zeichnet die Szene mit den Matrizen dieser Ansicht
    drawScene(view.projectionMatrix, view.transform.inverse.matrix);
  }
};

session.requestAnimationFrame(onXRFrame);

Viewport-Aufteilung zur Fehlersuche ausgeben

const logViewports = (frame, refSpace) => {
  const pose = frame.getViewerPose(refSpace);
  if (!pose) return;
  const layer = frame.session.renderState.baseLayer;
  pose.views.forEach((view) => {
    const { x, y, width, height } = layer.getViewport(view);
    console.log(`${view.eye}: x=${x}, y=${y}, ${width}x${height}`);
  });
};
left: x=0, y=0, 1832x1920 right: x=1832, y=0, 1832x1920

Dynamische Auflösung mit requestViewportScale

const renderView = (layer, view, gpuUnderLoad) => {
  // Bei hoher GPU-Last mit geringerer Auflösung rendern (falls unterstützt)
  if (typeof view.requestViewportScale === 'function') {
    view.requestViewportScale(gpuUnderLoad ? 0.7 : 1.0);
  }
  // Erst NACH requestViewportScale abfragen, damit die Skalierung greift
  const vp = layer.getViewport(view);
  gl.viewport(vp.x, vp.y, vp.width, vp.height);
  return vp;
};

// Wichtig · Fallstricke

  • Nur im aktiven Frame gültig: Der XRView muss aus dem gerade laufenden requestAnimationFrame-Callback stammen. Außerhalb davon oder mit einer Ansicht aus einer anderen Session wirft die Methode einen InvalidStateError.
  • Nicht zwischenspeichern: Die Viewports können sich von Frame zu Frame ändern, z. B. durch requestViewportScale(). Frage sie deshalb in jedem Frame neu ab.
  • Vergiss nicht, vor dem Zeichnen layer.framebuffer zu binden. Der Viewport bezieht sich auf diesen Framebuffer und nicht auf das Canvas. Bei Inline-Sessions kann framebuffer auch null sein, dann wird der Standard-Framebuffer verwendet.
  • Der WebGL-Kontext muss XR-kompatibel sein: Entweder mit xrCompatible: true erzeugen oder vorher gl.makeXRCompatible() aufrufen.
  • Kompatibilität: WebXR steht nur in sicheren Kontexten (HTTPS) zur Verfügung. Chromium-Browser und die Browser von VR-Headsets unterstützen es, Firefox und Safari auf dem Desktop dagegen nicht. Prüfe vorher mit navigator.xr?.isSessionSupported(), ob WebXR verfügbar ist.
  • Für mehrere Schichten aus der WebXR Layers API (XRProjectionLayer) nutzt man stattdessen XRWebGLBinding.getViewSubImage(), das ebenfalls einen Viewport liefert.