Signatur
Beschreibung
copyTexSubImage2D() liest Pixel aus dem aktuell gebundenen Lese-Framebuffer (standardmäßig der Zeichenpuffer des Canvas, sonst ein per bindFramebuffer() gebundenes WebGLFramebuffer) und schreibt sie in einen Ausschnitt der Textur, die gerade an target gebunden ist. Die Kopie läuft komplett auf der GPU, die Daten gehen also nicht über den Umweg zur CPU wie bei readPixels().
Anders als copyTexImage2D() legt die Methode keinen neuen Texturspeicher an. Die Textur muss vorher mit texImage2D() (oder copyTexImage2D()) in ausreichender Größe angelegt worden sein. Deshalb eignet sich die Methode gut, um Texturen immer wieder teilweise zu aktualisieren, ohne sie neu anzulegen. Typische Anwendungen sind Feedback-Effekte (Bewegungsunschärfe, Nachzieheffekte), Snapshots der Szene für Nachbearbeitung, Spiegelungen oder das Füllen eines Textur-Atlas.
Das Quellrechteck wird durch x, y, width und height im Framebuffer beschrieben (Ursprung unten links), das Ziel durch xoffset/yoffset in der Textur. Liegen Teile des Quellrechtecks außerhalb des Framebuffers, sind die entsprechenden Texel in WebGL mit 0 gefüllt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | GLenum | Textur-Ziel: gl.TEXTURE_2D oder eine Cube-Map-Fläche wie gl.TEXTURE_CUBE_MAP_POSITIVE_X … gl.TEXTURE_CUBE_MAP_NEGATIVE_Z. |
|
| $level Pflicht | GLint | Mipmap-Stufe, in die geschrieben wird. 0 ist das Basisbild. |
|
| $xoffset Pflicht | GLint | Horizontaler Versatz (in Texeln) innerhalb der Zieltextur. | |
| $yoffset Pflicht | GLint | Vertikaler Versatz (in Texeln) innerhalb der Zieltextur. | |
| $x Pflicht | GLint | X-Koordinate der unteren linken Ecke des Quellrechtecks im Framebuffer. | |
| $y Pflicht | GLint | Y-Koordinate der unteren linken Ecke des Quellrechtecks im Framebuffer (Ursprung unten). | |
| $width Pflicht | GLsizei | Breite des zu kopierenden Bereichs in Pixeln. | |
| $height Pflicht | GLsizei | Höhe des zu kopierenden Bereichs in Pixeln. |
Rückgabewert
gl.getError() gemeldet (z. B. INVALID_VALUE, INVALID_OPERATION, INVALID_FRAMEBUFFER_OPERATION).Beispiele
Canvas-Inhalt in eine vorhandene Textur kopieren
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
// Textur einmalig in Canvas-Größe anlegen (ohne Daten)
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// Etwas zeichnen – hier nur eine Füllfarbe
gl.clearColor(0.2, 0.4, 0.8, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Gesamten Zeichenpuffer in die Textur kopieren
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, canvas.width, canvas.height);
console.log(gl.getError() === gl.NO_ERROR);
Ausschnitt aus einem Framebuffer in einen Textur-Atlas schreiben
const gl = document.createElement('canvas').getContext('webgl');
// Quelle: Offscreen-Framebuffer mit 128×128-Farbtextur
const srcTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, srcTex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 128, 128, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, srcTex, 0);
gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// Ziel: 256×256-Atlas
const atlas = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, atlas);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 256, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
// 64×64-Bereich ab (32,32) im Framebuffer nach (128,0) im Atlas kopieren
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 128, 0, 32, 32, 64, 64);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
console.log(gl.getError());
Feedback-Effekt: letztes Bild pro Frame sichern
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl', { preserveDrawingBuffer: false });
const history = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, history);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, canvas.width, canvas.height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
const drawScene = (t) => {
gl.clearColor(Math.sin(t / 1000) * 0.5 + 0.5, 0.2, 0.3, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// … hier würde die Szene die Textur 'history' überblenden …
};
const frame = (t) => {
drawScene(t);
// Ergebnis für den nächsten Frame sichern – noch im selben Frame!
gl.bindTexture(gl.TEXTURE_2D, history);
gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, canvas.width, canvas.height);
requestAnimationFrame(frame);
};
requestAnimationFrame(frame);