Start · Sprachen · JavaScript · Referenz · XRViewport

XRViewport

Schnittstelle

Beschreibt Position und Größe des Rechteckbereichs innerhalb einer <code>XRWebGLLayer</code>, in den eine einzelne <code>XRView</code> (z. B. ein Auge) gerendert wird.

seit JavaScript WebXR Device API – Chromium-Browser (Chrome/Edge 7 Kategorie: webapi

Signatur

interface XRViewport

Beschreibung

Das Interface XRViewport gehört zur WebXR Device API. Es gibt an, welchen rechteckigen Ausschnitt des Framebuffers einer XRWebGLLayer eine bestimmte Ansicht (XRView) belegt. Bei stereoskopischen Headsets hat jedes Auge seine eigene Ansicht und damit seinen eigenen Viewport. Meist liegen die beiden nebeneinander im selben Framebuffer, z. B. linke und rechte Hälfte.

Ein XRViewport legst du nie selbst an. Du bekommst es in jedem Animations-Frame über XRWebGLLayer.getViewport(view). Die vier schreibgeschützten Eigenschaften passen direkt zu den Argumenten von WebGLRenderingContext.viewport():

  • x – Abstand der linken Kante vom linken Rand des Framebuffers in Pixeln.
  • y – Abstand der unteren Kante vom unteren Rand des Framebuffers in Pixeln. Wie bei WebGL üblich liegt der Ursprung unten links.
  • width – Breite des Viewports in Pixeln.
  • height – Höhe des Viewports in Pixeln.

Du brauchst das Interface in jeder WebXR-Render-Schleife mit WebGL: Für jede Ansicht der Pose holst du den Viewport, setzt ihn mit gl.viewport() (bei Bedarf auch mit gl.scissor()) und zeichnest dann die Szene mit Projektions- und View-Matrix dieser Ansicht.

Parameter

Name Typ Default Beschreibung
$x Pflicht number Schreibgeschützt. Horizontaler Versatz der linken Kante in Pixeln, gemessen vom linken Rand des Framebuffers.
$y Pflicht number Schreibgeschützt. Vertikaler Versatz der unteren Kante in Pixeln, gemessen vom unteren Rand des Framebuffers.
$width Pflicht number Schreibgeschützt. Breite des Viewports in Pixeln.
$height Pflicht number Schreibgeschützt. Höhe des Viewports in Pixeln.

Rückgabewert

Typ
XRViewport
Beschreibung
Du erhältst Instanzen über XRWebGLLayer.getViewport(view). Der Wert ist null, wenn die Ansicht nicht zur Sitzung der Layer gehört.

Beispiele

Viewport pro Auge in der Render-Schleife setzen

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

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

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

    const layer = 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);
      // drawScene(view.projectionMatrix, view.transform.inverse.matrix);
    }
  };
  session.requestAnimationFrame(onFrame);
};

document.querySelector('#enter-vr').addEventListener('click', startXR);

Viewport-Werte eines Frames protokollieren

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

Scissor-Test auf den Viewport begrenzen

const applyViewport = (gl, vp) => {
  gl.viewport(vp.x, vp.y, vp.width, vp.height);
  gl.enable(gl.SCISSOR_TEST);
  gl.scissor(vp.x, vp.y, vp.width, vp.height);
  // Löscht nur den Bereich dieses Auges
  gl.clearColor(0.1, 0.1, 0.1, 1);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
};

// Wichtig · Fallstricke

  • Nicht zwischenspeichern: Hol den Viewport in jedem Frame neu. Größe und Position können sich ändern, etwa durch dynamische Auflösungsskalierung (XRView.requestViewportScale()) oder eine neue Layer.
  • Koordinatenursprung: y wird wie in WebGL vom unteren Rand gemessen, nicht vom oberen wie im DOM.
  • Rufst du gl.clear() ohne Scissor-Test auf, löscht das den gesamten Framebuffer. Mach das deshalb einmal vor der Schleife über die Ansichten und nicht einmal pro Auge.
  • Für einen WebGL-Kontext, den du mit der WebXR Layers API (XRWebGLBinding) nutzt, liefert XRSubImage.viewport ebenfalls ein XRViewport.
  • Kompatibilität: WebXR gibt es nur in sicheren Kontexten (HTTPS). Unterstützt wird es vor allem von Chromium-Browsern und dem Meta Quest Browser. Firefox und Safari auf dem Desktop unterstützen die API nicht standardmäßig. Prüf die Verfügbarkeit mit navigator.xr?.isSessionSupported().