Start · Sprachen · JavaScript · Referenz · XRWebGLBinding

XRWebGLBinding

Schnittstelle

Verbindet eine <code>XRSession</code> mit einem WebGL-Kontext und erzeugt GPU-gestützte XR-Ebenen (Projektions-, Quad-, Zylinder-, Würfel- und Equirect-Ebenen) samt ihren Texturen.

seit JavaScript WebXR Layers API – Chromium-basiert (Chrome Androi Kategorie: webapi

Signatur

interface XRWebGLBinding

Beschreibung

XRWebGLBinding ist das Bindeglied zwischen einer immersiven XRSession und einem WebGLRenderingContext bzw. WebGL2RenderingContext. Über diese Schnittstelle legst du im Rahmen der WebXR Layers API Ebenen an, deren Inhalte direkt auf der GPU liegen. Außerdem holst du darüber pro Frame die passenden Texturen (Sub-Images), in die du renderst.

Ein typischer Anwendungsfall ist die Projektionsebene (createProjectionLayer()), die die klassische XRWebGLLayer ersetzt und mehr Kontrolle über Texturformate, Tiefenpuffer und Texture-Arrays gibt. Dazu kommen Kompositionsebenen wie createQuadLayer() (flache Fläche im Raum, etwa für Videos oder Text), createCylinderLayer() (gebogene Fläche, gut für UI-Panels), createEquirectLayer() (360°-Panoramen) und createCubeLayer() (Skybox). Der Kompositor des Systems zeichnet diese Ebenen direkt, ohne dass sie vorher erneut abgetastet werden. Deshalb wirken Text und Videos darauf deutlich schärfer.

Im Render-Loop rufst du getViewSubImage(layer, view) für Projektionsebenen bzw. getSubImage(layer, frame, eye) für Kompositionsebenen auf. So bekommst du ein XRWebGLSubImage mit colorTexture, depthStencilTexture, imageIndex und viewport. Weitere Methoden sind getDepthInformation(view) für GPU-Tiefendaten (Depth Sensing), getCameraImage(camera) für den Zugriff auf die Rohkamera und getReflectionCubeMap(lightProbe) für Umgebungsreflexionen. Die Eigenschaften nativeProjectionScaleFactor und usesDepthValues geben Auskunft über die native Auflösung und darüber, ob der Tiefenpuffer genutzt wird.

Wichtig: Den WebGL-Kontext musst du mit xrCompatible: true erzeugen oder vorher makeXRCompatible() aufrufen. Die Session muss das Feature "layers" anfordern. Die erzeugten Ebenen werden erst sichtbar, wenn du sie über session.updateRenderState({ layers: [...] }) registrierst.

Parameter

Name Typ Default Beschreibung
$session Pflicht XRSession Die aktive (in der Regel immersive) XRSession, für die die Ebenen erzeugt werden. Für Kompositionsebenen muss die Session mit dem Feature "layers" angefordert worden sein.
$context Pflicht WebGLRenderingContext | WebGL2RenderingContext Ein XR-kompatibler WebGL-Kontext ({ xrCompatible: true } oder nach await gl.makeXRCompatible()). Texture-Array-Layouts und manche Formate erfordern WebGL 2.

Rückgabewert

Typ
XRWebGLBinding
Beschreibung
Eine neue Bindungsinstanz, über die du Ebenen erzeugst und pro Frame die zugehörigen Sub-Images (Texturen) abfragst. Der Konstruktor wirft einen InvalidStateError, wenn die Session beendet ist oder der Kontext verloren bzw. nicht XR-kompatibel ist.

Beispiele

Binding erstellen und Projektionsebene registrieren

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',      // oder 'texture-array'
    colorFormat: gl.RGBA8,
    depthFormat: gl.DEPTH_COMPONENT24,
    scaleFactor: 1.0
  });

  session.updateRenderState({ layers: [projLayer] });
  console.log('Native Skalierung:', binding.nativeProjectionScaleFactor);

  const refSpace = await session.requestReferenceSpace('local');
  return { session, gl, binding, projLayer, refSpace };
};
Native Skalierung: 1

Render-Loop mit getViewSubImage()

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

const onXRFrame = (time, frame) => {
  session.requestAnimationFrame(onXRFrame);
  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(onXRFrame);

Quad-Ebene für ein scharfes UI-Panel

const quad = binding.createQuadLayer({
  space: refSpace,
  viewPixelWidth: 1024,
  viewPixelHeight: 512,
  layout: 'mono',
  width: 1.0,   // Meter
  height: 0.5,
  transform: new XRRigidTransform({ x: 0, y: 1.5, z: -2 })
});

session.updateRenderState({ layers: [quad, projLayer] });

// Im Frame-Callback: nur neu zeichnen, wenn nötig
const drawQuad = (frame) => {
  if (!quad.needsRedraw) return;
  const sub = binding.getSubImage(quad, frame);
  gl.bindTexture(gl.TEXTURE_2D, sub.colorTexture);
  gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA,
    gl.UNSIGNED_BYTE, uiCanvas); // uiCanvas: 1024×512-Canvas
};