Signatur
Beschreibung
Die schreibgeschützte Eigenschaft x eines XRViewport-Objekts gibt den horizontalen Versatz in Pixeln an. Gemessen wird von der linken Kante der Zieloberfläche, meist dem Framebuffer eines XRWebGLLayer, bis zur linken Kante des Bereichs, in den eine bestimmte XRView gerendert werden soll.
Bei stereoskopischen Geräten (VR-Headsets) teilen sich meist beide Augen einen Framebuffer. Das linke Auge beginnt dann typischerweise bei x = 0, das rechte bei x = framebufferWidth / 2. Man bekommt den Viewport über XRWebGLLayer.getViewport(view) und reicht x zusammen mit y, width und height direkt an gl.viewport() weiter.
Den Wert selbst zu berechnen, ist keine gute Idee. Übernimm immer den Wert, den der Browser pro Frame und pro View liefert: Aufteilung und Auflösung des Framebuffers hängen vom Gerät ab und können sich während einer Sitzung ändern, etwa durch dynamische Viewport-Skalierung.
Rückgabewert
long) von der linken Kante der Zieloberfläche bis zur linken Kante des Viewports.Beispiele
Viewport pro Auge im Render-Loop setzen
// Voraussetzung: xrSession läuft, gl ist ein XR-kompatibler WebGL-Kontext
const onXRFrame = (time, frame) => {
const session = frame.session;
session.requestAnimationFrame(onXRFrame);
const pose = frame.getViewerPose(xrRefSpace);
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 und y legen die Position innerhalb des Framebuffers fest
gl.viewport(vp.x, vp.y, vp.width, vp.height);
drawScene(view.projectionMatrix, view.transform.inverse.matrix);
}
};
xrSession.requestAnimationFrame(onXRFrame);
Aufteilung des Framebuffers ausgeben
const logViewports = (frame, refSpace) => {
const pose = frame.getViewerPose(refSpace);
if (!pose) return;
const layer = frame.session.renderState.baseLayer;
for (const view of pose.views) {
const { x, y, width, height } = layer.getViewport(view);
console.log(`${view.eye}: x=${x}, y=${y}, ${width}×${height}`);
}
console.log(`Framebuffer: ${layer.framebufferWidth}×${layer.framebufferHeight}`);
};
Scissor-Test auf den Viewport begrenzen
// Verhindert, dass clear() in den Bereich des anderen Auges schreibt
const clearView = (gl, vp) => {
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(0.1, 0.1, 0.15, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.disable(gl.SCISSOR_TEST);
};
// Wichtig · Fallstricke
- Schreibgeschützt: Eine Zuweisung an
xhat keine Wirkung (im Strict Mode gibt es einenTypeError). Wenn du eine andere Auflösung willst, nutzeXRWebGLLayer-Optionen wieframebufferScaleFactoroderXRView.requestViewportScale(). - Nicht zwischenspeichern: Hol dir den Viewport in jedem Frame neu über
getViewport(). Die Werte können sich zwischen Frames ändern. - Koordinatenursprung:
xzählt wie in WebGL von links. Beiyliegt der Ursprung dagegen unten links (WebGL-Konvention), nicht oben wie im DOM. - Bei Mono-Darstellung (z. B.
inline-Sitzungen oder AR auf dem Smartphone) istxmeist0. - Kompatibilität: WebXR steht nur in sicheren Kontexten (HTTPS) zur Verfügung und wird im Wesentlichen von Chromium-Browsern und Headset-Browsern unterstützt. Prüfe die Unterstützung vorher mit
navigator.xr?.isSessionSupported().