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

WEBGL_multi_draw.multiDrawArraysWEBGL()

Methode

Rendert mit einem einzigen Aufruf mehrere Primitiv-Bereiche aus Vertex-Array-Daten – quasi mehrere <code>drawArrays()</code>-Aufrufe in einem Rutsch.

seit JavaScript WebGL-Erweiterung; Chrome 89+, Edge 89+, Firefox 1 Kategorie: webapi

Signatur

WEBGL_multi_draw.multiDrawArraysWEBGL(mode, firstsList, firstsOffset, countsList, countsOffset, drawcount)

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

Typ
void
Beschreibung
Kein 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);
true

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') kann null zurückgeben, etwa bei manchen Safari-Versionen, älteren Treibern oder Software-Rendering. Halte immer einen Fallback mit einer drawArrays()-Schleife bereit.
  • Offsets zählen Elemente: firstsOffset und countsOffset sind Indizes in die Listen, keine Byte-Offsets. Reichen die Listen ab dem Offset nicht für drawcount Einträge, wird INVALID_OPERATION ausgelöst; negative Werte führen zu INVALID_VALUE.
  • Gemeinsamer Zustand: Alle Teil-Zeichenvorgänge teilen sich Programm, Uniforms, Texturen und Blend-Zustand. Unterschiede pro Objekt lassen sich nur über gl_DrawID und Daten in Arrays bzw. Texturen abbilden.
  • gl_DrawID ist nur verfügbar, wenn der Shader #extension GL_ANGLE_multi_draw : require deklariert; 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.