Signatur
Beschreibung
XRView steht für einen einzelnen Blickpunkt, von dem aus die XR-Szene in einem bestimmten Frame gerendert werden muss. Bei einem VR-Headset gibt es meist zwei Ansichten (linkes und rechtes Auge), bei Handheld-AR auf dem Smartphone meist nur eine. Man erzeugt XRView-Objekte nicht selbst. Man erhält sie über das Array XRViewerPose.views, das XRFrame.getViewerPose(referenceSpace) liefert.
Die wichtigsten Eigenschaften:
eye:"left","right"oder"none". Gibt an, für welches Auge die Ansicht gedacht ist.projectionMatrix: EineFloat32Arraymit 16 Werten (4×4, spaltenweise). Das ist die Projektionsmatrix für den Shader.transform: EinXRRigidTransformmit Position und Ausrichtung der Ansicht. Die View-Matrix erhält man übertransform.inverse.matrix.recommendedViewportScale: Ein empfohlener Skalierungsfaktor für den Viewport (odernull). Man übergibt ihn anrequestViewportScale().requestViewportScale(scale): Fordert für diese Ansicht einen kleineren Viewport an, zum Beispiel um bei hoher GPU-Last Leistung zu sparen.
Typisch ist folgender Ablauf: In jedem Frame des requestAnimationFrame-Callbacks der XRSession geht man alle Views durch. Für jede View fragt man mit XRWebGLLayer.getViewport(view) den passenden Viewport ab, setzt Projektions- und View-Matrix und zeichnet die Szene. So entsteht stereoskopisches Rendering, das zur Kopfposition passt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $scale | number | null | Nur für requestViewportScale(scale): gewünschte Viewport-Skalierung im Bereich (0, 1]. Werte außerhalb dieses Bereichs oder null werden ignoriert. |
Rückgabewert
XRViewerPose.views bereitgestellt. Es gibt keinen öffentlichen Konstruktor. requestViewportScale() gibt undefined zurück.Beispiele
Views eines Frames durchlaufen und rendern
// gl: WebGL2-Kontext, der mit xrCompatible: true erstellt wurde
const session = await navigator.xr.requestSession('immersive-vr');
await gl.makeXRCompatible();
const layer = new XRWebGLLayer(session, gl);
session.updateRenderState({ baseLayer: layer });
const refSpace = await session.requestReferenceSpace('local');
const onFrame = (time, frame) => {
session.requestAnimationFrame(onFrame);
const pose = frame.getViewerPose(refSpace);
if (!pose) return; // Tracking verloren
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 ist die eigene Render-Funktion
drawScene(view.projectionMatrix, view.transform.inverse.matrix);
}
};
session.requestAnimationFrame(onFrame);
Auge und Kameraposition auslesen
const logViews = (frame, refSpace) => {
const pose = frame.getViewerPose(refSpace);
if (!pose) return;
for (const view of pose.views) {
const { x, y, z } = view.transform.position;
console.log(`${view.eye}: (${x.toFixed(3)}, ${y.toFixed(3)}, ${z.toFixed(3)})`);
}
};
Dynamische Viewport-Skalierung nutzen
const onFrame = (time, frame) => {
session.requestAnimationFrame(onFrame);
const pose = frame.getViewerPose(refSpace);
if (!pose) return;
for (const view of pose.views) {
// Empfehlung des Geräts übernehmen, falls vorhanden
if (view.recommendedViewportScale) {
view.requestViewportScale(view.recommendedViewportScale);
}
// Wichtig: getViewport erst NACH requestViewportScale aufrufen
const vp = layer.getViewport(view);
gl.viewport(vp.x, vp.y, vp.width, vp.height);
drawScene(view.projectionMatrix, view.transform.inverse.matrix);
}
};
// Wichtig · Fallstricke
- Nur innerhalb des Frames gültig:
XRView-Objekte gehören zum jeweiligenXRFrame. Man sollte sie nicht über den Animation-Callback hinaus speichern. Pose und Views müssen in jedem Frame neu abgefragt werden. transformist keine View-Matrix:transform.matrixbeschreibt die Pose der Kamera. Für den Shader braucht man die Inverse, alsotransform.inverse.matrix.- Anzahl der Views nicht fest verdrahten: Je nach Gerät gibt es 1, 2 oder mehr Views. Deshalb immer über
pose.viewsiterieren undeyenicht als gegeben voraussetzen. getViewerPose()kannnullliefern, etwa wenn das Tracking noch nicht bereit ist. Das muss man abfangen.requestViewportScale()wirkt nur, wenn man danach im selben FramegetViewport()aufruft. Die Methode wird nicht überall unterstützt.- Kompatibilität: WebXR erfordert einen sicheren Kontext (HTTPS). Unterstützung gibt es in Chromium-Browsern und im Meta Quest Browser. Firefox unterstützt WebXR nicht, Safari nur auf visionOS. Vorher mit
navigator.xr?.isSessionSupported()prüfen.