Signatur
Beschreibung
Die Eigenschaft textureHeight einer XRProjectionLayer gibt an, wie hoch (in Pixeln) die Farbtexturen sind, die der Browser für diese Projektionsebene bereitstellt. Die Größe legst du nicht direkt fest. Der Browser (User Agent) bestimmt sie beim Erzeugen der Ebene über XRWebGLBinding.createProjectionLayer(). Dabei richtet er sich nach der nativen Auflösung des Geräts und nach der Option scaleFactor.
Du brauchst den Wert vor allem, wenn eigene GPU-Ressourcen genau zur Ebene passen müssen. Beispiele sind ein eigener Tiefenpuffer (wenn beim Erzeugen depthFormat: 0 angegeben wurde), Offscreen-Renderziele für Nachbearbeitung oder Multisampling-Renderbuffer. Zusammen mit textureWidth und textureArrayLength beschreibt der Wert die vollständige Geometrie der Texturen.
Wichtig: textureHeight ist die Höhe der gesamten Textur und nicht die Höhe eines einzelnen Augen-Bildbereichs. Welcher Ausschnitt für eine bestimmte Ansicht gilt, erfährst du in jedem Frame über XRWebGLBinding.getViewSubImage() bzw. über dessen Eigenschaft viewport.
Rückgabewert
unsigned long): die Höhe der Farbtexturen der Ebene in Pixeln. Der Wert ist schreibgeschützt.Beispiele
Texturgröße nach dem Erzeugen der Ebene ausgeben
const startXR = async (canvas) => {
const session = await navigator.xr.requestSession('immersive-vr', {
requiredFeatures: ['layers']
});
const gl = canvas.getContext('webgl2', { xrCompatible: true });
const binding = new XRWebGLBinding(session, gl);
const layer = binding.createProjectionLayer({
textureType: 'texture-array',
colorFormat: gl.RGBA8,
scaleFactor: 1.0
});
session.updateRenderState({ layers: [layer] });
console.log(`Textur: ${layer.textureWidth} x ${layer.textureHeight}, Layer: ${layer.textureArrayLength}`);
return { session, gl, binding, layer };
};
document.querySelector('#enter-vr').addEventListener('click', () => {
startXR(document.querySelector('canvas'));
});
Eigenen Tiefenpuffer passend zur Ebene anlegen
// Ebene ohne vom Browser verwalteten Tiefenpuffer erzeugen
const layer = binding.createProjectionLayer({
textureType: 'texture',
colorFormat: gl.RGBA8,
depthFormat: 0
});
// Eigenen Tiefen-Renderbuffer exakt in Texturgröße anlegen
const depthBuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(
gl.RENDERBUFFER,
gl.DEPTH_COMPONENT24,
layer.textureWidth,
layer.textureHeight
);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
Gesamthöhe mit dem Viewport pro Auge vergleichen
const onXRFrame = (time, frame) => {
const session = frame.session;
session.requestAnimationFrame(onXRFrame);
const pose = frame.getViewerPose(refSpace);
if (!pose) return;
for (const view of pose.views) {
const subImage = binding.getViewSubImage(layer, view);
const { x, y, width, height } = subImage.viewport;
// height ist höchstens so groß wie layer.textureHeight
console.log(view.eye, height, '/', layer.textureHeight);
gl.viewport(x, y, width, height);
}
};
session.requestAnimationFrame(onXRFrame);
// Wichtig · Fallstricke
- Kompatibilität: Die WebXR Layers API ist experimentell und steht nur in Chromium-basierten Browsern zur Verfügung, vor allem im Meta Quest Browser und in Chrome mit WebXR. Firefox und Safari unterstützen sie derzeit nicht. Die API funktioniert nur in einem sicheren Kontext (HTTPS), und die Sitzung muss mit dem Feature
'layers'angefordert worden sein. - Nicht mit der Viewport-Größe verwechseln: Bei
textureType: 'texture'liegen beide Augen oft in einer Textur. Deren Höhe entspricht dann zwar meist der Höhe eines Auges, die Breite aber nicht. Für das Rendern gilt immergetViewSubImage(...).viewport. - Nicht ableitbar: Die tatsächliche Größe hängt vom Gerät und vom
scaleFactorab. Der Browser kann sie außerdem nach oben begrenzen (sieheXRWebGLBinding.nativeProjectionScaleFactor). Rechne sie also nicht selbst aus, sondern lies sie immer aus. - Der Wert bleibt über die Lebensdauer der Ebene gleich. Für eine andere Auflösung musst du eine neue Ebene erzeugen und per
updateRenderState()setzen.