Start · Sprachen · JavaScript · Referenz · WebGLRenderingContext.drawElements()

WebGLRenderingContext.drawElements()

Methode

Rendert Primitiven (Dreiecke, Linien, Punkte) anhand der Indizes im gebundenen <code>ELEMENT_ARRAY_BUFFER</code>, also indiziertes Zeichnen.

seit JavaScript Baseline: alle Browser (WebGL 1.0) Kategorie: webapi

Signatur

WebGLRenderingContext.prototype.drawElements(mode, count, type, offset)

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

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

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');
Indextyp: UNSIGNED_INT

// Wichtig · Fallstricke

  • Byte-Offset: offset wird in Bytes angegeben. Bei UNSIGNED_SHORT entspricht Index n also n * 2. Ist der Offset nicht passend zum Typ ausgerichtet, gibt es INVALID_OPERATION.
  • Typ muss zum Array passen: Ein Uint16Array gehört zu gl.UNSIGNED_SHORT. Wer das verwechselt, bekommt verzerrte Geometrie oder Fehler.
  • Index-Limit: Mit UNSIGNED_SHORT lassen sich höchstens 65.536 Vertices adressieren. Für größere Meshes braucht man UNSIGNED_INT. In WebGL1 geht das nur mit OES_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ßem count gibt es INVALID_OPERATION. Ein ungültiger mode oder type führt zu INVALID_ENUM, ein negativer count oder offset zu INVALID_VALUE.
  • VAOs: Die Bindung von ELEMENT_ARRAY_BUFFER gehö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_BUFFER gebunden war, nicht als ARRAY_BUFFER genutzt werden und umgekehrt.
  • Die Methode wird in allen modernen Browsern unterstützt. Für Instancing gibt es drawElementsInstanced() (WebGL2) bzw. ANGLE_instanced_arrays.