Signatur
Beschreibung
Die schreibgeschützte Eigenschaft colorTextureWidth gibt die Breite der WebGL-Farbtextur in Pixeln an, die über XRWebGLSubImage.colorTexture bereitgestellt wird. Diese Textur ist das Ziel, in das die Anwendung für eine bestimmte Composition-Ebene (XRProjectionLayer, XRQuadLayer, XRCylinderLayer usw.) rendert.
Ein XRWebGLSubImage erhält man pro Frame über XRWebGLBinding.getSubImage() (für Nicht-Projektionsebenen) oder XRWebGLBinding.getViewSubImage() (für Projektionsebenen je Ansicht bzw. Auge). Die Breite brauchst du vor allem dann, wenn du eigene Anhänge passend dimensionieren musst, etwa einen Tiefen-Renderbuffer, einen Multisample-Zwischenpuffer oder einen Offscreen-Framebuffer für Post-Processing.
Wichtig: colorTextureWidth beschreibt die gesamte Textur, nicht den Bereich, in den gezeichnet werden soll. Bei Projektionsebenen mit Layout "stereo-left-right" teilen sich beide Augen eine Textur. Die Textur ist dann doppelt so breit wie der Bereich eines Auges. Für gl.viewport() nimmst du deshalb immer subImage.viewport, und colorTextureWidth nur für die Größe von Texturen und Puffern.
Rückgabewert
Beispiele
Texturgröße im Render-Loop einer Projektionsebene auslesen
// Voraussetzung: Session mit requiredFeatures: ['layers'] und ein XR-kompatibler WebGL2-Kontext
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 layer = binding.createProjectionLayer({ textureType: 'texture' });
session.updateRenderState({ layers: [layer] });
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(layer, view);
console.log(`Textur: ${sub.colorTextureWidth}×${sub.colorTextureHeight}`);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, sub.colorTexture, 0);
const { x, y, width, height } = sub.viewport;
gl.viewport(x, y, width, height); // Viewport statt Texturgröße verwenden
// ... Szene für dieses Auge zeichnen
}
};
session.requestAnimationFrame(onFrame);
Passenden Tiefen-Renderbuffer anlegen und bei Größenänderung neu erzeugen
let depthRb = null;
let depthSize = { w: 0, h: 0 };
const ensureDepthBuffer = (gl, sub) => {
const w = sub.colorTextureWidth;
const h = sub.colorTextureHeight;
if (depthRb && depthSize.w === w && depthSize.h === h) return depthRb;
if (depthRb) gl.deleteRenderbuffer(depthRb);
depthRb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthRb);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, w, h);
depthSize = { w, h };
console.log(`Tiefenpuffer neu angelegt: ${w}×${h}`);
return depthRb;
};
// im Frame-Callback:
// const sub = binding.getSubImage(quadLayer, frame);
// const rb = ensureDepthBuffer(gl, sub);
// gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb);
Stereo-Layout erkennen: Texturbreite mit der Viewport-Breite vergleichen
const describeLayout = (sub) => {
const ratio = sub.colorTextureWidth / sub.viewport.width;
return ratio >= 2 ? 'Beide Augen teilen sich eine Textur (links/rechts)'
: 'Eigene Textur bzw. Textur-Array-Ebene pro Auge';
};
// const sub = binding.getViewSubImage(layer, pose.views[0]);
// console.log(describeLayout(sub));
// Wichtig · Fallstricke
- Nicht mit dem Viewport verwechseln: Bei
"stereo-left-right"oder"stereo-top-bottom"ist die Textur größer als der Bereich eines Auges. Fürgl.viewport()undgl.scissor()immersubImage.viewportnehmen. - Die Größe kann sich zwischen Frames ändern, z. B. bei Ebenen mit
isStatic/Neuallokation oder bei geänderter Auflösung. Eigene Puffer deshalb pro Frame prüfen und nicht nur einmal beim Start dimensionieren. - Bei
textureType: "texture-array"gilt die Breite für jede Ebene des Arrays. Die Ebene wählst du überimageIndex. - Das
XRWebGLSubImageund seine Texturen gelten nur innerhalb des aktuellenrequestAnimationFrame-Callbacks. Außerhalb davon den Wert nicht speichern und die Textur nicht verwenden. - Kompatibilität: Die WebXR Layers API ist experimentell. Sie ist nur in sicheren Kontexten (HTTPS) verfügbar und läuft vor allem in Chromium-basierten XR-Browsern (z. B. Meta Quest Browser). Die Session muss mit dem Feature
"layers"angefordert werden. Firefox und Safari unterstützen sie derzeit nicht. Ein Polyfill gibt es im WebXR Layers Polyfill des Immersive Web Working Group.