Signatur
Beschreibung
Die Eigenschaft baseLayer eines XRRenderState-Objekts verweist auf die Basisebene einer WebXR-Sitzung. Das ist in der Regel eine XRWebGLLayer. Sie hängt an einem WebGL-Kontext und stellt einen Framebuffer bereit, in den die Anwendung jedes Bild zeichnet. Der XR-Compositor nimmt diesen Framebuffer und zeigt ihn im Headset oder auf dem AR-Display an.
Gesetzt wird die Ebene nicht direkt über die Eigenschaft, denn sie ist schreibgeschützt. Stattdessen ruft man XRSession.updateRenderState({ baseLayer }) auf. Die Änderung gilt erst ab dem nächsten Animationsbild. Den aktuellen Zustand liest man über session.renderState.baseLayer, zum Beispiel im Callback von requestAnimationFrame(). Dort holt man sich den framebuffer und mit getViewport(view) den Viewport für jedes Auge.
Solange noch keine Ebene gesetzt ist, ist der Wert null. In diesem Fall zeigt die Sitzung nichts an, und requestAnimationFrame-Callbacks werden nicht ausgeführt. Wer das neuere WebXR Layers API nutzt, setzt statt baseLayer die Eigenschaft layers. Beide Varianten lassen sich nicht gleichzeitig verwenden.
Rückgabewert
null, wenn noch keine Ebene festgelegt wurde (oder stattdessen layers verwendet wird).Beispiele
Basisebene beim Start einer VR-Sitzung setzen
const startXR = async () => {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2', { xrCompatible: true });
const session = await navigator.xr.requestSession('immersive-vr');
const layer = new XRWebGLLayer(session, gl);
// Direkt nach dem Aufruf noch null – die Änderung gilt erst ab dem nächsten Bild
await session.updateRenderState({ baseLayer: layer });
console.log(session.renderState.baseLayer === null);
session.requestAnimationFrame(() => {
console.log(session.renderState.baseLayer === layer);
});
};
document.querySelector('#vr-button').addEventListener('click', startXR);
Framebuffer und Viewports im Render-Loop nutzen
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.clearColor(0.1, 0.1, 0.2, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
for (const view of pose.views) {
const vp = glLayer.getViewport(view);
gl.viewport(vp.x, vp.y, vp.width, vp.height);
drawScene(view.projectionMatrix, view.transform.inverse.matrix);
}
};
Ebene mit geringerer Auflösung austauschen (Performance)
const reduceResolution = async (session, gl) => {
const old = session.renderState.baseLayer;
const scale = XRWebGLLayer.getNativeFramebufferScaleFactor(session) * 0.5;
const layer = new XRWebGLLayer(session, gl, { framebufferScaleFactor: scale });
await session.updateRenderState({ baseLayer: layer });
console.log(`Alt: ${old.framebufferWidth}px, neu ab nächstem Bild: ${layer.framebufferWidth}px`);
};
// Wichtig · Fallstricke
- Schreibgeschützt: Eine direkte Zuweisung wie
renderState.baseLayer = layerhat keine Wirkung. Setze die Ebene immer übersession.updateRenderState(). - Verzögerte Übernahme: Neue Werte werden erst zu Beginn des nächsten Animationsbilds sichtbar. Unmittelbar nach
updateRenderState()liefert die Eigenschaft noch den alten Wert. - Der WebGL-Kontext muss XR-kompatibel sein: Entweder mit
{ xrCompatible: true }erzeugen oder vorherawait gl.makeXRCompatible()aufrufen. Sonst wirft derXRWebGLLayer-Konstruktor einenInvalidStateError. - Die Ebene gehört fest zu ihrer Sitzung. Eine
XRWebGLLayeraus einer anderen Sitzung führt zu einem Fehler. Ist die Sitzung beendet, darf man die Ebene nicht weiterverwenden. - Wird
layers(WebXR Layers API) gesetzt, bleibtbaseLayeraufnull. Beide gleichzeitig anzugeben löst einenNotSupportedErroraus. - Kompatibilität: Die Eigenschaft ist nur in sicheren Kontexten (HTTPS) verfügbar. Unterstützt wird sie vor allem von Chromium-Browsern und Headset-Browsern. Firefox und Safari (Desktop) unterstützen WebXR nicht. Prüfe deshalb vorher mit
'xr' in navigatorundnavigator.xr.isSessionSupported().