Signatur
Beschreibung
Mit getViewSubImage() fragst du innerhalb eines XR-Animationsframes ab, wohin du eine bestimmte Ansicht (typischerweise das linke oder rechte Auge) rendern sollst. Das zurückgegebene XRWebGLSubImage liefert die vom Compositor bereitgestellte colorTexture, optional eine depthStencilTexture, den zu verwendenden viewport sowie bei Texture-Arrays den imageIndex der Schicht.
Die Methode gehört zur WebXR Layers API und ersetzt bei Projektionsebenen (XRProjectionLayer) das klassische Vorgehen mit XRWebGLLayer und getViewport(). Statt eines einzigen Framebuffers, den der Browser verwaltet, hängst du die gelieferten Texturen selbst an einen eigenen WebGLFramebuffer an. Das erlaubt u. a. Texture-Arrays für Multiview-Rendering und eine bessere Speicher- und Qualitätssteuerung.
Ruf die Methode in jedem Frame neu auf: Der Compositor kann pro Frame eine andere Textur aus seiner Swapchain liefern. Die Objekte sind nur während des aktuellen requestAnimationFrame-Callbacks gültig.
Für Quad-, Zylinder- oder Kubusebenen, die nicht an eine Ansicht gebunden sind, verwendest du stattdessen getSubImage().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $layer Pflicht | XRProjectionLayer | Die Projektionsebene, die zuvor mit createProjectionLayer() derselben XRWebGLBinding erzeugt wurde. |
|
| $view Pflicht | XRView | Die Ansicht, für die gerendert werden soll – in der Regel ein Eintrag aus XRViewerPose.views des aktuellen Frames. |
Rückgabewert
colorTexture (WebGLTexture), depthStencilTexture (WebGLTexture | null), motionVectorTexture, imageIndex (Schicht im Texture-Array oder null), colorTextureWidth/colorTextureHeight und viewport (XRViewport).Beispiele
Session mit Layers starten und Projektionsebene rendern
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2', { xrCompatible: true });
const session = await navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['layers']
});
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);
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);
gl.clearColor(0.1, 0.2, 0.4, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// drawScene(view.projectionMatrix, view.transform.inverse.matrix);
}
};
session.requestAnimationFrame(onFrame);
Texture-Array mit imageIndex und Tiefentextur
const layer = binding.createProjectionLayer({
textureType: 'texture-array',
depthFormat: gl.DEPTH_COMPONENT24
});
session.updateRenderState({ layers: [layer] });
const renderView = (view, fb) => {
const sub = binding.getViewSubImage(layer, view);
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
// Jede Ansicht liegt in einer eigenen Schicht des Arrays
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
sub.colorTexture, 0, sub.imageIndex);
if (sub.depthStencilTexture) {
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
sub.depthStencilTexture, 0, sub.imageIndex);
}
const vp = sub.viewport;
gl.viewport(vp.x, vp.y, vp.width, vp.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
console.log(`Auge ${view.eye}: Schicht ${sub.imageIndex}, ${vp.width}x${vp.height}`);
};
Fehler abfangen, wenn außerhalb des Frames aufgerufen
let lastView = null;
session.requestAnimationFrame((time, frame) => {
lastView = frame.getViewerPose(refSpace)?.views[0] ?? null;
});
setTimeout(() => {
try {
binding.getViewSubImage(layer, lastView);
} catch (err) {
console.warn(err.name); // Frame ist nicht mehr aktiv
}
}, 1000);
// Wichtig · Fallstricke
- Nur im aktiven Animationsframe: Außerhalb des
requestAnimationFrame-Callbacks oder mit einerXRViewaus einem alten Frame wird einInvalidStateErrorausgelöst. - Die Ebene muss von derselben
XRWebGLBindingerzeugt worden sein und zur selben Session gehören wie die Ansicht, sonst ebenfallsInvalidStateError. - Das Feature
'layers'muss beirequestSession()angefordert werden;textureType: 'texture-array'setzt einen WebGL2-Kontext voraus. - Texturen nicht zwischenspeichern – der Compositor kann pro Frame andere Swapchain-Texturen liefern. Die Texturen gehören dem Browser: nicht löschen und ihre Parameter nicht verändern.
- Immer den gelieferten
viewportverwenden; beitextureType: 'texture'teilen sich mehrere Ansichten ggf. eine Textur (Side-by-Side). - Kompatibilität: Experimentell, erfordert einen sicheren Kontext (HTTPS). Unterstützt vor allem in Chromium-basierten XR-Browsern (z. B. Meta Quest Browser); Firefox und Safari bieten die Layers API derzeit nicht an. Vorher per
'XRWebGLBinding' in windowprüfen.