Signatur
Beschreibung
Ein XRQuadLayer ist eine rechteckige, flache Ebene im 3D-Raum. Der XR-Kompositor zeigt sie direkt an, typischerweise für Texte, Menüs, Videos oder Bedienoberflächen in hoher Auflösung. Normalerweise zeichnet man den Inhalt nur einmal oder nur bei Änderungen. Den Rest erledigt der Kompositor: Er projiziert die Ebene in jedem Frame passend zur Kopfposition, ohne dass die Anwendung erneut rendern muss.
Das Ereignis redraw vom Typ XRLayerEvent meldet, dass der bisherige Inhalt nicht mehr gültig ist. Das passiert zum Beispiel, wenn die GPU-Ressourcen der Ebene verloren gegangen sind oder der Kompositor die Ebene aus einem anderen Grund nicht mehr darstellen kann. Die Anwendung sollte den Inhalt dann im nächsten Frame über XRWebGLBinding.getSubImage() neu in die Textur der Ebene zeichnen.
So registrieren Sie einen Handler für das Ereignis:
- mit
addEventListener("redraw", handler) - über die Eigenschaft
onredraw
Die Ereignis-Eigenschaft event.layer verweist auf die betroffene Ebene. Zusätzlich gibt es die Eigenschaft needsRedraw des Layers. Sie steht auf true, wenn neu gezeichnet werden muss, und eignet sich als Abfrage in der Render-Schleife.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignistyp, hier immer "redraw". |
|
| $handler Pflicht | (event: XRLayerEvent) => void | Callback, der ein XRLayerEvent erhält. event.layer ist das betroffene XRQuadLayer. |
|
| $options | boolean | AddEventListenerOptions | false | Optionale Einstellungen wie once oder signal zum späteren Entfernen über einen AbortController. |
Rückgabewert
addEventListener() gibt nichts zurück. Das Ereignis ist nicht abbrechbar, der Handler braucht also keinen Rückgabewert.Beispiele
Quad-Layer erzeugen und auf redraw reagieren
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2', { xrCompatible: true });
const session = await navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['layers']
});
const refSpace = await session.requestReferenceSpace('local');
const glBinding = new XRWebGLBinding(session, gl);
const quadLayer = glBinding.createQuadLayer({
space: refSpace,
viewPixelWidth: 1024,
viewPixelHeight: 512,
layout: 'mono',
width: 1.0, // Breite in Metern
height: 0.5,
transform: new XRRigidTransform({ x: 0, y: 1.5, z: -2 })
});
let contentDirty = true; // beim ersten Frame zeichnen
quadLayer.addEventListener('redraw', (event) => {
console.log('Neu zeichnen nötig für:', event.layer);
contentDirty = true;
});
session.updateRenderState({ layers: [quadLayer] });
In der Render-Schleife nur bei Bedarf zeichnen
const framebuffer = gl.createFramebuffer();
const drawMenu = () => {
gl.clearColor(0.1, 0.1, 0.3, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// … hier Menü-Inhalt rendern …
};
const onFrame = (time, frame) => {
frame.session.requestAnimationFrame(onFrame);
// contentDirty wird vom redraw-Handler gesetzt, needsRedraw vom Browser
if (contentDirty || quadLayer.needsRedraw) {
const subImage = glBinding.getSubImage(quadLayer, frame);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.framebufferTexture2D(
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, subImage.colorTexture, 0
);
const { x, y, width, height } = subImage.viewport;
gl.viewport(x, y, width, height);
drawMenu();
contentDirty = false;
}
};
session.requestAnimationFrame(onFrame);
onredraw-Eigenschaft und Entfernen des Handlers
const handleRedraw = () => {
contentDirty = true;
};
// Variante 1: Event-Handler-Eigenschaft
quadLayer.onredraw = handleRedraw;
// Variante 2: addEventListener mit AbortController
const controller = new AbortController();
quadLayer.addEventListener('redraw', handleRedraw, { signal: controller.signal });
// Beim Beenden der Sitzung aufräumen
session.addEventListener('end', () => {
quadLayer.onredraw = null;
controller.abort(); // entspricht removeEventListener('redraw', handleRedraw)
quadLayer.destroy();
});
// Wichtig · Fallstricke
- Experimentelle API: Die WebXR Layers API wird bisher nur von einigen Chromium-basierten XR-Browsern unterstützt, vor allem vom Meta Quest Browser. Firefox und Safari unterstützen sie nicht. Fordern Sie beim Aufruf von
requestSession()das Feature'layers'an und prüfen Sie die Verfügbarkeit, etwa mittypeof XRWebGLBinding !== 'undefined' && 'createQuadLayer' in XRWebGLBinding.prototype. Bauen Sie für Browser ohne Layers eine Alternative, zum Beispiel ein Textur-Quad in der normalen WebGL-Szene. - Zeichnen nur innerhalb eines XR-Frames:
getSubImage()ist nur im Callback vonsession.requestAnimationFrame()gültig. Zeichnen Sie deshalb nicht direkt imredraw-Handler, sondern setzen Sie dort nur ein Flag. - Ereignis nicht als einzige Quelle nutzen: Fragen Sie in der Render-Schleife zusätzlich
quadLayer.needsRedrawab. Die Eigenschaft ist auch unmittelbar nach dem Erzeugen der Ebenetrue, und dann wird nicht unbedingt ein Ereignis gesendet. - Die Ebene muss über
session.updateRenderState({ layers })aktiv sein, sonst wird sie weder angezeigt noch neu gezeichnet. - Die WebGL-Kontexte müssen mit
xrCompatible: trueerzeugt werden oder übermakeXRCompatible()angepasst sein.