Signatur
Beschreibung
Die Eigenschaft colorTexture eines XRWebGLSubImage-Objekts enthält die Farbtextur, in die eine WebXR-Anwendung den Inhalt einer Kompositionsebene (XRProjectionLayer, XRQuadLayer, XRCylinderLayer, XREquirectLayer oder XRCubeLayer) für den aktuellen Frame zeichnet. Der XR-Compositor liest diese Textur danach aus und stellt sie auf dem Headset dar.
Das Objekt bekommt man pro Frame über XRWebGLBinding.getViewSubImage(layer, view) (bei Projektionsebenen) oder XRWebGLBinding.getSubImage(layer, frame, eye) (bei den übrigen Ebenen). Die Textur hängt man mit framebufferTexture2D() an einen eigenen Framebuffer an. Hat die Ebene den Texturtyp "texture-array", verwendet man stattdessen framebufferTextureLayer() zusammen mit imageIndex. Den Zeichenbereich innerhalb der Textur legt viewport fest.
Die Textur ist undurchsichtig (opaque): Sie gehört dem XR-System und ist nur innerhalb des requestAnimationFrame-Callbacks der XRSession gültig, in dem sie abgerufen wurde. Man darf sie weder löschen noch in ihrer Größe oder ihrem Format ändern und sollte sie nicht über mehrere Frames hinweg zwischenspeichern.
Rückgabewert
colorTextureWidth und colorTextureHeight.Beispiele
Projektionsebene pro Auge rendern
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);
const layer = binding.createProjectionLayer({ textureType: 'texture' });
session.updateRenderState({ layers: [layer] });
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 subImage = binding.getViewSubImage(layer, view);
// Farbtextur als Renderziel anhängen
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, subImage.colorTexture, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D, subImage.depthStencilTexture, 0);
const { x, y, width, height } = subImage.viewport;
gl.viewport(x, y, width, height);
gl.clearColor(0.1, 0.2, 0.3, 1);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
// drawScene(view) …
}
};
session.requestAnimationFrame(onFrame);
Texture-Array-Ebene mit imageIndex
const layer = binding.createProjectionLayer({ textureType: 'texture-array' });
session.updateRenderState({ layers: [layer] });
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(layer, view);
// Bei Arrays: Schicht über imageIndex auswählen
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
sub.colorTexture, 0, sub.imageIndex);
const { x, y, width, height } = sub.viewport;
gl.viewport(x, y, width, height);
// drawScene(view) …
}
};
session.requestAnimationFrame(onFrame);
Quad-Ebene nur bei Bedarf neu zeichnen
const quad = binding.createQuadLayer({
space: refSpace,
viewPixelWidth: 512,
viewPixelHeight: 512,
layout: 'mono'
});
session.updateRenderState({ layers: [quad] });
const onFrame = (time, frame) => {
session.requestAnimationFrame(onFrame);
if (!quad.needsRedraw) return;
const sub = binding.getSubImage(quad, frame);
console.log(sub.colorTextureWidth, sub.colorTextureHeight);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, sub.colorTexture, 0);
gl.viewport(0, 0, sub.colorTextureWidth, sub.colorTextureHeight);
gl.clearColor(1, 1, 1, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
};
session.requestAnimationFrame(onFrame);
// Wichtig · Fallstricke
- Nur innerhalb des Frames gültig: Die Textur gilt nur im laufenden
XRSession.requestAnimationFrame-Callback. Wer sie außerhalb davon verwendet oder über mehrere Frames hinweg zwischenspeichert, erhält WebGL-Fehler oder ein schwarzes Bild. DasXRWebGLSubImagemuss man daher in jedem Frame neu abrufen. - Undurchsichtige Textur:
gl.deleteTexture(),texImage2D()odertexStorage2D()aufcolorTexturesind nicht erlaubt und führen zu einemINVALID_OPERATION-Fehler. - Richtige Anhänge-Funktion: Bei
textureType: "texture-array"braucht manframebufferTextureLayer()(WebGL 2), bei"texture"dagegenframebufferTexture2D(). - Der WebGL-Kontext muss mit
xrCompatible: trueerstellt werden (oder mitmakeXRCompatible()nachträglich kompatibel gemacht werden), und die Sitzung muss das Feature"layers"anfordern. - Kompatibilität: Die Eigenschaft ist experimentell. Sie ist in Chromium-basierten Browsern mit WebXR-Unterstützung (u. a. Meta Quest Browser) verfügbar, nicht aber in Firefox und Safari. Außerdem ist ein sicherer Kontext (HTTPS) nötig. Vor der Nutzung sollte man mit
'XRWebGLBinding' in windowprüfen, ob die API vorhanden ist.