Signatur
Beschreibung
WebGLFramebuffer ist ein undurchsichtiges Handle auf ein Framebuffer-Objekt (FBO) der GPU. Ein Framebuffer bündelt mehrere Anhänge (Attachments): typischerweise eine Textur oder einen WebGLRenderbuffer als Farbpuffer sowie optional einen Tiefen- und/oder Schablonenpuffer. Ist ein Framebuffer gebunden, landen alle Zeichenbefehle nicht auf der sichtbaren Canvas, sondern in diesen Anhängen.
Die Schnittstelle besitzt weder eigene Eigenschaften noch Methoden. Sie wird ausschließlich über Methoden des Rendering-Kontexts verwendet: gl.createFramebuffer() erzeugt sie, gl.bindFramebuffer() aktiviert sie, gl.framebufferTexture2D() bzw. gl.framebufferRenderbuffer() hängen Puffer an, gl.checkFramebufferStatus() prüft die Vollständigkeit und gl.deleteFramebuffer() gibt sie wieder frei.
Typische Einsatzgebiete sind Render-to-Texture (Nachbearbeitungseffekte wie Unschärfe, Bloom, Farbkorrektur), Schattenwurf über Shadow Maps, Picking (Objektauswahl per Farb-ID) sowie Offscreen-Berechnungen (GPGPU). Mit WebGL 2 kommen Multiple Render Targets (gl.drawBuffers()), getrennte Lese-/Schreibziele (READ_FRAMEBUFFER/DRAW_FRAMEBUFFER) und gl.blitFramebuffer() hinzu.
Um wieder auf die Canvas zu zeichnen, bindet man null: gl.bindFramebuffer(gl.FRAMEBUFFER, null).
Rückgabewert
gl.createFramebuffer(); ein Konstruktor existiert nicht.Beispiele
Framebuffer mit Textur-Anhang erzeugen (Render-to-Texture)
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const width = 512;
const height = 512;
// Zieltextur anlegen
const targetTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, targetTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// Framebuffer erzeugen und Textur als Farbanhang einhängen
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, targetTexture, 0);
console.log(fbo instanceof WebGLFramebuffer);
console.log(gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE);
// In die Textur zeichnen
gl.viewport(0, 0, width, height);
gl.clearColor(0.2, 0.4, 0.8, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Zurück zur Canvas
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, canvas.width, canvas.height);
Tiefenpuffer per Renderbuffer ergänzen
const gl = document.querySelector('canvas').getContext('webgl');
const size = 256;
const colorTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, colorTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
const depthRb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, depthRb);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, colorTex, 0);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT,
gl.RENDERBUFFER, depthRb);
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
const names = {
[gl.FRAMEBUFFER_COMPLETE]: 'COMPLETE',
[gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT]: 'INCOMPLETE_ATTACHMENT',
[gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT]: 'MISSING_ATTACHMENT',
[gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS]: 'INCOMPLETE_DIMENSIONS',
[gl.FRAMEBUFFER_UNSUPPORTED]: 'UNSUPPORTED'
};
console.log(`Framebuffer-Status: ${names[status] ?? status}`);
gl.enable(gl.DEPTH_TEST);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
Picking: Pixel aus einem Offscreen-Framebuffer lesen und aufräumen
const gl = document.querySelector('canvas').getContext('webgl');
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
const pickFbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, pickFbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D, tex, 0);
// Objekt-ID 42 als Farbe "zeichnen" (hier vereinfacht per clear)
gl.viewport(0, 0, 1, 1);
gl.clearColor(42 / 255, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
const pixel = new Uint8Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
console.log(`Getroffene Objekt-ID: ${pixel[0]}`);
// Ressourcen freigeben
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.deleteFramebuffer(pickFbo);
gl.deleteTexture(tex);
console.log(gl.isFramebuffer(pickFbo));