Start · Sprachen · JavaScript · Referenz · XRQuadLayer: redraw

XRQuadLayer: redraw

Ereignis

Wird an ein <code>XRQuadLayer</code> gesendet, wenn seine Ressourcen verloren gegangen sind oder der XR-Kompositor ihn nicht mehr neu projizieren kann. Der Inhalt muss dann neu gezeichnet werden.

seit JavaScript Experimentell: WebXR Layers API (Chromium-basierte Kategorie: webapi

Signatur

xrQuadLayer.addEventListener("redraw", handler)

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

Typ
void
Beschreibung
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] });
Neu zeichnen nötig für: XRQuadLayer {…}

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 mit typeof 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 von session.requestAnimationFrame() gültig. Zeichnen Sie deshalb nicht direkt im redraw-Handler, sondern setzen Sie dort nur ein Flag.
  • Ereignis nicht als einzige Quelle nutzen: Fragen Sie in der Render-Schleife zusätzlich quadLayer.needsRedraw ab. Die Eigenschaft ist auch unmittelbar nach dem Erzeugen der Ebene true, 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: true erzeugt werden oder über makeXRCompatible() angepasst sein.