Signatur
Beschreibung
Die schreibgeschützte Eigenschaft vertices des Interfaces XRVisibilityMaskChangeEvent enthält die Eckpunkt-Koordinaten der Sichtbarkeitsmaske einer Ansicht. Die Maske beschreibt den Bereich eines Views (z. B. eines Auges), den der Nutzer durch die Linsen des Headsets tatsächlich sehen kann. Das Event wird als visibilitymaskchange auf der XRSession ausgelöst, sobald sich die Maske ändert oder erstmals verfügbar ist.
Die Werte liegen als flaches Array von Koordinatenpaaren vor (x0, y0, x1, y1, …). Sie werden nicht einzeln ausgewertet, sondern immer zusammen mit indices: Je drei Indizes aus indices verweisen auf drei Eckpunkte in vertices und bilden zusammen ein Dreieck. Alle Dreiecke zusammen ergeben die sichtbare Fläche.
Gedacht ist das vor allem für die Performance-Optimierung: Mit den Daten füllt man vor dem eigentlichen Rendern den Stencil- oder Tiefenpuffer. Dadurch muss der Fragment-Shader keine Pixel berechnen, die ohnehin außerhalb des Sichtfelds der Linse liegen. Zu welchem View die Maske gehört, geben die Eigenschaften eye und index des Events an.
Rückgabewert
Beispiele
Maskendaten beim Event auslesen
const session = await navigator.xr.requestSession('immersive-vr');
session.addEventListener('visibilitymaskchange', (event) => {
const { eye, index, vertices, indices } = event;
const vertexCount = vertices.length / 2; // Paare aus x und y
const triangleCount = indices.length / 3;
console.log(`View ${index} (${eye}): ${vertexCount} Eckpunkte, ${triangleCount} Dreiecke`);
});
Maske pro View in WebGL-Puffer hochladen
const gl = canvas.getContext('webgl2', { xrCompatible: true, stencil: true });
const masks = new Map(); // View-Index → Puffer
session.addEventListener('visibilitymaskchange', ({ index, vertices, indices }) => {
if (vertices.length === 0) {
masks.delete(index); // keine Maske → ganzen View rendern
return;
}
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
masks.set(index, { vbo, ibo, count: indices.length });
});
Eckpunkte in Punktobjekte umwandeln
const toPoints = (vertices) => {
const points = [];
for (let i = 0; i < vertices.length; i += 2) {
points.push({ x: vertices[i], y: vertices[i + 1] });
}
return points;
};
session.addEventListener('visibilitymaskchange', (e) => {
const points = toPoints(e.vertices);
console.log(points.slice(0, 2));
});
// Wichtig · Fallstricke
- Experimentell: Das Feature ist nicht standardisiert und wird bisher nur von einigen Chromium-Browsern auf passender XR-Hardware unterstützt. Vor der Nutzung sollte man prüfen, ob es verfügbar ist, z. B. mit
'XRVisibilityMaskChangeEvent' in window. Ohne Unterstützung wird das Event einfach nie ausgelöst. verticesist ohneindicesnicht verwendbar: Die Eckpunkte stehen nicht in der Reihenfolge der Dreiecke. Deshalb immer indiziert zeichnen, z. B. mitgl.drawElements(gl.TRIANGLES, …, gl.UNSIGNED_INT, 0). WeilindiceseinUint32Arrayist, braucht man in WebGL 1 dafür die ErweiterungOES_element_index_uint.- Die Koordinaten beziehen sich auf den jeweiligen View. Welches Koordinatensystem genau gilt und ob die
projectionMatrixdes Views angewendet werden muss, legt die Spezifikation fest. Das sollte man vor dem Einsatz dort nachlesen. - Die Maske kann sich während der Sitzung ändern, z. B. wenn der Linsenabstand verstellt wird. Gespeicherte Puffer müssen bei jedem Event ersetzt und die alten mit
gl.deleteBuffer()freigegeben werden. - Ein leeres Array ist erlaubt. Dann einfach ohne Maske den ganzen View rendern.