Signatur
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
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 };
};
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
};