Signatur
Beschreibung
Mit generateMipmap() berechnet WebGL aus dem Bild der Basisstufe (Level 0) der gerade an target gebundenen Textur alle kleineren Mipmap-Stufen. Jede Stufe ist halb so breit und halb so hoch wie die vorherige, bis 1×1 Pixel erreicht sind. Die Stufen werden auf der GPU erzeugt. Das geht in der Regel viel schneller, als jede Stufe selbst mit texImage2D() hochzuladen.
Mipmaps braucht man vor allem, wenn Texturen verkleinert, schräg oder weit entfernt dargestellt werden, etwa bei Böden, Geländeoberflächen oder Objekten in der Tiefe. Ohne Mipmaps entstehen Flimmern und Moiré-Muster. Außerdem sinkt mit Mipmaps die Speicherbandbreite, weil der Sampler auf kleinere Stufen zugreift. Mipmaps werden nur verwendet, wenn der Verkleinerungsfilter TEXTURE_MIN_FILTER auf einen *_MIPMAP_*-Modus steht, z. B. gl.LINEAR_MIPMAP_LINEAR (trilineare Filterung).
Rufe die Methode nach dem Hochladen der Basisstufe auf, also nach texImage2D() oder texSubImage2D(). Wenn sich die Basisstufe ändert, musst du sie erneut aufrufen. Das ist z. B. bei Videotexturen der Fall, sofern du dort überhaupt Mipmaps verwendest. In WebGL 1 muss die Textur Seitenlängen haben, die eine Zweierpotenz sind (Power of Two, z. B. 256×512). In WebGL 2 (WebGL2RenderingContext) fällt diese Einschränkung weg.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $target Pflicht | GLenum | Das Texturziel, dessen gebundene Textur Mipmaps erhalten soll. In WebGL 1: gl.TEXTURE_2D oder gl.TEXTURE_CUBE_MAP. In WebGL 2 zusätzlich: gl.TEXTURE_3D oder gl.TEXTURE_2D_ARRAY. |
Rückgabewert
undefined). Fehler werden nicht als Ausnahme geworfen, sondern über gl.getError() gemeldet.Beispiele
Bild laden und Mipmaps erzeugen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const isPowerOf2 = (v) => (v & (v - 1)) === 0;
const loadTexture = (url) => new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => {
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
if (isPowerOf2(img.width) && isPowerOf2(img.height)) {
// Basisstufe ist hochgeladen -> jetzt Mipmaps berechnen
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
} else {
// WebGL 1: Nicht-Zweierpotenz-Texturen ohne Mipmaps und mit CLAMP_TO_EDGE
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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
}
resolve(tex);
};
img.onerror = reject;
img.src = url;
});
const texture = await loadTexture('textures/boden-512.png');
console.log(gl.getError() === gl.NO_ERROR);
Fehler bei Nicht-Zweierpotenz-Texturen in WebGL 1 erkennen
const gl = document.createElement('canvas').getContext('webgl');
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
// 300×200 ist keine Zweierpotenz
const pixels = new Uint8Array(300 * 200 * 4).fill(255);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 300, 200, 0, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
gl.generateMipmap(gl.TEXTURE_2D);
const err = gl.getError();
console.log(err === gl.INVALID_OPERATION ? 'INVALID_OPERATION' : err);
Cube-Map mit Mipmaps (Skybox / Umgebungsspiegelung)
const gl = document.querySelector('canvas').getContext('webgl2');
const size = 256;
const faces = [
gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
gl.TEXTURE_CUBE_MAP_POSITIVE_Z, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
];
const cube = gl.createTexture();
gl.bindTexture(gl.TEXTURE_CUBE_MAP, cube);
faces.forEach((face, i) => {
const data = new Uint8Array(size * size * 4).fill(i * 40);
gl.texImage2D(face, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
});
// Alle sechs Seiten müssen vollständig und quadratisch gleich groß sein
gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
console.log('Mipmap-Stufen:', Math.log2(size) + 1);
// Wichtig · Fallstricke
- Zweierpotenz in WebGL 1: Hat die Basisstufe keine Seitenlängen, die Zweierpotenzen sind, erzeugt der Aufruf den Fehler
INVALID_OPERATION. Eine solche Textur wird außerdem schwarz dargestellt, wenn ein Mipmap-Filter oderREPEATeingestellt ist. In WebGL 2 gilt diese Einschränkung nicht. - Reihenfolge: Rufe die Methode erst nach
texImage2D()auf. Wird sie vor dem Hochladen aufgerufen oder ist keine Textur gebunden, meldet WebGLINVALID_OPERATION. - Cube-Maps: Alle sechs Seiten müssen quadratisch sein und dieselbe Größe, dasselbe Format und denselben Typ haben. Sonst meldet WebGL
INVALID_OPERATION. - Formate: Komprimierte Texturen (z. B. S3TC/ETC) und Tiefentexturen sind nicht erlaubt. In WebGL 2 muss das interne Format außerdem filterbar und als Renderziel nutzbar sein. Bei Formaten wie
RGBA32Fist das ohne Erweiterungen wieOES_texture_float_linearbzw.EXT_color_buffer_floatnicht der Fall. - Ungültiges
target: Führt zuINVALID_ENUM. - Filter nicht vergessen: Der Standardwert von
TEXTURE_MIN_FILTERistNEAREST_MIPMAP_LINEAR. Eine Textur ohne vollständige Mipmap-Kette ist deshalb „unvollständig“ und wird schwarz dargestellt. Entweder du erzeugst Mipmaps, oder du stellst den Filter aufLINEAR/NEAREST. - Leistung: Mipmaps brauchen etwa 33 % zusätzlichen Grafikspeicher. Bei Texturen, die sich in jedem Frame ändern (Video, Canvas), kostet ein wiederholter Aufruf spürbar Leistung.
- Kompatibilität: Wird von allen modernen Browsern mit WebGL-Unterstützung unterstützt. Die Methode gibt es auch in
WebGL2RenderingContext.