Start · Sprachen · JavaScript · Referenz · WebGLFramebuffer

WebGLFramebuffer

Schnittstelle

Repräsentiert ein WebGL-Framebuffer-Objekt – eine Sammlung von Anhängen (Farbe, Tiefe, Schablone), die statt der Canvas als Rendering-Ziel dienen kann.

seit JavaScript WebGL 1.0 – Baseline: alle Browser Kategorie: webapi

Signatur

interface WebGLFramebuffer extends WebGLObject

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

Typ
WebGLFramebuffer
Beschreibung
Instanzen entstehen nur über 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);
true true

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);
Framebuffer-Status: COMPLETE

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));
Getroffene Objekt-ID: 42 false