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

WEBGL_multi_draw.multiDrawArraysInstancedWEBGL()

Methode

Führt mit einem einzigen Aufruf mehrere instanzierte <code>drawArrays</code>-Zeichenbefehle aus. Jeder Befehl hat einen eigenen Startindex, eine eigene Vertex-Anzahl und eine eigene Instanzanzahl.

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

Signatur

WEBGL_multi_draw.multiDrawArraysInstancedWEBGL(mode, firstsList, firstsOffset, countsList, countsOffset, instanceCountsList, instanceCountsOffset, drawcount)

Beschreibung

Die Methode multiDrawArraysInstancedWEBGL() gehört zur Erweiterung WEBGL_multi_draw. Sie fasst mehrere Aufrufe von drawArraysInstanced() zu einem einzigen API-Aufruf zusammen. Das Ergebnis entspricht einer Schleife über drawcount Einträge, in der jeweils gl.drawArraysInstanced(mode, firsts[i], counts[i], instanceCounts[i]) aufgerufen wird. Der Aufwand in JavaScript und im Treiber fällt dabei aber nur einmal an.

Das lohnt sich, wenn viele kleine Objekte mit derselben Shader- und Zustandskonfiguration gezeichnet werden. Beispiele sind Partikelsysteme, Vegetation, Kacheln oder ein Text-Renderer, bei denen die Geometrien hintereinander im selben Vertex-Puffer liegen. Die Zahl der Zeichenaufrufe (engl. „draw calls“) sinkt so deutlich, was vor allem bei einer CPU-Engstelle spürbar Leistung bringt.

Im Vertex-Shader kann man mit der eingebauten Variable gl_DrawID feststellen, welcher Teil-Zeichenbefehl gerade läuft. Dafür muss der Shader #extension GL_ANGLE_multi_draw : require deklarieren. Mit gl_DrawID lässt sich pro Teil-Befehl zum Beispiel eine Farbe oder eine Transformation aus einem Uniform-Array oder einer Textur auswählen.

Die Listen für Startindizes, Vertex-Anzahlen und Instanzanzahlen können Int32Arrays oder normale Arrays sein. Mit den zugehörigen Offsets ab dem jeweiligen Listenanfang lassen sich Teilbereiche größerer Puffer verwenden, ohne sie zu kopieren.

Parameter

Name Typ Default Beschreibung
$mode Pflicht GLenum Primitivtyp: gl.POINTS, gl.LINE_STRIP, gl.LINE_LOOP, gl.LINES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN oder gl.TRIANGLES.
$firstsList Pflicht Int32Array | Array<number> Liste der Start-Vertex-Indizes, einer pro Teil-Zeichenbefehl.
$firstsOffset Pflicht number Position in firstsList, ab der gelesen wird.
$countsList Pflicht Int32Array | Array<number> Liste mit der Anzahl zu zeichnender Vertices pro Teil-Zeichenbefehl.
$countsOffset Pflicht number Position in countsList, ab der gelesen wird.
$instanceCountsList Pflicht Int32Array | Array<number> Liste mit der Anzahl der Instanzen pro Teil-Zeichenbefehl.
$instanceCountsOffset Pflicht number Position in instanceCountsList, ab der gelesen wird.
$drawcount Pflicht number Anzahl der auszuführenden Teil-Zeichenbefehle. Jede Liste muss ab ihrem Offset mindestens so viele Einträge haben.

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Fehler werden über gl.getError() gemeldet, zum Beispiel gl.INVALID_VALUE bei negativen Werten oder zu kurzen Listen.

Beispiele

Erweiterung abfragen und drei Geometrien instanziert zeichnen

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');
} else {
  // Voraussetzung: Programm, VAO und Vertex-Puffer sind gebunden.
  // Im Puffer liegen hintereinander: Dreieck (3), Quad (6), Hexagon (18)
  const firsts         = new Int32Array([0, 3, 9]);
  const counts         = new Int32Array([3, 6, 18]);
  const instanceCounts = new Int32Array([100, 50, 10]);

  ext.multiDrawArraysInstancedWEBGL(
    gl.TRIANGLES,
    firsts, 0,
    counts, 0,
    instanceCounts, 0,
    firsts.length
  );
  console.log(gl.getError() === gl.NO_ERROR);
}
true

gl_DrawID im Vertex-Shader verwenden

const vsSource = `#version 300 es
#extension GL_ANGLE_multi_draw : require
in vec2 aPos;
in vec2 aOffset;          // pro Instanz (vertexAttribDivisor = 1)
uniform vec3 uColors[3];
out vec3 vColor;
void main() {
  vColor = uColors[gl_DrawID];   // Farbe je Teil-Zeichenbefehl
  gl_Position = vec4(aPos * 0.05 + aOffset, 0.0, 1.0);
}`;

const fsSource = `#version 300 es
precision mediump float;
in vec3 vColor;
out vec4 outColor;
void main() { outColor = vec4(vColor, 1.0); }`;

// Die Erweiterung vor dem Kompilieren aktivieren:
const ext = gl.getExtension('WEBGL_multi_draw');
const compile = (type, src) => {
  const s = gl.createShader(type);
  gl.shaderSource(s, src);
  gl.compileShader(s);
  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
    throw new Error(gl.getShaderInfoLog(s));
  }
  return s;
};
const vs = compile(gl.VERTEX_SHADER, vsSource);
const fs = compile(gl.FRAGMENT_SHADER, fsSource);

Teilbereich eines großen Befehlspuffers per Offset zeichnen

// Befehle für 1000 Objekte, pro Frame wird nur ein sichtbarer Ausschnitt gezeichnet
const MAX = 1000;
const firsts = new Int32Array(MAX);
const counts = new Int32Array(MAX).fill(6);
const instances = new Int32Array(MAX).fill(1);
for (let i = 0; i < MAX; i++) firsts[i] = i * 6;

const drawRange = (start, n) => {
  ext.multiDrawArraysInstancedWEBGL(
    gl.TRIANGLES,
    firsts, start,
    counts, start,
    instances, start,
    n
  );
};

drawRange(200, 150); // zeichnet die Objekte 200 bis 349