Signatur
Beschreibung
Die Methode createProjectionLayer() des Interfaces XRWebGLBinding legt eine Projektionsebene (XRProjectionLayer) an. Eine Projektionsebene deckt das komplette Sichtfeld ab und wird für jedes Auge (jede XRView) passend gerendert. Sie ersetzt in der WebXR Layers API die klassische XRWebGLLayer als Hauptebene für die 3D-Szene.
Im Unterschied zu XRWebGLLayer gibt es keinen gemeinsamen Framebuffer. Das XR-System stellt stattdessen Texturen bereit. Diese holst du pro Frame und pro View mit XRWebGLBinding.getViewSubImage() und hängst sie an einen eigenen Framebuffer an. Mit textureType: "texture-array" lassen sich beide Augen in einer Array-Textur ablegen. Das ist die Grundlage für Multiview-Rendering (OVR_multiview2), das die GPU deutlich weniger belastet.
Voraussetzung ist eine XR-Sitzung, die mit dem Feature "layers" angefordert wurde. Die erzeugte Ebene wird erst sichtbar, wenn du sie über XRSession.updateRenderState({ layers: [...] }) in die Ebenenliste der Sitzung einträgst. Dort kannst du sie mit weiteren Ebenen kombinieren, zum Beispiel mit Quad-, Zylinder- oder Kugelebenen für scharfen Text oder Videos.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | object | {} | Optionales Konfigurationsobjekt (XRProjectionLayerInit) mit den folgenden Eigenschaften. |
| $options.textureType | "texture" | "texture-array" | "texture" | "texture" liefert eine 2D-Textur pro View. "texture-array" liefert eine TEXTURE_2D_ARRAY mit einer Schicht pro View. Das setzt WebGL2 voraus und eignet sich für Multiview. |
| $options.colorFormat | GLenum | gl.RGBA8 (0x8058) | Internes Format der Farbtextur, z. B. gl.RGBA8 oder gl.SRGB8_ALPHA8. |
| $options.depthFormat | GLenum | gl.DEPTH_COMPONENT (0x1902) | Internes Format der Tiefen-/Stencil-Textur, z. B. gl.DEPTH_COMPONENT24 oder gl.DEPTH24_STENCIL8. Mit 0 wird keine Tiefentextur angelegt. |
| $options.scaleFactor | number | 1.0 | Skalierungsfaktor für die Texturauflösung, bezogen auf die empfohlene Auflösung des Geräts. Werte unter 1 verbessern die Leistung, machen das Bild aber unschärfer. |
Rückgabewert
XRWebGLBinding.getViewSubImage(layer, view).Beispiele
Sitzung mit Layers-Feature starten und Projektionsebene setzen
const startXR = async () => {
const session = await navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['layers']
});
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2', { xrCompatible: true });
const binding = new XRWebGLBinding(session, gl);
const projLayer = binding.createProjectionLayer({
textureType: 'texture',
colorFormat: gl.RGBA8,
depthFormat: gl.DEPTH_COMPONENT24
});
// Ebene aktivieren – ersetzt baseLayer
session.updateRenderState({ layers: [projLayer] });
console.log('Texturgröße:', projLayer.textureWidth, 'x', projLayer.textureHeight);
return { session, gl, binding, projLayer };
};
startXR();
Pro Frame in die Texturen der Ebene rendern
const { session, gl, binding, projLayer } = await startXR();
const refSpace = await session.requestReferenceSpace('local');
const fb = gl.createFramebuffer();
const onFrame = (time, frame) => {
session.requestAnimationFrame(onFrame);
const pose = frame.getViewerPose(refSpace);
if (!pose) return;
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
for (const view of pose.views) {
const sub = binding.getViewSubImage(projLayer, view);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, sub.colorTexture, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D, sub.depthStencilTexture, 0);
const { x, y, width, height } = sub.viewport;
gl.viewport(x, y, width, height);
gl.clearColor(0.1, 0.1, 0.2, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// drawScene(view.projectionMatrix, view.transform.inverse.matrix);
}
};
session.requestAnimationFrame(onFrame);
Fallback bei fehlender Layers-Unterstützung und reduzierte Auflösung
const createMainLayer = (session, gl) => {
if (typeof XRWebGLBinding !== 'undefined' &&
'createProjectionLayer' in XRWebGLBinding.prototype) {
try {
const binding = new XRWebGLBinding(session, gl);
const layer = binding.createProjectionLayer({ scaleFactor: 0.8 });
session.updateRenderState({ layers: [layer] });
return { type: 'projection', layer, binding };
} catch (err) {
console.warn('Projektionsebene nicht möglich:', err.name);
}
}
// Klassischer Weg über XRWebGLLayer
const baseLayer = new XRWebGLLayer(session, gl);
session.updateRenderState({ baseLayer });
return { type: 'webgl', layer: baseLayer };
};
// Wichtig · Fallstricke
- Feature anfordern: Ohne
"layers"inrequiredFeaturesoderoptionalFeatureswirft die Methode einenNotSupportedError. - InvalidStateError tritt auf, wenn die Sitzung bereits beendet ist oder der WebGL-Kontext verloren ging.
textureType: "texture-array"funktioniert nur mit einem WebGL2-Kontext. Mit WebGL1 gibt es einenTypeErrorbzw.NotSupportedError.- Die Texturen gehören dem XR-System. Nutze sie nur innerhalb des aktuellen
requestAnimationFrame-Callbacks und lösche sie nicht selbst. Frage sie jeden Frame neu mitgetViewSubImage()ab, denn das System kann zwischen mehreren Texturen wechseln. - Pro Sitzung sollte es nur eine Projektionsebene geben. Setze sie nicht gleichzeitig mit
baseLayerinupdateRenderState(), sonst wird eine Ausnahme ausgelöst. - Kompatibilität: Die WebXR Layers API wird vor allem im Meta Quest Browser und in Chromium-basierten Browsern unterstützt, dort teilweise nur hinter einem Flag. Firefox und Safari unterstützen sie nicht. Prüfe die Unterstützung vorher (siehe Fallback-Beispiel). WebXR läuft außerdem nur in einem sicheren Kontext (HTTPS).