Signatur
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
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}`);
}
};
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:
ywird 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, liefertXRSubImage.viewportebenfalls einXRViewport. - 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().