Start · Sprachen · JavaScript · Referenz · WebGL2RenderingContext.readBuffer()

WebGL2RenderingContext.readBuffer()

Methode

Legt fest, aus welchem Farb-Puffer <code>readPixels()</code>, <code>copyTexImage2D()</code>, <code>copyTexSubImage2D()</code> und <code>copyTexSubImage3D()</code> die Pixel lesen.

seit JavaScript WebGL 2 – Baseline: alle modernen Browser (seit 20 Kategorie: webapi

Signatur

WebGL2RenderingContext.prototype.readBuffer(source)

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 (null gebunden): nur gl.BACK oder gl.NONE.
  • Eigener Framebuffer: gl.COLOR_ATTACHMENT0 bis gl.COLOR_ATTACHMENT{n} (Obergrenze über gl.MAX_COLOR_ATTACHMENTS) oder gl.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

Typ
void
Beschreibung
Kein 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));
Objekt-ID: 42

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

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

// Wichtig · Fallstricke

  • Fehlercodes: INVALID_ENUM bei einem unbekannten Wert. INVALID_OPERATION, wenn beim Standard-Framebuffer etwas anderes als BACK/NONE übergeben wird oder wenn bei einem eigenen Framebuffer COLOR_ATTACHMENTi nicht zum gebundenen Framebuffer passt oder BACK verwendet 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.NONE oder ist der gewählte Anhang leer, lösen readPixels() und copyTex*() den Fehler INVALID_OPERATION aus.
  • Bei Anhängen mit Integer-Format (z. B. RGBA32UI) muss readPixels() mit gl.RGBA_INTEGER und einem passenden Typ wie gl.UNSIGNED_INT aufgerufen werden.
  • readPixels() blockiert, bis die GPU fertig ist, und kann deshalb ruckeln. Für Lesezugriffe in jedem Frame lohnen sich PIXEL_PACK_BUFFER und fenceSync().
  • 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.