Start · Sprachen · JavaScript · Referenz · WEBGL_draw_buffers.drawBuffersWEBGL()

WEBGL_draw_buffers.drawBuffersWEBGL()

Methode

Legt fest, in welche Farbanhänge (Draw Buffers) ein Fragment-Shader schreibt – Grundlage für Multiple Render Targets (MRT) in WebGL 1.

seit JavaScript WebGL 1 Erweiterung; in WebGL 2 als gl.drawBuffers Kategorie: webapi

Signatur

WEBGL_draw_buffers.drawBuffersWEBGL(buffers)

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

Typ
void
Beschreibung
Kein 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));
}
Max. Draw Buffers: 8 Max. Color Attachments: 8

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

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

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] entweder COLOR_ATTACHMENTi_WEBGL oder gl.NONE sein – [COLOR_ATTACHMENT1_WEBGL, COLOR_ATTACHMENT0_WEBGL] erzeugt INVALID_OPERATION.
  • Beim Standard-Framebuffer ist nur [gl.BACK] oder [gl.NONE] zulässig.
  • Der Shader braucht #extension GL_EXT_draw_buffers : require; ohne sie ist gl_FragData[i] mit i > 0 nicht verfügbar. gl_FragColor und gl_FragData dü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.