Start · Sprachen · JavaScript · Referenz · ANGLE_instanced_arrays.vertexAttribDivisorANGLE()

ANGLE_instanced_arrays.vertexAttribDivisorANGLE()

Methode

Legt fest, wie oft ein generisches Vertex-Attribut beim instanzierten Zeichnen weiterrückt: pro Vertex (<code>0</code>) oder einmal alle <code>divisor</code> Instanzen.

seit JavaScript WebGL 1.0-Erweiterung, Baseline: alle Browser Kategorie: webapi

Signatur

ANGLE_instanced_arrays.vertexAttribDivisorANGLE(index, divisor)

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

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

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 auf gl. Prüfen Sie, ob das Ergebnis null ist. 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 0 führt bei späteren Draw-Calls zu falschen Ergebnissen, die schwer zu finden sind. Setzen Sie ihn deshalb bei Bedarf zurück. Mit OES_vertex_array_object wird der Divisor im VAO gespeichert.
  • Mindestens ein Attribut mit Divisor 0: In WebGL 1 muss mindestens ein aktives Attribut den Divisor 0 haben, sonst löst der instanzierte Draw-Call INVALID_OPERATION aus.
  • Matrizen als Attribut: Ein mat4 belegt vier Attribut-Slots. Den Divisor müssen Sie für jeden der vier Indizes einzeln setzen.
  • Ist index ≥ MAX_VERTEX_ATTRIBS, wird INVALID_VALUE erzeugt.