Signatur
Beschreibung
framebufferTextureLayer() verbindet genau eine Schicht einer TEXTURE_3D- oder TEXTURE_2D_ARRAY-Textur mit einem Anhangspunkt (z. B. COLOR_ATTACHMENT0 oder DEPTH_ATTACHMENT) des Framebuffers, der gerade an target gebunden ist. Danach landen alle Zeichenaufrufe in genau dieser Schicht.
Gebraucht wird die Methode z. B. für Cascaded Shadow Maps (eine Array-Ebene pro Kaskade), für Texturatlanten, die als Array-Textur organisiert sind, oder für Volumen-Rendering und Simulationen, bei denen man Scheibe für Scheibe in eine 3D-Textur rendert. Für normale 2D-Texturen und Cube-Map-Seiten nimmt man stattdessen framebufferTexture2D().
Gibt man für texture den Wert null an, wird der Anhang am angegebenen Punkt entfernt. Ob die Konfiguration vollständig ist, prüft man danach mit checkFramebufferStatus().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | GLenum | — | Framebuffer-Ziel: gl.FRAMEBUFFER (entspricht DRAW_FRAMEBUFFER), gl.DRAW_FRAMEBUFFER oder gl.READ_FRAMEBUFFER. |
| $attachment Pflicht | GLenum | — | Anhangspunkt: gl.COLOR_ATTACHMENT0 … gl.COLOR_ATTACHMENT15, gl.DEPTH_ATTACHMENT, gl.STENCIL_ATTACHMENT oder gl.DEPTH_STENCIL_ATTACHMENT. |
| $texture Pflicht | WebGLTexture | null | — | Eine TEXTURE_3D- oder TEXTURE_2D_ARRAY-Textur. null löst den Anhang. |
| $level Pflicht | GLint | — | Mipmap-Stufe, in die gerendert wird (meist 0). |
| $layer Pflicht | GLint | — | Index der Schicht (Array-Index bzw. Z-Scheibe). Muss kleiner als die Tiefe der Textur auf der jeweiligen Mip-Stufe sein. |
Beispiele
In eine Schicht einer 2D-Array-Textur rendern
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2');
const size = 256;
const layers = 4;
// Array-Textur mit 4 Schichten anlegen
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, tex);
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, size, size, layers);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
// Schicht 2 als Farb-Ziel anhängen
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex, 0, 2);
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
console.log(status === gl.FRAMEBUFFER_COMPLETE);
gl.viewport(0, 0, size, size);
gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT); // färbt nur Schicht 2 rot
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
Cascaded Shadow Maps: jede Kaskade in eine eigene Tiefen-Schicht
const gl = document.createElement('canvas').getContext('webgl2');
const cascades = 3;
const res = 1024;
const depthArray = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, depthArray);
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.DEPTH_COMPONENT24, res, res, cascades);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
const shadowFb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, shadowFb);
gl.drawBuffers([gl.NONE]);
gl.readBuffer(gl.NONE);
const renderSceneDepth = (cascadeIndex) => {
// hier würde die Szene mit der Licht-Matrix der Kaskade gezeichnet
};
for (let i = 0; i < cascades; i++) {
gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, depthArray, 0, i);
gl.viewport(0, 0, res, res);
gl.clear(gl.DEPTH_BUFFER_BIT);
renderSceneDepth(i);
}
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
console.log('Kaskaden gerendert:', cascades);
Scheibe einer 3D-Textur auslesen und Anhang wieder lösen
const gl = document.createElement('canvas').getContext('webgl2');
const [w, h, d] = [8, 8, 8];
const vol = gl.createTexture();
gl.bindTexture(gl.TEXTURE_3D, vol);
gl.texStorage3D(gl.TEXTURE_3D, 1, gl.RGBA8, w, h, d);
const readFb = gl.createFramebuffer();
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, readFb);
gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, vol, 0, 5);
const pixels = new Uint8Array(w * h * 4);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
console.log(pixels.length);
// Anhang entfernen
gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, null, 0, 0);
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
// Wichtig · Fallstricke
- Nur WebGL 2: Die Methode gibt es nicht auf
WebGLRenderingContext(WebGL 1). Den Kontext mitgetContext('webgl2')anfordern und aufnullprüfen. Unterstützt wird sie in allen modernen Browsern (Chrome, Edge, Firefox, Safari ab 15). - Texturtyp: Bei einer
TEXTURE_2D- oder Cube-Map-Textur entstehtINVALID_OPERATION. DafürframebufferTexture2D()verwenden. - Grenzen: Ist
layernegativ, gibt esINVALID_VALUE. LiegtlayerüberMAX_ARRAY_TEXTURE_LAYERS(Array-Texturen) bzw.MAX_3D_TEXTURE_SIZE(3D-Texturen), ebenfalls. Einlevelaußerhalb der vorhandenen Mip-Stufen macht den Framebuffer unvollständig. - Feedback-Schleifen: Man darf nicht in eine Schicht rendern und gleichzeitig dieselbe Textur(-stufe) im Shader lesen. Das ergibt undefiniertes Verhalten oder einen
INVALID_OPERATION-Fehler beim Zeichnen. - Pro Aufruf wird nur eine Schicht angehängt. Layered Rendering (mehrere Schichten in einem Draw-Call) wie in Desktop-OpenGL kennt WebGL 2 nicht. Man braucht also eine Schleife über die Schichten, mehrere Farb-Anhänge mit
drawBuffers()oder die ErweiterungOVR_multiview2. - Formate wie
RGBA32Fsind nur mit der ErweiterungEXT_color_buffer_floatals Farb-Ziel renderbar.