Start · Sprachen · JavaScript · Referenz · XRProjectionLayer

XRProjectionLayer

Schnittstelle

Eine WebXR-Kompositionsebene, die die gesamte Sicht des Betrachters ausfüllt und nahezu mit der nativen Bildrate des XR-Geräts neu gezeichnet wird.

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

Signatur

interface XRProjectionLayer extends XRCompositionLayer

Beschreibung

XRProjectionLayer gehört zum WebXR Layers Module und stellt die klassische „Hauptebene“ einer immersiven Sitzung dar: Sie deckt das gesamte Sichtfeld beider Augen ab und wird in jedem Frame neu gerendert. Damit ersetzt sie in Layer-basierten Anwendungen die ältere XRWebGLLayer, bietet aber mehr Kontrolle, z. B. über Textur-Arrays (ein Draw-Aufruf für beide Augen mittels Multiview), Farb- und Tiefenformate sowie die Auflösung über einen Skalierungsfaktor.

Einen Konstruktor gibt es nicht. Instanzen werden über XRWebGLBinding.createProjectionLayer(options) erzeugt und anschließend per XRSession.updateRenderState({ layers: [...] }) in die Sitzung eingehängt. Im Render-Loop liefert XRWebGLBinding.getViewSubImage(layer, view) pro Ansicht ein XRWebGLSubImage mit Farbtextur, Tiefentextur, Viewport und ggf. Array-Index, in das gezeichnet wird.

Wichtige Eigenschaften (zusätzlich zu den geerbten aus XRCompositionLayer wie layout, blendTextureSourceAlpha, mipLevels, needsRedraw und destroy()):

  • textureWidth, textureHeight (schreibgeschützt): Größe der zugrunde liegenden Texturen in Pixeln.
  • textureArrayLength (schreibgeschützt): Anzahl der Ebenen, wenn textureType: "texture-array" verwendet wird, sonst 1.
  • ignoreDepthValues (schreibgeschützt): true, wenn der Compositor den Tiefenpuffer nicht auswertet.
  • fixedFoveation: Zahl von 0 bis 1 bzw. null; steuert, wie stark die Auflösung am Bildrand reduziert wird (Foveated Rendering), um Leistung zu sparen.
  • deltaPose (schreibgeschützt, optional): Pose-Differenz für Reprojektion, sofern vom Gerät unterstützt.

Parameter

Name Typ Default Beschreibung
$options.textureType "texture" | "texture-array" "texture" Optionen-Feld von createProjectionLayer(): "texture-array" liefert ein TEXTURE_2D_ARRAY (für Multiview, WebGL2 nötig).
$options.colorFormat GLenum gl.RGBA8 Internes Farbformat der Farbtexturen, z. B. gl.RGBA8 oder gl.SRGB8_ALPHA8.
$options.depthFormat GLenum gl.DEPTH_COMPONENT Format der Tiefen-/Stenciltextur; 0 bedeutet: keine Tiefentextur anlegen.
$options.scaleFactor number 1.0 Skalierung der empfohlenen Texturauflösung; Werte < 1 sparen GPU-Leistung, Werte > 1 erhöhen die Schärfe.

Beispiele

Projektionsebene erzeugen und in die Sitzung einhängen

const startXR = async () => {
  const session = await navigator.xr.requestSession('immersive-vr', {
    requiredFeatures: ['layers']
  });

  const canvas = document.createElement('canvas');
  const gl = canvas.getContext('webgl2', { xrCompatible: true });

  const binding = new XRWebGLBinding(session, gl);
  const projLayer = binding.createProjectionLayer({
    textureType: 'texture',
    colorFormat: gl.RGBA8,
    depthFormat: gl.DEPTH_COMPONENT24,
    scaleFactor: 1.0
  });

  session.updateRenderState({ layers: [projLayer] });
  console.log(`Textur: ${projLayer.textureWidth}x${projLayer.textureHeight}`);
  return { session, gl, binding, projLayer };
};

startXR().catch(err => console.error('XR nicht verfügbar:', err));
Textur: 1832x1920

Render-Loop mit getViewSubImage()

const { session, gl, binding, projLayer } = await startXR();
const refSpace = await session.requestReferenceSpace('local');
const fb = gl.createFramebuffer();

const onFrame = (time, frame) => {
  session.requestAnimationFrame(onFrame);
  const pose = frame.getViewerPose(refSpace);
  if (!pose) return;

  gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
  for (const view of pose.views) {
    const sub = binding.getViewSubImage(projLayer, view);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
      gl.TEXTURE_2D, sub.colorTexture, 0);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
      gl.TEXTURE_2D, sub.depthStencilTexture, 0);

    const { x, y, width, height } = sub.viewport;
    gl.viewport(x, y, width, height);
    gl.clearColor(0.1, 0.1, 0.2, 1);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // drawScene(view.projectionMatrix, view.transform.inverse.matrix);
  }
};
session.requestAnimationFrame(onFrame);

Foveated Rendering für mehr Leistung aktivieren

const tuneLayer = (layer) => {
  if (layer.fixedFoveation !== null) {
    layer.fixedFoveation = 0.5; // mittlere Foveation
  }
  console.log('Foveation:', layer.fixedFoveation,
    '| Array-Länge:', layer.textureArrayLength,
    '| Tiefe ignoriert:', layer.ignoreDepthValues);
};

tuneLayer(projLayer);
Foveation: 0.5 | Array-Länge: 1 | Tiefe ignoriert: false