Start · Sprachen · JavaScript · Referenz · ANGLE_instanced_arrays

ANGLE_instanced_arrays

Schnittstelle

WebGL-1-Erweiterung für instanziertes Zeichnen: Viele Kopien derselben Geometrie werden mit einem einzigen Draw-Call gerendert.

seit JavaScript WebGL 1 Erweiterung – Baseline: alle modernen Brow Kategorie: webapi

Signatur

interface ANGLE_instanced_arrays

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): wie drawArrays(), aber primcount-mal instanziert.
  • drawElementsInstancedANGLE(mode, count, type, offset, primcount): wie drawElements(), aber instanziert.
  • vertexAttribDivisorANGLE(index, divisor): legt fest, wie oft ein Attribut weiterrückt. 0 bedeutet pro Vertex (Standard), 1 pro Instanz, n alle n Instanzen.
  • VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE (0x88FE): Konstante für gl.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

Typ
ANGLE_instanced_arrays | null
Beschreibung
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));
function 88fe

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);
Divisor von a_offset: 1