Signatur
Beschreibung
compressedTexSubImage2D() ersetzt einen Ausschnitt eines 2D-Texturbildes oder einer Cube-Map-Fläche durch neue, bereits komprimierte Bilddaten. Die Textur muss vorher mit compressedTexImage2D() (oder in WebGL 2 mit texStorage2D()) im gleichen komprimierten Format angelegt worden sein. Speicher wird dabei nicht neu belegt, es werden nur die Daten ausgetauscht.
Typische Einsatzfälle sind Streaming von Textur-Kacheln (Terrain, Karten, Megatexturen), das schrittweise Nachladen von Mip-Stufen oder das Aktualisieren von Atlanten, ohne die komplette Textur neu hochzuladen. Komprimierte Formate sparen GPU-Speicher und Bandbreite, weil die GPU die Blöcke direkt dekodiert.
Komprimierte Formate stehen nur über Erweiterungen zur Verfügung, z. B. WEBGL_compressed_texture_s3tc (Desktop), WEBGL_compressed_texture_etc, WEBGL_compressed_texture_astc oder EXT_texture_compression_bptc. Die Formatkonstante (etwa ext.COMPRESSED_RGBA_S3TC_DXT5_EXT) stammt aus dem jeweiligen Erweiterungsobjekt, das man mit gl.getExtension() erhält.
Da blockbasierte Formate in Blöcken (meist 4×4 Pixel) organisiert sind, müssen xoffset, yoffset, width und height in der Regel Vielfache der Blockgröße sein; die Byte-Länge von pixels muss exakt zur Größe des Bereichs passen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | GLenum | Texturziel: 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; 0 ist das Basisbild. |
|
| $xoffset Pflicht | GLint | Horizontaler Versatz (in Texeln) des Teilbereichs; meist Vielfaches der Blockbreite (z. B. 4). | |
| $yoffset Pflicht | GLint | Vertikaler Versatz (in Texeln) des Teilbereichs; meist Vielfaches der Blockhöhe. | |
| $width Pflicht | GLsizei | Breite des Teilbereichs in Texeln. | |
| $height Pflicht | GLsizei | Höhe des Teilbereichs in Texeln. | |
| $format Pflicht | GLenum | Komprimiertes Format aus einer Erweiterung, z. B. ext.COMPRESSED_RGBA_S3TC_DXT5_EXT. Muss dem Format der bestehenden Textur entsprechen. |
|
| $pixels Pflicht | ArrayBufferView | Komprimierte Blockdaten (meist Uint8Array). In WebGL 2 alternativ ein Offset (GLintptr) in einen gebundenen PIXEL_UNPACK_BUFFER plus imageSize, bzw. zusätzliche Parameter srcOffset/srcLengthOverride. |
Rückgabewert
undefined). Fehler werden über gl.getError() gemeldet.Beispiele
Textur anlegen und eine 4×4-Kachel ersetzen (S3TC/DXT1)
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('WEBGL_compressed_texture_s3tc');
if (!ext) {
console.log('S3TC wird nicht unterstützt');
} else {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// DXT1: 8 Byte pro 4×4-Block -> 16×16 Textur = 16 Blöcke = 128 Byte
const full = new Uint8Array(128);
gl.compressedTexImage2D(gl.TEXTURE_2D, 0,
ext.COMPRESSED_RGB_S3TC_DXT1_EXT, 16, 16, 0, full);
// Einen Block (4×4 Texel) an Position (4, 8) ersetzen
const block = new Uint8Array([0xff, 0xff, 0x00, 0x00, 0, 0, 0, 0]);
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 4, 8, 4, 4,
ext.COMPRESSED_RGB_S3TC_DXT1_EXT, block);
console.log(gl.getError() === gl.NO_ERROR ? 'OK' : 'Fehler');
}
Kachel-Streaming: komprimierte Kachel nachladen
// Lädt eine vorkomprimierte DXT5-Kachel (z. B. 64×64) und schreibt sie in einen Atlas
const updateTile = async (gl, ext, atlasTex, url, tileX, tileY, size = 64) => {
const response = await fetch(url);
const data = new Uint8Array(await response.arrayBuffer());
// DXT5: 16 Byte pro 4×4-Block
const expected = (size / 4) * (size / 4) * 16;
if (data.byteLength !== expected) {
throw new Error(`Falsche Datenlänge: ${data.byteLength} statt ${expected}`);
}
gl.bindTexture(gl.TEXTURE_2D, atlasTex);
gl.compressedTexSubImage2D(
gl.TEXTURE_2D, 0,
tileX * size, tileY * size, size, size,
ext.COMPRESSED_RGBA_S3TC_DXT5_EXT,
data
);
};
// Aufruf: await updateTile(gl, ext, atlas, '/tiles/3_2.dxt5', 3, 2);
WebGL 2: Upload aus einem Pixel-Unpack-Buffer
const gl = document.createElement('canvas').getContext('webgl2');
const ext = gl.getExtension('WEBGL_compressed_texture_etc');
if (ext) {
const fmt = ext.COMPRESSED_RGBA8_ETC2_EAC; // 16 Byte pro Block
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texStorage2D(gl.TEXTURE_2D, 1, fmt, 32, 32);
const pbo = gl.createBuffer();
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo);
const bytes = (8 / 4) * (8 / 4) * 16; // 8×8-Bereich = 64 Byte
gl.bufferData(gl.PIXEL_UNPACK_BUFFER, new Uint8Array(bytes), gl.STATIC_DRAW);
// Signatur mit imageSize und Offset in den gebundenen Buffer
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 8, 8, 8, 8, fmt, bytes, 0);
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null);
console.log(gl.getError());
}