Signatur
Beschreibung
Die Erweiterung WEBGL_compressed_texture_s3tc gehört zur WebGL-API und macht die S3TC-Kompression verfügbar, die auch als DXTn bzw. BC1–BC3 bekannt ist. Mit ihr lädt man vorab komprimierte Texturdaten (meist aus .dds- oder .ktx-Dateien) direkt auf die GPU. Die Textur bleibt auch im Grafikspeicher komprimiert. Sie braucht dadurch 4- bis 8-mal weniger VRAM als eine unkomprimierte RGBA-Textur, lädt schneller und belastet beim Sampling die Speicherbandbreite weniger.
Aktiviert wird die Erweiterung mit gl.getExtension("WEBGL_compressed_texture_s3tc"). Man bekommt entweder ein Objekt mit vier Formatkonstanten zurück oder null, wenn der Browser bzw. die GPU sie nicht unterstützt. Die Konstanten übergibt man als internalformat an gl.compressedTexImage2D() und gl.compressedTexSubImage2D(), in WebGL 2 außerdem an gl.texStorage2D(). Welche Formate gerade aktiv sind, liefert zusätzlich gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS).
COMPRESSED_RGB_S3TC_DXT1_EXT(0x83F0): 8 Byte pro 4×4-Block, ohne Alphakanal. Gut für deckende Farbtexturen.COMPRESSED_RGBA_S3TC_DXT1_EXT(0x83F1): 8 Byte pro Block mit 1-Bit-Alpha. Ein Pixel ist also entweder voll deckend oder voll transparent.COMPRESSED_RGBA_S3TC_DXT3_EXT(0x83F2): 16 Byte pro Block mit explizitem 4-Bit-Alpha. Geeignet für scharfe Alphaübergänge.COMPRESSED_RGBA_S3TC_DXT5_EXT(0x83F3): 16 Byte pro Block mit interpoliertem Alpha. Geeignet für weiche Verläufe wie Rauch oder Laub.
S3TC ist vor allem auf Desktop-GPUs verbreitet. Für plattformübergreifende Anwendungen liefert man die Texturen deshalb meist in mehreren Formaten aus, z. B. S3TC, ETC2 und ASTC, oder man nutzt Basis Universal/KTX2 und transkodiert zur Laufzeit in das Format, das die jeweilige GPU unterstützt.
Rückgabewert
gl.getExtension() liefert ein Objekt mit den Konstanten COMPRESSED_RGB_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT3_EXT und COMPRESSED_RGBA_S3TC_DXT5_EXT – oder null, wenn die Erweiterung nicht unterstützt wird.Beispiele
Erweiterung aktivieren und Formate prüfen
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl2') ?? canvas.getContext('webgl');
const s3tc = gl.getExtension('WEBGL_compressed_texture_s3tc');
if (!s3tc) {
console.log('S3TC nicht verfügbar – Fallback auf ETC2/ASTC oder PNG');
} else {
console.log(s3tc.COMPRESSED_RGBA_S3TC_DXT5_EXT.toString(16));
// Aktive komprimierte Formate abfragen
const formats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
console.log(formats.includes(s3tc.COMPRESSED_RGBA_S3TC_DXT5_EXT));
}
DXT5-Textur aus einer DDS-Datei hochladen (inkl. Mipmaps)
const loadDDS = async (gl, url) => {
const ext = gl.getExtension('WEBGL_compressed_texture_s3tc');
if (!ext) throw new Error('S3TC nicht unterstützt');
const buffer = await (await fetch(url)).arrayBuffer();
const header = new Int32Array(buffer, 0, 31);
if (header[0] !== 0x20534444) throw new Error('Keine DDS-Datei'); // 'DDS '
const height = header[3];
const width = header[4];
const mipCount = Math.max(1, header[7]);
const fourCC = header[21];
const FOURCC = { DXT1: 0x31545844, DXT3: 0x33545844, DXT5: 0x35545844 };
const [format, blockBytes] =
fourCC === FOURCC.DXT1 ? [ext.COMPRESSED_RGBA_S3TC_DXT1_EXT, 8] :
fourCC === FOURCC.DXT3 ? [ext.COMPRESSED_RGBA_S3TC_DXT3_EXT, 16] :
fourCC === FOURCC.DXT5 ? [ext.COMPRESSED_RGBA_S3TC_DXT5_EXT, 16] :
(() => { throw new Error('Unbekanntes FourCC'); })();
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
let offset = 128; // DDS-Header: 4 Byte Magic + 124 Byte Header
let w = width, h = height;
for (let level = 0; level < mipCount; level++) {
const size = Math.max(1, Math.ceil(w / 4)) * Math.max(1, Math.ceil(h / 4)) * blockBytes;
const data = new Uint8Array(buffer, offset, size);
gl.compressedTexImage2D(gl.TEXTURE_2D, level, format, w, h, 0, data);
offset += size;
w = Math.max(1, w >> 1);
h = Math.max(1, h >> 1);
}
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER,
mipCount > 1 ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
return tex;
};
const gl = document.querySelector('canvas').getContext('webgl');
const texture = await loadDDS(gl, '/textures/crate_dxt5.dds');
console.log(gl.getError() === gl.NO_ERROR);
WebGL 2: Speicher mit texStorage2D reservieren und Teilbereich aktualisieren
const gl = document.querySelector('canvas').getContext('webgl2');
const ext = gl.getExtension('WEBGL_compressed_texture_s3tc');
if (ext) {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// Unveränderlicher Speicher: 256×256, 1 Mip-Level, DXT1 ohne Alpha
gl.texStorage2D(gl.TEXTURE_2D, 1, ext.COMPRESSED_RGB_S3TC_DXT1_EXT, 256, 256);
// 64×64-Kachel = 16×16 Blöcke à 8 Byte = 2048 Byte
const tile = new Uint8Array(16 * 16 * 8); // hier: schwarze Blöcke
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 64, 64, 64, 64,
ext.COMPRESSED_RGB_S3TC_DXT1_EXT, tile);
console.log(gl.getError() === gl.NO_ERROR);
}