Signatur
Beschreibung
XRViewport.height gibt die Höhe des rechteckigen Bereichs der Zeichenfläche (des Framebuffers einer XRWebGLLayer) in Pixeln an, in den eine einzelne XRView gerendert wird. Zusammen mit x, y und width beschreibt sie genau den Ausschnitt, den du an gl.viewport() übergibst.
Bei stereoskopischen Geräten wie VR-Headsets teilen sich meist zwei Ansichten (linkes und rechtes Auge) einen Framebuffer. Jede Ansicht bekommt ihren eigenen Viewport. Typischerweise liegen die Viewports nebeneinander und sind gleich hoch, aber verlassen solltest du dich darauf nicht: Frag die Werte in jedem Frame über XRWebGLLayer.getViewport(view) neu ab.
Der Wert ist eine Ganzzahl (long) und kann sich zur Laufzeit ändern, etwa wenn der Browser die Auflösung dynamisch anpasst (requestViewportScale()) oder wenn du die Ebene mit einem anderen framebufferScaleFactor neu anlegst.
Beispiele
Viewport pro Auge im Render-Loop setzen
const onXRFrame = (time, frame) => {
const session = frame.session;
session.requestAnimationFrame(onXRFrame);
const pose = frame.getViewerPose(refSpace);
if (!pose) return;
const glLayer = session.renderState.baseLayer;
gl.bindFramebuffer(gl.FRAMEBUFFER, glLayer.framebuffer);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
for (const view of pose.views) {
const vp = glLayer.getViewport(view);
// x, y, width und height direkt an WebGL übergeben
gl.viewport(vp.x, vp.y, vp.width, vp.height);
drawScene(view.projectionMatrix, view.transform.inverse.matrix);
}
};
Seitenverhältnis einer Ansicht berechnen
const logViewportInfo = (glLayer, view) => {
const { width, height } = glLayer.getViewport(view);
const aspect = width / height;
console.log(`${view.eye}: ${width}×${height} px, Seitenverhältnis ${aspect.toFixed(3)}`);
return aspect;
};
// Aufruf innerhalb eines XRFrame-Callbacks:
// pose.views.forEach((view) => logViewportInfo(glLayer, view));
Scissor-Test auf den Viewport begrenzen
const clearViewOnly = (gl, glLayer, view, color) => {
const vp = glLayer.getViewport(view);
gl.enable(gl.SCISSOR_TEST);
gl.scissor(vp.x, vp.y, vp.width, vp.height);
gl.viewport(vp.x, vp.y, vp.width, vp.height);
gl.clearColor(...color);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.disable(gl.SCISSOR_TEST);
};
// Nur das linke Auge rot einfärben (Debugging):
// clearViewOnly(gl, glLayer, pose.views[0], [1, 0, 0, 1]);
// Wichtig · Fallstricke
- Schreibgeschützt: Wenn du
viewport.heightzuweist, passiert nichts. Die Auflösung änderst du überframebufferScaleFactorbeim Anlegen derXRWebGLLayeroder überXRView.requestViewportScale(). - Nicht mit der Canvas-Höhe verwechseln:
heightbezieht sich auf den XR-Framebuffer (glLayer.framebufferHeight), nicht aufcanvas.heightoder die CSS-Größe. - Pro Frame neu abfragen: Viewports können sich zwischen Frames ändern. Wenn du Werte zwischenspeicherst, führt das bei dynamischer Auflösung zu verzerrten Bildern.
- Der Ursprung (
x/y) liegt wie bei WebGL üblich unten links. Die Höhe wird von dort aus nach oben gemessen. - Kompatibilität: WebXR steht nur in sicheren Kontexten (HTTPS) zur Verfügung. Unterstützt wird es in Chromium-basierten Browsern und XR-Browsern wie dem Meta Quest Browser. Firefox und Safari auf dem Desktop unterstützen es nicht. Prüfe die Unterstützung vorher mit
navigator.xr?.isSessionSupported().