Signatur
Beschreibung
readPixels() kopiert Pixeldaten von der GPU in den Arbeitsspeicher des Browsers. Gelesen wird aus dem aktuell gebundenen Framebuffer: entweder dem Standard-Framebuffer (der Canvas) oder einem mit bindFramebuffer() gebundenen WebGLFramebuffer. Das Ergebnis landet in dem übergebenen TypedArray, das groß genug sein muss.
Typische Anwendungsfälle sind Color-Picking (Objekt-IDs werden als Farben in einen Offscreen-Buffer gerendert und unter dem Mauszeiger ausgelesen), Screenshots, GPU-Berechnungen (GPGPU) und automatisierte Tests, die Render-Ergebnisse vergleichen.
Wichtig: Der Koordinatenursprung (0, 0) liegt in WebGL unten links, nicht oben links wie im DOM. Mauskoordinaten müssen daher vertikal umgerechnet werden. Außerdem ist die Zeilenreihenfolge der ausgelesenen Daten von unten nach oben.
In WebGL 1 ist die Kombination gl.RGBA + gl.UNSIGNED_BYTE immer erlaubt. Eine zweite, implementierungsabhängige Kombination lässt sich über gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT) und gl.IMPLEMENTATION_COLOR_READ_TYPE abfragen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $x Pflicht | number | — | Horizontale Koordinate der linken unteren Ecke des Rechtecks (in Pixeln, GLint). |
| $y Pflicht | number | — | Vertikale Koordinate der linken unteren Ecke – gemessen von unten. |
| $width Pflicht | number | — | Breite des Rechtecks in Pixeln. |
| $height Pflicht | number | — | Höhe des Rechtecks in Pixeln. |
| $format Pflicht | GLenum | — | Format der Pixeldaten, z. B. gl.RGBA, gl.RGB oder gl.ALPHA. Garantiert unterstützt ist nur gl.RGBA. |
| $type Pflicht | GLenum | — | Datentyp pro Komponente: gl.UNSIGNED_BYTE (→ Uint8Array), gl.UNSIGNED_SHORT_5_6_5, gl.UNSIGNED_SHORT_4_4_4_4, gl.UNSIGNED_SHORT_5_5_5_1 (→ Uint16Array) oder gl.FLOAT (→ Float32Array, nur mit Erweiterung). |
| $pixels Pflicht | ArrayBufferView | null | — | Zielpuffer, dessen Typ zu type passen muss. Größe mindestens width * height * Komponenten (unter Beachtung von PACK_ALIGNMENT). Bei null wird ein INVALID_VALUE-Fehler erzeugt. |
Rückgabewert
undefined). Die Daten werden direkt in pixels geschrieben; Fehler werden über gl.getError() gemeldet.Beispiele
Farbe eines einzelnen Pixels lesen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
// Canvas rot einfärben
gl.clearColor(1, 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(pixel);
Color-Picking: Pixel unter dem Mauszeiger auslesen
canvas.addEventListener('click', (event) => {
const rect = canvas.getBoundingClientRect();
// CSS-Pixel in Framebuffer-Pixel umrechnen
const x = Math.floor((event.clientX - rect.left) * canvas.width / rect.width);
const yTop = Math.floor((event.clientY - rect.top) * canvas.height / rect.height);
const y = canvas.height - yTop - 1; // WebGL-Ursprung liegt unten links
gl.bindFramebuffer(gl.FRAMEBUFFER, pickingFramebuffer);
renderPickingScene(); // zeichnet Objekt-IDs als Farben
const data = new Uint8Array(4);
gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, data);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
const id = data[0] | (data[1] << 8) | (data[2] << 16);
console.log('Getroffenes Objekt:', id);
});
Kompletten Framebuffer als Bild exportieren (Zeilen spiegeln)
const exportCanvas = (gl) => {
const { drawingBufferWidth: w, drawingBufferHeight: h } = gl;
const pixels = new Uint8Array(w * h * 4);
gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
// WebGL liefert Zeilen von unten nach oben – für ImageData umdrehen
const flipped = new Uint8ClampedArray(w * h * 4);
const rowBytes = w * 4;
for (let row = 0; row < h; row++) {
const src = (h - row - 1) * rowBytes;
flipped.set(pixels.subarray(src, src + rowBytes), row * rowBytes);
}
const out = document.createElement('canvas');
out.width = w;
out.height = h;
out.getContext('2d').putImageData(new ImageData(flipped, w, h), 0, 0);
return out.toDataURL('image/png');
};
console.log(exportCanvas(gl).slice(0, 22));
// Wichtig · Fallstricke
- Leerer Canvas: Ohne
preserveDrawingBuffer: truebeimgetContext()wird der Zeichenpuffer nach dem Compositing gelöscht.readPixels()muss daher im selben Frame (z. B. im selbenrequestAnimationFrame-Callback) wie das Rendern aufgerufen werden, sonst erhält man nur Nullen. - Performance: Der Aufruf ist synchron und erzwingt, dass die GPU alle ausstehenden Befehle abarbeitet (Pipeline-Stall). Nicht in jedem Frame für große Bereiche verwenden. In WebGL 2 lässt sich mit einem
PIXEL_PACK_BUFFERundfenceSync()asynchron lesen. - Typ-Konsistenz: Der Array-Typ muss zu
typepassen (UNSIGNED_BYTE→Uint8Array), sonst entstehtINVALID_OPERATION. EinUint8ClampedArrayist in WebGL 1 nicht erlaubt. - Ausrichtung:
gl.PACK_ALIGNMENT(Standard 4) beeinflusst die Zeilenlänge im Puffer – beigl.RGBund ungeraden Breiten ggf.gl.pixelStorei(gl.PACK_ALIGNMENT, 1)setzen. - Außerhalb liegende Pixel (außerhalb des Framebuffers) werden nicht beschrieben bzw. bleiben 0.
- Sicherheit: Wurden Cross-Origin-Bilder ohne CORS-Freigabe als Textur verwendet, ist der Canvas „tainted“; WebGL verbietet solche Texturen bereits bei
texImage2D()mit einemSecurityError. Mitantialiasund High-DPI-Displays aufdrawingBufferWidth/drawingBufferHeightstatt CSS-Größen achten. - WebGL 2 (
WebGL2RenderingContext) bietet zusätzliche Überladungen mit Offset bzw. Pufferziel.