Signatur
Beschreibung
Die Methode multiDrawArraysWEBGL() gehört zur WebGL-Erweiterung WEBGL_multi_draw und entspricht funktional einer Schleife über gl.drawArrays(mode, firsts[i], counts[i]). Statt vieler einzelner Zeichenaufrufe übergibt man zwei Listen mit Startindizes und Vertex-Anzahlen; der Browser reicht diese gebündelt an den Treiber weiter. Das spart JavaScript-Overhead und Validierungskosten pro Aufruf.
Typische Einsatzgebiete sind Szenen mit vielen kleinen Objekten, die sich einen gemeinsamen Vertex-Buffer, dasselbe Shader-Programm und denselben Render-Zustand teilen – etwa Partikel-Cluster, Kacheln einer Karte, Text-Glyphen oder Linienzüge unterschiedlicher Länge.
Im Vertex-Shader lässt sich über die eingebaute Variable gl_DrawID ermitteln, welcher Teil-Zeichenvorgang gerade ausgeführt wird. Dafür muss der Shader #extension GL_ANGLE_multi_draw : require deklarieren. So können etwa pro Teilobjekt unterschiedliche Transformationen oder Farben aus einem Uniform-Array oder einer Textur gelesen werden.
Die Erweiterung erhält man mit gl.getExtension("WEBGL_multi_draw"); sie steht sowohl für WebGL 1 als auch für WebGL 2 zur Verfügung.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $mode Pflicht | GLenum | Primitivtyp, z. B. gl.POINTS, gl.LINES, gl.LINE_STRIP, gl.LINE_LOOP, gl.TRIANGLES, gl.TRIANGLE_STRIP oder gl.TRIANGLE_FAN. |
|
| $firstsList Pflicht | Int32Array | Array<number> | Liste der Startindizes im Vertex-Array – ein Eintrag pro Teil-Zeichenvorgang. | |
| $firstsOffset Pflicht | number | Index in firstsList, ab dem gelesen wird (Elemente, nicht Bytes). |
|
| $countsList Pflicht | Int32Array | Array<number> | Liste der Vertex-Anzahlen pro Teil-Zeichenvorgang. | |
| $countsOffset Pflicht | number | Index in countsList, ab dem gelesen wird. |
|
| $drawcount Pflicht | number | Anzahl der auszuführenden Teil-Zeichenvorgänge. Beide Listen müssen ab ihrem Offset mindestens so viele Einträge enthalten. |
Rückgabewert
undefined). Fehler werden über gl.getError() gemeldet.Beispiele
Drei Dreiecke aus einem gemeinsamen Buffer zeichnen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
const ext = gl.getExtension('WEBGL_multi_draw');
if (!ext) throw new Error('WEBGL_multi_draw wird nicht unterstützt');
const vs = `#version 300 es
in vec2 aPos;
void main() { gl_Position = vec4(aPos, 0.0, 1.0); }`;
const fs = `#version 300 es
precision mediump float;
out vec4 color;
void main() { color = vec4(0.2, 0.6, 1.0, 1.0); }`;
const compile = (type, src) => {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
return s;
};
const prog = gl.createProgram();
gl.attachShader(prog, compile(gl.VERTEX_SHADER, vs));
gl.attachShader(prog, compile(gl.FRAGMENT_SHADER, fs));
gl.linkProgram(prog);
gl.useProgram(prog);
// 3 Dreiecke à 3 Vertices hintereinander im Buffer
const verts = new Float32Array([
-0.9, -0.5, -0.6, -0.5, -0.75, 0.0,
-0.15, -0.5, 0.15, -0.5, 0.0, 0.0,
0.6, -0.5, 0.9, -0.5, 0.75, 0.0
]);
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, verts, gl.STATIC_DRAW);
const loc = gl.getAttribLocation(prog, 'aPos');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
const firsts = new Int32Array([0, 3, 6]);
const counts = new Int32Array([3, 3, 3]);
gl.clear(gl.COLOR_BUFFER_BIT);
ext.multiDrawArraysWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, firsts.length);
console.log(gl.getError() === gl.NO_ERROR);
Pro Teilobjekt eine eigene Farbe über gl_DrawID
// Vertex-Shader mit gl_DrawID (WebGL 2)
const vs = `#version 300 es
#extension GL_ANGLE_multi_draw : require
in vec2 aPos;
flat out int vId;
void main() {
vId = gl_DrawID;
gl_Position = vec4(aPos, 0.0, 1.0);
}`;
const fs = `#version 300 es
precision mediump float;
flat in int vId;
uniform vec3 uColors[3];
out vec4 color;
void main() { color = vec4(uColors[vId], 1.0); }`;
// ... Programm wie oben erstellen und Buffer binden ...
gl.uniform3fv(gl.getUniformLocation(prog, 'uColors'), [
1, 0, 0,
0, 1, 0,
0, 0, 1
]);
ext.multiDrawArraysWEBGL(gl.TRIANGLES, [0, 3, 6], 0, [3, 3, 3], 0, 3);
Linienzüge unterschiedlicher Länge mit Fallback
const drawStrips = (gl, ext, strips) => {
// strips: [{ first, count }, ...]
const firsts = Int32Array.from(strips, (s) => s.first);
const counts = Int32Array.from(strips, (s) => s.count);
if (ext) {
ext.multiDrawArraysWEBGL(gl.LINE_STRIP, firsts, 0, counts, 0, strips.length);
} else {
// Fallback ohne Erweiterung
for (let i = 0; i < strips.length; i++) {
gl.drawArrays(gl.LINE_STRIP, firsts[i], counts[i]);
}
}
};
const ext = gl.getExtension('WEBGL_multi_draw');
drawStrips(gl, ext, [
{ first: 0, count: 5 },
{ first: 5, count: 12 },
{ first: 17, count: 3 }
]);
// Wichtig · Fallstricke
- Verfügbarkeit prüfen:
gl.getExtension('WEBGL_multi_draw')kannnullzurückgeben, etwa bei manchen Safari-Versionen, älteren Treibern oder Software-Rendering. Halte immer einen Fallback mit einerdrawArrays()-Schleife bereit. - Offsets zählen Elemente:
firstsOffsetundcountsOffsetsind Indizes in die Listen, keine Byte-Offsets. Reichen die Listen ab dem Offset nicht fürdrawcountEinträge, wirdINVALID_OPERATIONausgelöst; negative Werte führen zuINVALID_VALUE. - Gemeinsamer Zustand: Alle Teil-Zeichenvorgänge teilen sich Programm, Uniforms, Texturen und Blend-Zustand. Unterschiede pro Objekt lassen sich nur über
gl_DrawIDund Daten in Arrays bzw. Texturen abbilden. - gl_DrawID ist nur verfügbar, wenn der Shader
#extension GL_ANGLE_multi_draw : requiredeklariert; in GLSL ES 1.0 steht die Direktive ganz oben, in GLSL ES 3.0 direkt nach#version 300 es. - Leistung: Der Gewinn entsteht vor allem durch weniger JavaScript- und Validierungsaufwand. Nicht jeder Treiber setzt echtes Multi-Draw auf der GPU um. Bei vielen gleichartigen Objekten ist Instanced Rendering (
drawArraysInstanced) oft die bessere Wahl. - Typisierte Arrays (
Int32Array) wiederzuverwenden vermeidet Speicherbereinigungs-Druck in der Render-Schleife.