Start · Sprachen · JavaScript · Referenz · XRCylinderLayer

XRCylinderLayer

Schnittstelle

Eine WebXR-Kompositionsschicht, die Inhalte auf einem gebogenen, zylinderförmigen Rechteck im Raum darstellt – gut geeignet für Menüs, Videos und Textpanels.

seit JavaScript Experimentell: WebXR Layers API (u. a. Meta Quest Kategorie: webapi

Signatur

interface XRCylinderLayer extends XRCompositionLayer

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, transform sowie die geerbten Eigenschaften aus XRCompositionLayer (layout, mipLevels, needsRedraw, blendTextureSourceAlpha, chromaticAberrationCorrection)
  • Methode: destroy() (geerbt), gibt die Texturen der Schicht frei
  • Ereignis: redraw (bzw. die Eigenschaft onredraw). 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

Typ
XRCylinderLayer
Beschreibung
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));
1.5 1.047

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());
Zylinderschicht muss neu gezeichnet werden