Signatur
Beschreibung
ANGLE_instanced_arrays ist eine Erweiterung für WebGL 1. Mit ihr zeichnen Sie dieselbe Geometrie, also dieselben Vertex-Daten mit gleicher Primitivanzahl und gleichem Primitivtyp, mit einem einzigen Draw-Call viele Male. Jede Instanz kann eigene Attribute bekommen, etwa Position, Farbe oder Transformationsmatrix. Das senkt den CPU-Aufwand und den Overhead zwischen JavaScript und GPU deutlich, weil nicht mehr jedes Objekt einen eigenen Aufruf von drawArrays() oder drawElements() braucht.
Sie erhalten die Erweiterung mit gl.getExtension("ANGLE_instanced_arrays"). Das zurückgegebene Objekt stellt diese Mitglieder bereit:
drawArraysInstancedANGLE(mode, first, count, primcount): wiedrawArrays(), aberprimcount-mal instanziert.drawElementsInstancedANGLE(mode, count, type, offset, primcount): wiedrawElements(), aber instanziert.vertexAttribDivisorANGLE(index, divisor): legt fest, wie oft ein Attribut weiterrückt.0bedeutet pro Vertex (Standard),1pro Instanz,nallenInstanzen.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE(0x88FE): Konstante fürgl.getVertexAttrib(), mit der Sie den aktuellen Divisor eines Attributs abfragen.
Typische Einsatzfälle sind Partikelsysteme, Vegetation wie Gras und Bäume, Menschenmengen, Kachel- und Voxel-Welten sowie Datenvisualisierungen mit Tausenden gleichartigen Markern. In WebGL 2 ist die Funktion fest eingebaut: Dort heißen die Methoden gl.drawArraysInstanced(), gl.drawElementsInstanced() und gl.vertexAttribDivisor(), eine Erweiterung ist nicht nötig.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $mode Pflicht | GLenum | Primitivtyp für drawArraysInstancedANGLE/drawElementsInstancedANGLE, z. B. gl.TRIANGLES, gl.TRIANGLE_STRIP, gl.LINES, gl.POINTS. |
|
| $first Pflicht | GLint | Nur drawArraysInstancedANGLE: Startindex im Vertex-Array. |
|
| $count Pflicht | GLsizei | Anzahl der Vertices (bei drawArrays…) bzw. Indizes (bei drawElements…) pro Instanz. |
|
| $type Pflicht | GLenum | Nur drawElementsInstancedANGLE: Typ der Indexwerte, also gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT oder mit OES_element_index_uint auch gl.UNSIGNED_INT. |
|
| $offset Pflicht | GLintptr | Nur drawElementsInstancedANGLE: Byte-Offset im Index-Puffer. Er muss ein Vielfaches der Größe von type sein. |
|
| $primcount Pflicht | GLsizei | Anzahl der Instanzen, die gezeichnet werden. | |
| $index Pflicht | GLuint | Nur vertexAttribDivisorANGLE: Location des Vertex-Attributs (aus gl.getAttribLocation()). |
|
| $divisor Pflicht | GLuint | 0 | Nur vertexAttribDivisorANGLE: 0 = pro Vertex, 1 = pro Instanz, n = alle n Instanzen. |
Rückgabewert
gl.getExtension("ANGLE_instanced_arrays") gibt das Erweiterungsobjekt zurück oder null, wenn die Erweiterung nicht unterstützt wird. Die Zeichenmethoden und vertexAttribDivisorANGLE geben undefined zurück.Beispiele
Erweiterung abrufen und Verfügbarkeit prüfen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
throw new Error('Instanziertes Zeichnen wird nicht unterstützt');
}
console.log(typeof ext.drawArraysInstancedANGLE);
console.log(ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE.toString(16));
1000 farbige Quadrate mit einem Draw-Call
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
const vsSource = `
attribute vec2 a_pos; // pro Vertex
attribute vec2 a_offset; // pro Instanz
attribute vec3 a_color; // pro Instanz
varying vec3 v_color;
void main() {
v_color = a_color;
gl_Position = vec4(a_pos * 0.02 + a_offset, 0.0, 1.0);
}`;
const fsSource = `
precision mediump float;
varying vec3 v_color;
void main() { gl_FragColor = vec4(v_color, 1.0); }`;
const compile = (type, src) => {
const s = gl.createShader(type);
gl.shaderSource(s, src);
gl.compileShader(s);
return s;
};
const program = gl.createProgram();
gl.attachShader(program, compile(gl.VERTEX_SHADER, vsSource));
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, fsSource));
gl.linkProgram(program);
gl.useProgram(program);
const setupAttrib = (name, data, size, divisor) => {
const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
const loc = gl.getAttribLocation(program, name);
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, size, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(loc, divisor);
};
const COUNT = 1000;
const offsets = new Float32Array(COUNT * 2).map(() => Math.random() * 2 - 1);
const colors = new Float32Array(COUNT * 3).map(() => Math.random());
// Quadrat als Triangle-Strip (4 Vertices)
setupAttrib('a_pos', new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), 2, 0);
setupAttrib('a_offset', offsets, 2, 1);
setupAttrib('a_color', colors, 3, 1);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
ext.drawArraysInstancedANGLE(gl.TRIANGLE_STRIP, 0, 4, COUNT);
Indiziertes Zeichnen und Divisor abfragen
// Voraussetzung: Programm und Attribute wie oben, zusätzlich ein Index-Puffer
const indices = new Uint16Array([0, 1, 2, 2, 1, 3]);
const ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
ext.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, 1000);
const loc = gl.getAttribLocation(program, 'a_offset');
const divisor = gl.getVertexAttrib(loc, ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE);
console.log(`Divisor von a_offset: ${divisor}`);
// Aufräumen: Divisor zurücksetzen, damit andere Draw-Calls nicht betroffen sind
ext.vertexAttribDivisorANGLE(loc, 0);