Start · Sprachen · JavaScript · Referenz · WEBGL_multi_draw.multiDrawElementsInstancedWEBGL()

WEBGL_multi_draw.multiDrawElementsInstancedWEBGL()

Methode

Rendert mit einem einzigen Aufruf mehrere indizierte, instanziierte Zeichenvorgänge – so, als würde man <code>drawElementsInstanced()</code> mehrfach hintereinander aufrufen.

seit JavaScript WebGL-Erweiterung (WebGL 1 und WebGL 2); Chromium- Kategorie: webapi

Signatur

WEBGL_multi_draw.multiDrawElementsInstancedWEBGL(mode, countsList, countsOffset, type, offsetsList, offsetsOffset, instanceCountsList, instanceCountsOffset, drawCount)

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

Typ
undefined
Beschreibung
Kein Rückgabewert. Fehler werden über 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);
}
function

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));
true

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]);
  }
};