Signatur
Beschreibung
texStorage2D() legt in einem einzigen Aufruf den Speicher für alle Mipmap-Ebenen der aktuell gebundenen Textur an. Das Ergebnis ist eine unveränderliche Textur (immutable texture): Format, Größe und Anzahl der Ebenen stehen danach fest. Die Pixeldaten selbst werden anschließend mit texSubImage2D() oder compressedTexSubImage2D() hochgeladen oder per Framebuffer gerendert.
Im Vergleich zu mehrfachen texImage2D()-Aufrufen hat das zwei Vorteile: Der Treiber muss die Vollständigkeit der Textur nicht bei jedem Zeichenaufruf prüfen, und Fehler durch inkonsistente Ebenengrößen oder Formate sind ausgeschlossen. Daher ist texStorage2D() in WebGL 2 der empfohlene Weg, um Texturen – insbesondere Render-Targets und Mipmap-Texturen – anzulegen.
Als internalformat muss ein sized Format wie gl.RGBA8, gl.RGB565, gl.R32F, gl.DEPTH_COMPONENT24 oder ein komprimiertes Format angegeben werden. Unsized Formate wie gl.RGBA sind nicht erlaubt. Bei gl.TEXTURE_CUBE_MAP wird Speicher für alle sechs Seiten gleichzeitig reserviert; width und height müssen dann gleich sein.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | GLenum | Bindungsziel: gl.TEXTURE_2D oder gl.TEXTURE_CUBE_MAP. |
|
| $levels Pflicht | GLsizei | Anzahl der Mipmap-Ebenen (mindestens 1). Maximal Math.floor(Math.log2(Math.max(width, height))) + 1. |
|
| $internalformat Pflicht | GLenum | Sized internes Format, z. B. gl.RGBA8, gl.SRGB8_ALPHA8, gl.RGBA16F, gl.R8, gl.DEPTH_COMPONENT24, gl.DEPTH24_STENCIL8 oder ein komprimiertes Format aus einer Erweiterung. |
|
| $width Pflicht | GLsizei | Breite der Basisebene (Ebene 0) in Texeln. | |
| $height Pflicht | GLsizei | Höhe der Basisebene (Ebene 0) in Texeln. |
Rückgabewert
gl.getError() gemeldet.Beispiele
Textur mit Mipmaps anlegen und Bild hochladen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
const loadTexture = async (url) => {
const img = new Image();
img.src = url;
await img.decode();
const levels = Math.floor(Math.log2(Math.max(img.width, img.height))) + 1;
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// Unveränderlichen Speicher für alle Ebenen reservieren
gl.texStorage2D(gl.TEXTURE_2D, levels, gl.RGBA8, img.width, img.height);
// Ebene 0 befüllen, restliche Ebenen generieren
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
console.log('Ebenen:', gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_IMMUTABLE_LEVELS));
return tex;
};
loadTexture('holz-512.png');
Render-Target für Framebuffer (Farbe + Tiefe)
const gl = document.querySelector('canvas').getContext('webgl2');
const [w, h] = [1024, 768];
const color = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, color);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, w, h);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
const depth = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, depth);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.DEPTH_COMPONENT24, w, h);
const fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depth, 0);
const ok = gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE;
console.log('Framebuffer vollständig:', ok);
Cubemap mit Float-Daten und Fehlerprüfung
const gl = document.querySelector('canvas').getContext('webgl2');
const size = 64;
const cube = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, cube);
// Speicher für alle 6 Seiten auf einmal
gl.texStorage2D(gl.TEXTURE_CUBE_MAP, 1, gl.RGBA16F, size, size);
const data = new Float32Array(size * size * 4).fill(0.5);
for (let face = 0; face < 6; face++) {
gl.texSubImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, 0, 0,
size, size, gl.RGBA, gl.FLOAT, data);
}
console.log('Fehler:', gl.getError() === gl.NO_ERROR ? 'keiner' : gl.getError());
// Wichtig · Fallstricke
- Unveränderlich: Ein zweiter Aufruf von
texStorage2D()oder eintexImage2D()auf derselben Textur erzeugtINVALID_OPERATION. Zum Ändern der Größe (z. B. beim Canvas-Resize) muss die Textur gelöscht und neu angelegt werden. - Nur sized Formate:
gl.RGBAalsinternalformatführt zuINVALID_ENUM. Das Format beitexSubImage2D()(format/type) muss zum internen Format passen, z. B.RGBA8↔RGBA/UNSIGNED_BYTE. - Zu viele Ebenen (
levelsgrößer als log2(max)+1) oderlevels/Größe < 1 führen zuINVALID_OPERATIONbzw.INVALID_VALUE. - Float-Formate wie
RGBA16F/RGBA32Fsind als Render-Target nur mit der ErweiterungEXT_color_buffer_floatnutzbar; lineares Filtern vonRGBA32FerfordertOES_texture_float_linear. - Ist die Textur nicht vollständig mit Mipmaps befüllt,
TEXTURE_MIN_FILTERaufLINEARoderNEARESTsetzen odergenerateMipmap()aufrufen – sonst wird schwarz gesampelt. - Nur im WebGL-2-Kontext verfügbar. In WebGL 1 gibt es die Methode nicht (dort ggf. über Erweiterungen nicht standardisiert); Verfügbarkeit per
canvas.getContext('webgl2')prüfen.