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

WebGL2RenderingContext.copyTexSubImage3D()

Methode

Kopiert einen rechteckigen Pixelbereich aus dem aktuell gebundenen Lese-Framebuffer in eine einzelne Schicht (Slice) einer 3D- oder 2D-Array-Textur.

seit JavaScript WebGL 2.0 – Baseline: alle modernen Browser Kategorie: webapi

Signatur

WebGL2RenderingContext.prototype.copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height)

Beschreibung

copyTexSubImage3D() liest einen rechteckigen Bereich aus dem aktuellen Lese-Framebuffer (gl.READ_FRAMEBUFFER bzw. dem Standard-Zeichenpuffer, wenn keiner gebunden ist). Die Pixel landen in einer bereits angelegten 3D-Textur (gl.TEXTURE_3D) oder 2D-Array-Textur (gl.TEXTURE_2D_ARRAY). Welche Schicht (Tiefe bzw. Array-Index) beschrieben wird, legt zoffset fest. Pro Aufruf wird immer genau eine Schicht beschrieben.

Die Methode eignet sich gut, um Render-Ergebnisse ohne Umweg über die CPU in Texturen abzulegen. Typische Einsätze sind Schatten-Kaskaden, Zeitverläufe (Frame-Historie) in einem Textur-Array oder Volumendaten, die man Schicht für Schicht per Rendering aufbaut. Die Daten bleiben dabei vollständig auf der GPU. Das ist deutlich schneller als readPixels() mit anschließendem texSubImage3D().

Der Speicher der Zieltextur muss vorher angelegt sein, etwa mit texStorage3D() oder texImage3D(). Anders als bei copyTexImage2D() gibt es keine Variante copyTexImage3D(), die Speicher reserviert. Das interne Format der Textur muss zum Farbformat des Lese-Framebuffers passen.

Parameter

Name Typ Default Beschreibung
$target Pflicht GLenum Textur-Ziel: gl.TEXTURE_3D oder gl.TEXTURE_2D_ARRAY.
$level Pflicht GLint Mipmap-Stufe, in die geschrieben wird (0 = Basisstufe).
$xoffset Pflicht GLint Horizontaler Versatz in der Zieltextur (in Texeln).
$yoffset Pflicht GLint Vertikaler Versatz in der Zieltextur (in Texeln).
$zoffset Pflicht GLint Tiefen- bzw. Schichtindex in der Zieltextur, in den kopiert wird.
$x Pflicht GLint X-Koordinate der unteren linken Ecke des Quellbereichs im Framebuffer.
$y Pflicht GLint Y-Koordinate der unteren linken Ecke des Quellbereichs im Framebuffer (Ursprung unten links).
$width Pflicht GLsizei Breite des zu kopierenden Bereichs in Pixeln.
$height Pflicht GLsizei Höhe des zu kopierenden Bereichs in Pixeln.

Rückgabewert

Typ
void
Beschreibung
Kein Rückgabewert (undefined). Fehler lassen sich mit gl.getError() abfragen.

Beispiele

Canvas-Inhalt in Schicht 0 einer 2D-Array-Textur kopieren

const canvas = document.createElement('canvas');
canvas.width = canvas.height = 256;
const gl = canvas.getContext('webgl2');

// Etwas in den Standard-Framebuffer zeichnen
gl.clearColor(1, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);

// Array-Textur mit 4 Schichten anlegen
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, tex);
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, 256, 256, 4);

// Gesamten Zeichenpuffer in Schicht 0 kopieren
gl.copyTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, 256, 256);

console.log(gl.getError() === gl.NO_ERROR);
true

Frame-Historie: jedes Frame in eine andere Schicht schreiben

const gl = document.querySelector('canvas').getContext('webgl2');
const W = gl.drawingBufferWidth;
const H = gl.drawingBufferHeight;
const HISTORY = 8;

const history = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D_ARRAY, history);
gl.texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, W, H, HISTORY);
gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

let frame = 0;
const loop = (t) => {
  gl.clearColor(Math.sin(t / 500) * 0.5 + 0.5, 0.2, 0.4, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);

  // Aktuelles Bild in die Ringpuffer-Schicht sichern
  gl.bindTexture(gl.TEXTURE_2D_ARRAY, history);
  gl.copyTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, frame % HISTORY, 0, 0, W, H);

  frame++;
  requestAnimationFrame(loop);
};
requestAnimationFrame(loop);

Ausschnitt aus einem Offscreen-Framebuffer in eine 3D-Textur kopieren

const gl = document.createElement('canvas').getContext('webgl2');

// Quelle: Framebuffer mit 128×128-Farbtextur
const srcTex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, srcTex);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 128, 128);
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, srcTex, 0);
gl.clearColor(0, 1, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);

// Ziel: 64×64×16-Volumentextur
const vol = gl.createTexture();
gl.bindTexture(gl.TEXTURE_3D, vol);
gl.texStorage3D(gl.TEXTURE_3D, 1, gl.RGBA8, 64, 64, 16);

// 32×32-Ausschnitt ab (16,16) nach Tiefe 5, Position (8,8)
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
gl.copyTexSubImage3D(gl.TEXTURE_3D, 0, 8, 8, 5, 16, 16, 32, 32);
console.log(gl.getError());

// Wichtig · Fallstricke

  • Speicher muss existieren: Die Zieltextur braucht vorher Speicher über texStorage3D() oder texImage3D(). Sonst entsteht INVALID_OPERATION. Liegt der Zielbereich (xoffset + width, zoffset usw.) außerhalb der Textur, gibt es INVALID_VALUE.
  • Formatverträglichkeit: Das interne Format der Textur muss zum Lese-Framebuffer passen. Ein Beispiel ist Ganzzahl nach Ganzzahl. Tiefen- und Stencil-Formate lassen sich nicht kopieren. Float-Framebuffer brauchen in der Regel die Erweiterung EXT_color_buffer_float.
  • Feedback-Schleife: Ist dieselbe Textur-Schicht zugleich an den Lese-Framebuffer gebunden, ist das Ergebnis undefiniert bzw. führt zu INVALID_OPERATION.
  • Pixel außerhalb des Framebuffers ergeben undefinierte Werte. Die y-Achse beginnt unten links.
  • Beim Standard-Framebuffer mit preserveDrawingBuffer: false sollte die Kopie im selben Frame wie das Zeichnen geschehen.
  • Nur im WebGL2RenderingContext verfügbar, nicht in WebGL 1. Wird von allen aktuellen Browsern unterstützt (Safari ab 15).