Signatur
Beschreibung
drawArraysInstancedANGLE() gehört zur WebGL-1-Erweiterung ANGLE_instanced_arrays. Die Methode arbeitet wie gl.drawArrays(), zeichnet dieselbe Geometrie aber primcount-mal hintereinander. Nützlich ist das überall, wo viele gleichartige Objekte vorkommen: Partikel, Grasbüschel, Bäume, Kacheln oder Sprites.
Damit die Instanzen unterschiedlich aussehen, legt man pro Instanz Attribute an, etwa Position, Farbe oder Skalierung. Mit ext.vertexAttribDivisorANGLE(location, 1) legt man fest, dass ein Attribut nicht pro Vertex, sondern nur einmal pro Instanz weitergeschaltet wird. Attribute mit Divisor 0 (das ist die Voreinstellung) verhalten sich wie gewohnt pro Vertex.
Der große Vorteil ist die Leistung: Statt tausender einzelner drawArrays()-Aufrufe, die jeweils die CPU und den Treiber belasten, genügt ein Aufruf. Die Erweiterung holt man mit gl.getExtension('ANGLE_instanced_arrays'). In WebGL 2 ist die Funktion fest eingebaut und heißt dort gl.drawArraysInstanced().
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $mode Pflicht | GLenum | Art der Primitive: gl.POINTS, gl.LINE_STRIP, gl.LINE_LOOP, gl.LINES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN oder gl.TRIANGLES. |
|
| $first Pflicht | GLint | Index des ersten Vertex in den aktivierten Attribut-Arrays. | |
| $count Pflicht | GLsizei | Anzahl der Vertices, die pro Instanz gezeichnet werden. | |
| $primcount Pflicht | GLsizei | Anzahl der Instanzen, die gezeichnet werden. |
Rückgabewert
gl.getError() gemeldet.Beispiele
Erweiterung holen und prüfen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
console.warn('Instancing wird nicht unterstützt');
} else {
console.log(typeof ext.drawArraysInstancedANGLE);
}
100 Dreiecke mit Positionen pro Instanz zeichnen
const gl = document.querySelector('canvas').getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
const vs = `
attribute vec2 aPos;
attribute vec2 aOffset;
void main() {
gl_Position = vec4(aPos * 0.05 + aOffset, 0.0, 1.0);
}`;
const fs = `
precision mediump float;
void main() { gl_FragColor = vec4(1.0, 0.5, 0.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);
// Geometrie: ein Dreieck (pro Vertex)
const posBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, posBuf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 1, -1, -1, 1, -1]), gl.STATIC_DRAW);
const aPos = gl.getAttribLocation(prog, 'aPos');
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
// Versatz pro Instanz (10 × 10 Raster)
const offsets = new Float32Array(200);
for (let i = 0; i < 100; i++) {
offsets[i * 2] = (i % 10) / 5 - 0.9;
offsets[i * 2 + 1] = Math.floor(i / 10) / 5 - 0.9;
}
const offBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, offBuf);
gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW);
const aOffset = gl.getAttribLocation(prog, 'aOffset');
gl.enableVertexAttribArray(aOffset);
gl.vertexAttribPointer(aOffset, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(aOffset, 1); // einmal pro Instanz weiterschalten
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 3, 100);
Einheitliche Hilfsfunktion für WebGL 1 und WebGL 2
const getInstancedDraw = (gl) => {
if (typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext) {
return (mode, first, count, n) => gl.drawArraysInstanced(mode, first, count, n);
}
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) return null;
return (mode, first, count, n) => ext.drawArraysInstancedANGLE(mode, first, count, n);
};
const gl = document.createElement('canvas').getContext('webgl');
const drawInstanced = getInstancedDraw(gl);
console.log(drawInstanced ? 'Instancing verfügbar' : 'Kein Instancing');
// Wichtig · Fallstricke
- Divisor nicht vergessen: Ohne
vertexAttribDivisorANGLE(loc, 1)werden Attribute pro Instanz wie Attribute pro Vertex gelesen. Dann kommt eine falsche Darstellung heraus oder es gibt einen Fehler wegen zu kurzer Puffer. - Mindestens ein Attribut mit Divisor 0: Wenn alle aktivierten Attribute einen Divisor ungleich 0 haben, meldet der Aufruf
gl.INVALID_OPERATION. - Negative Werte für
first,countoderprimcountführen zugl.INVALID_VALUE, ein ungültigermodezugl.INVALID_ENUM. - Die Puffer müssen groß genug sein: für Attribute pro Instanz mindestens
ceil(primcount / divisor)Einträge. Sonst bricht WebGL den Aufruf ab. - Kompatibilität: Die Erweiterung wird praktisch überall unterstützt, wo WebGL 1 läuft. In einem WebGL-2-Kontext gibt es sie nicht, dort nimmt man
gl.drawArraysInstanced(). - Das Erweiterungsobjekt hängt am Kontext. Nach einem Kontextverlust (
webglcontextlost) muss man es neu holen.