Signatur
Beschreibung
drawBuffersWEBGL() gehört zur WebGL-1-Erweiterung WEBGL_draw_buffers. Damit kann ein einzelner Zeichenaufruf gleichzeitig in mehrere Farbanhänge eines Framebuffers schreiben (Multiple Render Targets, MRT). Im Fragment-Shader schreibt man dann nicht mehr nur in gl_FragColor, sondern in gl_FragData[0], gl_FragData[1] usw.
Typische Einsatzgebiete sind Deferred Shading (G-Buffer mit Position, Normale und Albedo in einem Durchgang), Picking-Puffer parallel zum normalen Rendering oder Post-Processing-Effekte, die mehrere Zwischenergebnisse benötigen.
Das übergebene Array ordnet jedem Fragment-Ausgang gl_FragData[i] ein Ziel zu. Bei einem eigenen Framebuffer muss Element i entweder ext.COLOR_ATTACHMENTi_WEBGL oder gl.NONE sein. Beim Standard-Framebuffer (null gebunden) ist nur ein Array mit genau einem Element erlaubt: gl.BACK oder gl.NONE.
In WebGL 2 ist diese Funktionalität fester Bestandteil der API: dort verwendet man gl.drawBuffers() mit gl.COLOR_ATTACHMENTi und GLSL-ES-3.00-Ausgängen (layout(location = i) out vec4 ...).
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $buffers Pflicht | Array<GLenum> | Liste der Zielpuffer, Index i entspricht gl_FragData[i]. Erlaubte Werte: gl.NONE, gl.BACK (nur Standard-Framebuffer) sowie ext.COLOR_ATTACHMENT0_WEBGL bis ext.COLOR_ATTACHMENT15_WEBGL. Die Länge darf ext.MAX_DRAW_BUFFERS_WEBGL nicht überschreiten. |
Rückgabewert
undefined). Fehler werden über gl.getError() gemeldet (z. B. INVALID_OPERATION oder INVALID_VALUE).Beispiele
Erweiterung prüfen und Grenzen abfragen
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('WEBGL_draw_buffers');
if (!ext) {
console.log('MRT wird nicht unterstützt');
} else {
console.log('Max. Draw Buffers:', gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL));
console.log('Max. Color Attachments:', gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL));
}
G-Buffer mit zwei Farbanhängen einrichten
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('WEBGL_draw_buffers');
const createTex = (w, h) => {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
return tex;
};
const albedoTex = createTex(512, 512);
const normalTex = createTex(512, 512);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, albedoTex, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, normalTex, 0);
// gl_FragData[0] -> Albedo, gl_FragData[1] -> Normale
ext.drawBuffersWEBGL([
ext.COLOR_ATTACHMENT0_WEBGL,
ext.COLOR_ATTACHMENT1_WEBGL
]);
console.log(gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE);
Passender Fragment-Shader (GLSL ES 1.00)
const fragmentSource = `
#extension GL_EXT_draw_buffers : require
precision mediump float;
varying vec3 vNormal;
uniform vec3 uColor;
void main() {
gl_FragData[0] = vec4(uColor, 1.0); // Albedo
gl_FragData[1] = vec4(normalize(vNormal) * 0.5 + 0.5, 1.0); // Normale
}`;
const shader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(shader, fragmentSource);
gl.compileShader(shader);
console.log(gl.getShaderParameter(shader, gl.COMPILE_STATUS));
Einzelnen Ausgang deaktivieren und zurücksetzen
// Nur Anhang 1 beschreiben, gl_FragData[0] verwerfen
ext.drawBuffersWEBGL([gl.NONE, ext.COLOR_ATTACHMENT1_WEBGL]);
// Zurück auf den Canvas rendern
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
ext.drawBuffersWEBGL([gl.BACK]);
// Wichtig · Fallstricke
- Reihenfolge ist fest: Bei einem eigenen Framebuffer muss
buffers[i]entwederCOLOR_ATTACHMENTi_WEBGLodergl.NONEsein –[COLOR_ATTACHMENT1_WEBGL, COLOR_ATTACHMENT0_WEBGL]erzeugtINVALID_OPERATION. - Beim Standard-Framebuffer ist nur
[gl.BACK]oder[gl.NONE]zulässig. - Der Shader braucht
#extension GL_EXT_draw_buffers : require; ohne sie istgl_FragData[i]miti > 0nicht verfügbar.gl_FragColorundgl_FragDatadürfen nicht gleichzeitig beschrieben werden. - Die Einstellung gilt pro Framebuffer-Objekt und bleibt nach dem Binden erhalten.
- Alle Anhänge sollten gleiche Größe haben; nicht jede Format-Kombination ist auf jeder GPU „complete“ – immer
checkFramebufferStatus()prüfen. - Kompatibilität: Auf Desktop-Browsern weit verbreitet, auf älteren Mobilgeräten oft nicht vorhanden. In WebGL-2-Kontexten ist die Erweiterung nicht verfügbar – dort
gl.drawBuffers()verwenden.