Signatur
Beschreibung
multiDrawElementsInstancedWEBGL() gehört zur WebGL-Erweiterung WEBGL_multi_draw und fasst viele Aufrufe von drawElementsInstanced() zu einem einzigen Aufruf zusammen. Für jeden Zeichenvorgang i geben Sie in drei parallelen Arrays die Anzahl der Indizes (counts), den Byte-Offset im gebundenen ELEMENT_ARRAY_BUFFER (offsets) und die Anzahl der Instanzen (instanceCounts) an.
Das lohnt sich vor allem, wenn viele Meshes in einem gemeinsamen Vertex- und Index-Puffer liegen (z. B. Vegetation, Partikelgruppen, Level-Geometrie) und mit demselben Shader und demselben Zustand gezeichnet werden. Weil JavaScript und die Validierung im Browser nur einmal statt n-mal durchlaufen werden, sinkt der CPU-Aufwand pro Frame spürbar.
Im Shader können Sie über die eingebaute Variable gl_DrawID erkennen, welcher Teil-Zeichenvorgang gerade läuft, und so z. B. pro Mesh eine andere Farbe oder Transformation aus einem Uniform-Array lesen. Dafür muss der Shader die Direktive #extension GL_ANGLE_multi_draw : require enthalten. gl_InstanceID (WebGL 2) zählt innerhalb jedes Teil-Zeichenvorgangs wieder ab 0.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $mode Pflicht | GLenum | Primitiv-Typ, z. B. gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.LINES oder gl.POINTS. |
|
| $countsList Pflicht | Int32Array | Array<number> | Anzahl der zu zeichnenden Indizes für jeden Zeichenvorgang. | |
| $countsOffset Pflicht | number | Startposition (in Elementen, nicht Bytes) innerhalb von countsList. |
|
| $type Pflicht | GLenum | Datentyp der Indizes: gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT oder (WebGL 2 bzw. mit OES_element_index_uint) gl.UNSIGNED_INT. |
|
| $offsetsList Pflicht | Int32Array | Array<number> | Byte-Offsets in den gebundenen Index-Puffer für jeden Zeichenvorgang. Müssen ein Vielfaches der Indexgröße sein. | |
| $offsetsOffset Pflicht | number | Startposition innerhalb von offsetsList. |
|
| $instanceCountsList Pflicht | Int32Array | Array<number> | Anzahl der Instanzen für jeden Zeichenvorgang. | |
| $instanceCountsOffset Pflicht | number | Startposition innerhalb von instanceCountsList. |
|
| $drawCount Pflicht | number | Anzahl der auszuführenden Zeichenvorgänge. Jede Liste muss ab ihrem Offset mindestens so viele Einträge enthalten. |
Rückgabewert
gl.getError() gemeldet (z. B. INVALID_OPERATION, INVALID_VALUE).Beispiele
Erweiterung abfragen und Verfügbarkeit prüfen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
const ext = gl.getExtension('WEBGL_multi_draw');
if (!ext) {
console.warn('WEBGL_multi_draw wird nicht unterstützt – Fallback auf Einzelaufrufe.');
} else {
console.log(typeof ext.multiDrawElementsInstancedWEBGL);
}
Drei Meshes aus einem gemeinsamen Index-Puffer instanziiert zeichnen
// Voraussetzung: Programm, VAO mit Vertex-/Instanz-Attributen
// (vertexAttribDivisor) und ELEMENT_ARRAY_BUFFER sind gebunden.
// Indizes vom Typ UNSIGNED_SHORT (2 Byte pro Index).
const meshes = [
{ firstIndex: 0, indexCount: 36, instances: 100 }, // Würfel
{ firstIndex: 36, indexCount: 240, instances: 20 }, // Kugel
{ firstIndex: 276, indexCount: 6, instances: 500 } // Quad
];
const counts = new Int32Array(meshes.map(m => m.indexCount));
const offsets = new Int32Array(meshes.map(m => m.firstIndex * 2)); // Bytes!
const instanceCounts = new Int32Array(meshes.map(m => m.instances));
const drawScene = () => {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
ext.multiDrawElementsInstancedWEBGL(
gl.TRIANGLES,
counts, 0,
gl.UNSIGNED_SHORT,
offsets, 0,
instanceCounts, 0,
meshes.length
);
requestAnimationFrame(drawScene);
};
requestAnimationFrame(drawScene);
gl_DrawID im Vertex-Shader nutzen
const vertexSource = `#version 300 es
#extension GL_ANGLE_multi_draw : require
in vec3 aPosition;
in vec3 aInstanceOffset; // Divisor 1
uniform vec4 uColors[3];
uniform mat4 uViewProj;
out vec4 vColor;
void main() {
vColor = uColors[gl_DrawID]; // Farbe pro Mesh
gl_Position = uViewProj * vec4(aPosition + aInstanceOffset, 1.0);
}`;
const shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, vertexSource);
gl.compileShader(shader);
console.log(gl.getShaderParameter(shader, gl.COMPILE_STATUS));
Fallback ohne Erweiterung
const multiDraw = (gl, ext, mode, counts, type, offsets, instanceCounts, n) => {
if (ext) {
ext.multiDrawElementsInstancedWEBGL(mode, counts, 0, type, offsets, 0, instanceCounts, 0, n);
return;
}
// Fallback: Einzelaufrufe (gl_DrawID dann per Uniform nachbilden)
for (let i = 0; i < n; i++) {
gl.drawElementsInstanced(mode, counts[i], type, offsets[i], instanceCounts[i]);
}
};