Start · Sprachen · JavaScript · Referenz · WebGL2RenderingContext.framebufferTextureLayer()

WebGL2RenderingContext.framebufferTextureLayer()

Methode

Hängt eine einzelne Ebene (Layer) einer 3D- oder 2D-Array-Textur als Render-Ziel an das aktuell gebundene Framebuffer-Objekt an.

seit JavaScript WebGL 2 – Baseline: alle modernen Browser Kategorie: webapi

Signatur

WebGL2RenderingContext.prototype.framebufferTextureLayer(target, attachment, texture, level, layer)

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);
true

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);
Kaskaden gerendert: 3

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);
256

// Wichtig · Fallstricke

  • Nur WebGL 2: Die Methode gibt es nicht auf WebGLRenderingContext (WebGL 1). Den Kontext mit getContext('webgl2') anfordern und auf null prüfen. Unterstützt wird sie in allen modernen Browsern (Chrome, Edge, Firefox, Safari ab 15).
  • Texturtyp: Bei einer TEXTURE_2D- oder Cube-Map-Textur entsteht INVALID_OPERATION. Dafür framebufferTexture2D() verwenden.
  • Grenzen: Ist layer negativ, gibt es INVALID_VALUE. Liegt layer über MAX_ARRAY_TEXTURE_LAYERS (Array-Texturen) bzw. MAX_3D_TEXTURE_SIZE (3D-Texturen), ebenfalls. Ein level auß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 Erweiterung OVR_multiview2.
  • Formate wie RGBA32F sind nur mit der Erweiterung EXT_color_buffer_float als Farb-Ziel renderbar.