Signatur
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
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}`);
});
};
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
XRViewmuss aus dem gerade laufendenrequestAnimationFrame-Callback stammen. Außerhalb davon oder mit einer Ansicht aus einer anderen Session wirft die Methode einenInvalidStateError. - 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.framebufferzu binden. Der Viewport bezieht sich auf diesen Framebuffer und nicht auf das Canvas. Bei Inline-Sessions kannframebufferauchnullsein, dann wird der Standard-Framebuffer verwendet. - Der WebGL-Kontext muss XR-kompatibel sein: Entweder mit
xrCompatible: trueerzeugen oder vorhergl.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 stattdessenXRWebGLBinding.getViewSubImage(), das ebenfalls einen Viewport liefert.