Signatur
Beschreibung
drawElements() ist das indizierte Gegenstück zu drawArrays(). Die Vertices werden nicht der Reihe nach gelesen, sondern über einen Indexpuffer (gl.ELEMENT_ARRAY_BUFFER) adressiert. So lassen sich Vertices, die mehrere Dreiecke gemeinsam nutzen, wie etwa die Ecken eines Würfels oder eines Terrain-Gitters, einmal speichern und mehrfach verwenden. Das spart Speicher und Bandbreite. Zusätzlich kann der Vertex-Cache der GPU schon berechnete Ergebnisse wiederverwenden.
Vor dem Aufruf müssen ein Shader-Programm aktiv sein (gl.useProgram()), die Vertex-Attribute über vertexAttribPointer() und enableVertexAttribArray() eingerichtet sein und ein Indexpuffer an gl.ELEMENT_ARRAY_BUFFER gebunden sein. count gibt an, wie viele Indizes gelesen werden. Gemeint sind also nicht Dreiecke. offset ist ein Byte-Offset in den Indexpuffer.
Anders als in OpenGL gibt es in WebGL keine clientseitigen Index-Arrays. Die Indizes müssen immer in einem Pufferobjekt liegen. Außerdem prüft WebGL, ob Indizes außerhalb der gebundenen Vertex-Daten liegen. In dem Fall wird statt undefiniertem Verhalten ein Fehler ausgelöst.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $mode Pflicht | GLenum | Primitiv-Typ: gl.POINTS, gl.LINE_STRIP, gl.LINE_LOOP, gl.LINES, gl.TRIANGLE_STRIP, gl.TRIANGLE_FAN oder gl.TRIANGLES. |
|
| $count Pflicht | GLsizei (number) | Anzahl der zu lesenden Indizes, z. B. 36 für einen Würfel aus 12 Dreiecken. | |
| $type Pflicht | GLenum | Datentyp der Indizes: gl.UNSIGNED_BYTE, gl.UNSIGNED_SHORT oder, mit der Erweiterung OES_element_index_uint bzw. in WebGL2, gl.UNSIGNED_INT. |
|
| $offset Pflicht | GLintptr (number) | Byte-Offset im Indexpuffer, ab dem gelesen wird. Muss ein Vielfaches der Größe von type sein (z. B. gerade Zahl bei UNSIGNED_SHORT). |
Rückgabewert
undefined). Fehler werden über gl.getError() gemeldet.Beispiele
Rechteck aus zwei Dreiecken mit Indexpuffer
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const vsSource = `attribute vec2 aPos;
void main() { gl_Position = vec4(aPos, 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);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(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);
// 4 Eckpunkte statt 6 – Indizes teilen sich zwei Ecken
const vertices = new Float32Array([-0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5]);
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
const loc = gl.getAttribLocation(program, 'aPos');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
console.log(gl.getError() === gl.NO_ERROR);
Teilbereich des Indexpuffers zeichnen (Byte-Offset)
// Annahme: Indexpuffer mit Uint16Array für zwei Objekte ist gebunden
// Objekt A: Indizes 0–35, Objekt B: Indizes 36–71
const BYTES = Uint16Array.BYTES_PER_ELEMENT; // 2
// Nur Objekt B zeichnen: offset in Bytes, nicht in Indizes!
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_SHORT, 36 * BYTES);
32-Bit-Indizes für große Meshes
// In WebGL1 ist UNSIGNED_INT nur mit Erweiterung verfügbar
const ext = gl.getExtension('OES_element_index_uint');
const useUint32 = ext !== null || gl instanceof WebGL2RenderingContext;
const IndexArray = useUint32 ? Uint32Array : Uint16Array;
const indexType = useUint32 ? gl.UNSIGNED_INT : gl.UNSIGNED_SHORT;
const indices = new IndexArray([0, 1, 2 /* … mehr als 65535 Vertices möglich */]);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLES, indices.length, indexType, 0);
console.log('Indextyp:', useUint32 ? 'UNSIGNED_INT' : 'UNSIGNED_SHORT');
// Wichtig · Fallstricke
- Byte-Offset:
offsetwird in Bytes angegeben. BeiUNSIGNED_SHORTentspricht Index n alson * 2. Ist der Offset nicht passend zum Typ ausgerichtet, gibt esINVALID_OPERATION. - Typ muss zum Array passen: Ein
Uint16Arraygehört zugl.UNSIGNED_SHORT. Wer das verwechselt, bekommt verzerrte Geometrie oder Fehler. - Index-Limit: Mit
UNSIGNED_SHORTlassen sich höchstens 65.536 Vertices adressieren. Für größere Meshes braucht manUNSIGNED_INT. In WebGL1 geht das nur mitOES_element_index_uint, in WebGL2 ist es immer verfügbar. - Fehlerfälle: Ohne gebundenen
ELEMENT_ARRAY_BUFFER, bei Indizes außerhalb der Vertex-Puffer oder bei zu großemcountgibt esINVALID_OPERATION. Ein ungültigermodeodertypeführt zuINVALID_ENUM, ein negativercountoderoffsetzuINVALID_VALUE. - VAOs: Die Bindung von
ELEMENT_ARRAY_BUFFERgehört zum Zustand des Vertex Array Objects (WebGL2 bzw.OES_vertex_array_object). Nach einem VAO-Wechsel muss der Indexpuffer also passend gebunden sein. - In WebGL darf ein Puffer, der einmal als
ELEMENT_ARRAY_BUFFERgebunden war, nicht alsARRAY_BUFFERgenutzt werden und umgekehrt. - Die Methode wird in allen modernen Browsern unterstützt. Für Instancing gibt es
drawElementsInstanced()(WebGL2) bzw.ANGLE_instanced_arrays.