Signatur
Beschreibung
Die Methode vertexAttribDivisorANGLE() gehört zur WebGL-1-Erweiterung ANGLE_instanced_arrays. Sie bestimmt, mit welcher Rate ein Vertex-Attribut beim instanzierten Rendern durch seinen Puffer läuft. Wenn Sie viele gleichartige Objekte (z. B. Bäume, Partikel, Kacheln) mit einem einzigen Draw-Call zeichnen, braucht meist jede Instanz eigene Daten wie Position, Farbe oder Transformationsmatrix, die Geometrie selbst bleibt aber gleich.
Beim Divisor 0 (Standard) rückt das Attribut wie gewohnt pro Vertex weiter. Ist der Divisor 1, bekommt jede Instanz einen neuen Wert. Bei 2 teilen sich zwei aufeinanderfolgende Instanzen einen Wert, und so weiter. Gezeichnet wird anschließend mit drawArraysInstancedANGLE() oder drawElementsInstancedANGLE().
Der Divisor ist Teil des Zustands des jeweiligen Attribut-Slots. Er bleibt also gesetzt, bis Sie ihn ändern, und gilt auch für spätere normale drawArrays()-Aufrufe. In WebGL 2 ist diese Funktion direkt als WebGL2RenderingContext.vertexAttribDivisor() verfügbar. Die Erweiterung brauchen Sie dort nicht.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $index Pflicht | GLuint (number) | Index des generischen Vertex-Attributs, typischerweise über gl.getAttribLocation() ermittelt. |
|
| $divisor Pflicht | GLuint (number) | Anzahl der Instanzen, die sich einen Attributwert teilen. 0 = pro Vertex weiterrücken, 1 = pro Instanz, n = alle n Instanzen. |
Rückgabewert
undefined). Bei ungültigem index wird gl.INVALID_VALUE gesetzt, abfragbar über gl.getError().Beispiele
Erweiterung laden und Divisor setzen
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
if (!ext) {
throw new Error('Instanziertes Rendern wird nicht unterstützt');
}
// Attribut 1 soll pro Instanz weiterrücken
ext.vertexAttribDivisorANGLE(1, 1);
console.log(gl.getVertexAttrib(1, ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE));
100 Quadrate mit individuellen Offsets in einem Draw-Call
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('ANGLE_instanced_arrays');
const vsSource = `
attribute vec2 aPosition;
attribute vec2 aOffset;
void main() {
gl_Position = vec4(aPosition * 0.05 + aOffset, 0.0, 1.0);
}`;
const fsSource = `
precision mediump float;
void main() { gl_FragColor = 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 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);
// Geometrie: ein Quadrat aus zwei Dreiecken (pro Vertex)
const quad = new Float32Array([-1,-1, 1,-1, -1,1, -1,1, 1,-1, 1,1]);
const quadBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, quadBuf);
gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);
const aPosition = gl.getAttribLocation(program, 'aPosition');
gl.enableVertexAttribArray(aPosition);
gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(aPosition, 0); // pro Vertex
// Instanzdaten: 10×10 Raster von Offsets
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 offsetBuf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuf);
gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW);
const aOffset = gl.getAttribLocation(program, 'aOffset');
gl.enableVertexAttribArray(aOffset);
gl.vertexAttribPointer(aOffset, 2, gl.FLOAT, false, 0, 0);
ext.vertexAttribDivisorANGLE(aOffset, 1); // pro Instanz
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 100);
Divisor nach dem Zeichnen zurücksetzen
// Nach instanziertem Zeichnen den Zustand bereinigen,
// damit spätere normale drawArrays()-Aufrufe nicht betroffen sind
const resetDivisors = (ext, locations) => {
locations.forEach((loc) => ext.vertexAttribDivisorANGLE(loc, 0));
};
resetDivisors(ext, [aOffset]);
gl.drawArrays(gl.TRIANGLES, 0, 6);
// Wichtig · Fallstricke
- Nur über die Erweiterung verfügbar: Rufen Sie die Methode auf dem Objekt aus
gl.getExtension('ANGLE_instanced_arrays')auf, nicht aufgl. Prüfen Sie, ob das Ergebnisnullist. Die Erweiterung wird aber praktisch überall unterstützt. - WebGL 2: Dort gibt es die Erweiterung nicht. Verwenden Sie stattdessen
gl.vertexAttribDivisor(). - Zustand bleibt erhalten: Ein vergessener Divisor ungleich
0führt bei späteren Draw-Calls zu falschen Ergebnissen, die schwer zu finden sind. Setzen Sie ihn deshalb bei Bedarf zurück. MitOES_vertex_array_objectwird der Divisor im VAO gespeichert. - Mindestens ein Attribut mit Divisor 0: In WebGL 1 muss mindestens ein aktives Attribut den Divisor
0haben, sonst löst der instanzierte Draw-CallINVALID_OPERATIONaus. - Matrizen als Attribut: Ein
mat4belegt vier Attribut-Slots. Den Divisor müssen Sie für jeden der vier Indizes einzeln setzen. - Ist
index≥MAX_VERTEX_ATTRIBS, wirdINVALID_VALUEerzeugt.