Signatur
Beschreibung
XRCylinderLayer gehört zur WebXR Layers API und steht für eine Schicht, die ein Bild auf die Innenseite eines Zylindersegments abbildet. Der XR-Compositor zeichnet die Schicht direkt. Sie wird also nicht erst in die Projektionsschicht Ihrer Szene gerendert und anschließend neu abgetastet. Deshalb bleibt die Darstellung schärfer und es gibt weniger Aliasing. Das zahlt sich vor allem bei Text, Benutzeroberflächen und Videos aus.
Im Vergleich zu einer flachen XRQuadLayer hat die gebogene Fläche einen Vorteil: Alle Stellen des Panels sind ungefähr gleich weit vom Betrachter entfernt, wenn er im Mittelpunkt des Zylinders steht. Breite Panels lassen sich dadurch bis an die Ränder gut lesen, etwa virtuelle Monitore, Kinoleinwände oder Menüleisten.
Einen Konstruktor gibt es nicht. Sie erzeugen eine Instanz mit XRWebGLBinding.prototype.createCylinderLayer(init) oder mit XRMediaBinding.prototype.createCylinderLayer(video, init) für Videos. Die Geometrie bestimmen die Eigenschaften radius, centralAngle (Öffnungswinkel im Bogenmaß) und aspectRatio (Verhältnis von Breite zu Höhe) sowie die Position über space und transform. Alle diese Werte können Sie zur Laufzeit ändern. Vorher muss die Schicht über session.updateRenderState({ layers }) aktiviert werden.
- Eigenschaften:
aspectRatio,centralAngle,radius,space,transformsowie die geerbten Eigenschaften ausXRCompositionLayer(layout,mipLevels,needsRedraw,blendTextureSourceAlpha,chromaticAberrationCorrection) - Methode:
destroy()(geerbt), gibt die Texturen der Schicht frei - Ereignis:
redraw(bzw. die Eigenschaftonredraw). Es wird ausgelöst, wenn der Compositor den Inhalt verloren hat und die Schicht neu gezeichnet werden muss.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $init.space Pflicht | XRSpace | Referenzraum, auf den sich die Position der Schicht bezieht, z. B. aus requestReferenceSpace('local'). |
|
| $init.viewPixelWidth Pflicht | number | Breite der Textur der Schicht in Pixeln. | |
| $init.viewPixelHeight Pflicht | number | Höhe der Textur der Schicht in Pixeln. | |
| $init.radius | number | 2.0 | Radius des Zylinders in Metern. |
| $init.centralAngle | number | 0.78539 (π/4) | Öffnungswinkel des sichtbaren Segments im Bogenmaß. |
| $init.aspectRatio | number | 2.0 | Verhältnis von Breite zu Höhe. Daraus wird die Höhe der Schicht berechnet. |
| $init.transform | XRRigidTransform | Identität | Position und Ausrichtung des Zylindermittelpunkts relativ zu space. |
| $init.layout | "default" | "mono" | "stereo" | "stereo-left-right" | "stereo-top-bottom" | "mono" | Legt fest, wie die Textur auf die Augen verteilt wird. |
| $init.isStatic | boolean | false | Bei true darf nur gezeichnet werden, wenn needsRedraw gesetzt ist. Das spart Speicher und Rechenleistung bei unveränderlichen Inhalten. |
| $init.textureType | "texture" | "texture-array" | "texture" | Art der WebGL-Textur. |
| $init.colorFormat | GLenum | gl.RGBA | Farbformat der Textur. |
| $init.mipLevels | number | 1 | Anzahl der Mipmap-Stufen. |
Rückgabewert
createCylinderLayer() gibt eine neue XRCylinderLayer-Instanz zurück. Sie ist erst sichtbar, wenn sie in renderState.layers eingetragen wurde.Beispiele
Zylinderschicht erzeugen und aktivieren
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 binding = new XRWebGLBinding(session, gl);
// Gebogenes UI-Panel, 1,5 m vor dem Betrachter
const cylinderLayer = binding.createCylinderLayer({
space: refSpace,
viewPixelWidth: 1024,
viewPixelHeight: 512,
layout: 'mono',
radius: 1.5,
centralAngle: Math.PI / 3, // 60°
aspectRatio: 2,
transform: new XRRigidTransform({ x: 0, y: 1.4, z: 0 })
});
const projectionLayer = binding.createProjectionLayer();
// Reihenfolge = Zeichenreihenfolge (hinten nach vorn)
session.updateRenderState({ layers: [projectionLayer, cylinderLayer] });
console.log(cylinderLayer.radius, cylinderLayer.centralAngle.toFixed(3));
Inhalt im Frame-Loop in die Schicht zeichnen
const fb = gl.createFramebuffer();
const onFrame = (time, frame) => {
session.requestAnimationFrame(onFrame);
// Bei statischen Schichten nur bei Bedarf zeichnen
if (cylinderLayer.isStatic && !cylinderLayer.needsRedraw) return;
const subImage = binding.getSubImage(cylinderLayer, frame);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
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);
gl.clearColor(0.1, 0.2, 0.4, 1.0); // blauer Panel-Hintergrund
gl.clear(gl.COLOR_BUFFER_BIT);
// ... hier UI/Text zeichnen ...
};
session.requestAnimationFrame(onFrame);
Geometrie zur Laufzeit ändern und auf redraw reagieren
// Panel per Controller-Taste zur „Kinoleinwand“ aufziehen
const toggleWide = () => {
const wide = cylinderLayer.centralAngle < Math.PI / 2;
cylinderLayer.centralAngle = wide ? Math.PI * 0.75 : Math.PI / 3;
cylinderLayer.aspectRatio = wide ? 16 / 9 * 1.5 : 2;
cylinderLayer.radius = wide ? 3 : 1.5;
};
session.addEventListener('select', toggleWide);
// Compositor hat den Inhalt verloren → neu zeichnen
cylinderLayer.onredraw = () => console.log('Zylinderschicht muss neu gezeichnet werden');
// Beim Beenden Ressourcen freigeben
session.addEventListener('end', () => cylinderLayer.destroy());