Signatur
Beschreibung
Mit readBuffer() wählst du den Lese-Farbpuffer des aktuell an gl.READ_FRAMEBUFFER gebundenen Framebuffers aus. Alle folgenden Lesezugriffe über readPixels() und die copyTex*-Methoden nutzen dann diesen Puffer als Quelle. Die Einstellung gehört zum Zustand des jeweiligen Framebuffer-Objekts und nicht global zum Kontext.
Richtig nützlich wird die Methode bei Multiple Render Targets (MRT): Schreibt ein Fragment-Shader über drawBuffers() gleichzeitig in mehrere Farbanhänge, etwa Albedo, Normalen und IDs in einem G-Buffer, kannst du mit readBuffer(gl.COLOR_ATTACHMENTi) gezielt einen davon auslesen, zum Beispiel für Objekt-Picking oder zum Debuggen.
Welche Werte erlaubt sind, hängt vom gebundenen Framebuffer ab:
- Standard-Framebuffer (
nullgebunden): nurgl.BACKodergl.NONE. - Eigener Framebuffer:
gl.COLOR_ATTACHMENT0bisgl.COLOR_ATTACHMENT{n}(Obergrenze übergl.MAX_COLOR_ATTACHMENTS) odergl.NONE.
Bei einem neu angelegten Framebuffer ist gl.COLOR_ATTACHMENT0 voreingestellt, beim Standard-Framebuffer gl.BACK. Den aktuellen Wert fragst du mit gl.getParameter(gl.READ_BUFFER) ab.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $source Pflicht | GLenum | Farbpuffer, aus dem gelesen wird: gl.BACK (nur Standard-Framebuffer), gl.NONE (keine Lesequelle) oder gl.COLOR_ATTACHMENT0 … gl.COLOR_ATTACHMENT15 (nur eigene Framebuffer, höchstens bis MAX_COLOR_ATTACHMENTS - 1). |
Rückgabewert
undefined). Fehler werden über gl.getError() gemeldet.Beispiele
Zweiten MRT-Anhang für Objekt-Picking auslesen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
const w = canvas.width, h = canvas.height;
const createTex = () => {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, w, h);
return tex;
};
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, createTex(), 0); // Farbe
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, createTex(), 0); // Objekt-IDs
gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
// ... Szene rendern (Shader schreibt in beide Ausgänge) ...
// Pixel unter dem Mauszeiger aus dem ID-Puffer lesen
const pickAt = (x, y) => {
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
gl.readBuffer(gl.COLOR_ATTACHMENT1);
const px = new Uint8Array(4);
gl.readPixels(x, h - y - 1, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, px);
return px[0] | (px[1] << 8) | (px[2] << 16);
};
console.log('Objekt-ID:', pickAt(120, 80));
Aktuellen Lesepuffer abfragen
const gl = document.createElement('canvas').getContext('webgl2');
// Standard-Framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
console.log(gl.getParameter(gl.READ_BUFFER) === gl.BACK);
// Eigener Framebuffer: Standardwert ist COLOR_ATTACHMENT0
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
console.log(gl.getParameter(gl.READ_BUFFER) === gl.COLOR_ATTACHMENT0);
gl.readBuffer(gl.NONE);
console.log(gl.getParameter(gl.READ_BUFFER) === gl.NONE);
Anhang in eine Textur kopieren (copyTexSubImage2D)
// Voraussetzung: fbo mit befülltem COLOR_ATTACHMENT1 (RGBA8), Größe w × h
const copyAttachment = (gl, fbo, w, h) => {
const target = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, target);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, w, h);
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
gl.readBuffer(gl.COLOR_ATTACHMENT1);
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, w, h);
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
return target;
};
Fehlerfall: ungültiger Wert für den Standard-Framebuffer
const gl = document.createElement('canvas').getContext('webgl2');
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.readBuffer(gl.COLOR_ATTACHMENT0); // hier nicht erlaubt
console.log(gl.getError() === gl.INVALID_OPERATION);
// Wichtig · Fallstricke
- Fehlercodes:
INVALID_ENUMbei einem unbekannten Wert.INVALID_OPERATION, wenn beim Standard-Framebuffer etwas anderes alsBACK/NONEübergeben wird oder wenn bei einem eigenen FramebufferCOLOR_ATTACHMENTinicht zum gebundenen Framebuffer passt oderBACKverwendet wird. - Maßgeblich ist das Binding an
gl.READ_FRAMEBUFFER.bindFramebuffer(gl.FRAMEBUFFER, …)setzt Lese- und Zeichen-Binding gleichzeitig. - Die Einstellung wird pro Framebuffer-Objekt gespeichert. Nach einem Wechsel des Framebuffers gilt also dessen eigener Lesepuffer.
- Steht der Lesepuffer auf
gl.NONEoder ist der gewählte Anhang leer, lösenreadPixels()undcopyTex*()den FehlerINVALID_OPERATIONaus. - Bei Anhängen mit Integer-Format (z. B.
RGBA32UI) mussreadPixels()mitgl.RGBA_INTEGERund einem passenden Typ wiegl.UNSIGNED_INTaufgerufen werden. readPixels()blockiert, bis die GPU fertig ist, und kann deshalb ruckeln. Für Lesezugriffe in jedem Frame lohnen sichPIXEL_PACK_BUFFERundfenceSync().- Nur in WebGL 2 verfügbar. WebGL 1 kennt diese Methode nicht; dort liest man aus einem Framebuffer mit nur einem Anhang, bei Bedarf mit der Erweiterung
WEBGL_draw_buffers.