Start · Sprachen · JavaScript · Referenz · GPURenderPassEncoder.drawIndexed()

GPURenderPassEncoder.drawIndexed()

Methode

Zeichnet indizierte Primitiven mit den über <code>setVertexBuffer()</code> und <code>setIndexBuffer()</code> gebundenen Vertex- und Indexpuffern.

seit JavaScript WebGPU – Chrome/Edge 113+, Safari 26+, Firefox 141 Kategorie: webapi

Signatur

GPURenderPassEncoder.prototype.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance)

Beschreibung

drawIndexed() zeichnet innerhalb eines Render-Passes Primitiven, deren Eckpunkte über einen Indexpuffer referenziert werden. Statt jeden Vertex für jedes Dreieck erneut abzulegen, speichert man jeden Eckpunkt nur einmal im Vertexpuffer. Der Indexpuffer legt dann fest, in welcher Reihenfolge die Eckpunkte zu Dreiecken, Linien oder Punkten zusammengesetzt werden. Das spart Speicher und Bandbreite, zum Beispiel bei Meshes, in denen sich viele Dreiecke Eckpunkte teilen.

Vor dem Aufruf müssen gesetzt sein: eine Render-Pipeline über setPipeline(), alle von der Pipeline erwarteten Vertexpuffer über setVertexBuffer(), ein Indexpuffer über setIndexBuffer(buffer, "uint16" | "uint32") und die nötigen Bind-Groups über setBindGroup(). Der Aufruf wird nur in den Befehlspuffer aufgezeichnet. Die GPU führt ihn erst aus, wenn der Puffer über device.queue.submit() übermittelt wird.

Mit instanceCount und firstInstance lässt sich Instancing umsetzen: Dasselbe Mesh wird mehrfach gezeichnet, und der Shader kann die Instanzen über @builtin(instance_index) unterscheiden. baseVertex wird zu jedem gelesenen Indexwert addiert. So können mehrere Meshes in einem gemeinsamen Vertexpuffer liegen und trotzdem Indizes verwenden, die jeweils bei 0 beginnen.

Parameter

Name Typ Default Beschreibung
$indexCount Pflicht number Anzahl der Indizes, die aus dem Indexpuffer gelesen und gezeichnet werden. Bei Dreiecken meist ein Vielfaches von 3.
$instanceCount number 1 Anzahl der zu zeichnenden Instanzen.
$firstIndex number 0 Position im Indexpuffer (in Indizes gezählt, nicht in Bytes), ab der gelesen wird. Sie wird zum Offset aus setIndexBuffer() addiert.
$baseVertex number 0 Wert, der vor dem Zugriff auf den Vertexpuffer zu jedem Indexwert addiert wird. Er darf negativ sein (vorzeichenbehaftete 32-Bit-Ganzzahl).
$firstInstance number 0 Instanz-Index, mit dem gezählt wird. Er wirkt auf instance_index und auf Vertexpuffer mit stepMode: "instance".

Rückgabewert

Typ
undefined
Beschreibung
Kein Rückgabewert. Der Zeichenbefehl wird nur im Encoder aufgezeichnet.

Beispiele

Quadrat aus zwei Dreiecken mit Indexpuffer zeichnen

// Voraussetzung: device, context, pipeline existieren bereits
const vertices = new Float32Array([
  // x,    y
  -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]);

const vertexBuffer = device.createBuffer({
  size: vertices.byteLength,
  usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(vertexBuffer, 0, vertices);

const indexBuffer = device.createBuffer({
  size: indices.byteLength,
  usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(indexBuffer, 0, indices);

const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
  colorAttachments: [{
    view: context.getCurrentTexture().createView(),
    clearValue: { r: 0.1, g: 0.1, b: 0.1, a: 1 },
    loadOp: 'clear',
    storeOp: 'store',
  }],
});
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, vertexBuffer);
pass.setIndexBuffer(indexBuffer, 'uint16');
pass.drawIndexed(indices.length); // 6 Indizes = 2 Dreiecke
pass.end();
device.queue.submit([encoder.finish()]);

Instancing: dasselbe Mesh 100-mal zeichnen

// Im WGSL-Shader: @builtin(instance_index) idx : u32
// liefert pro Instanz den Wert 0..99, z. B. als Index in ein Offset-Array
pass.setPipeline(pipeline);
pass.setBindGroup(0, offsetsBindGroup);
pass.setVertexBuffer(0, cubeVertexBuffer);
pass.setIndexBuffer(cubeIndexBuffer, 'uint16');
pass.drawIndexed(36, 100); // 36 Indizes pro Würfel, 100 Instanzen

Mehrere Meshes in gemeinsamen Puffern mit firstIndex und baseVertex

// Alle Meshes liegen hintereinander in einem Vertex- und einem Indexpuffer
const meshes = [
  { name: 'Würfel',   indexCount: 36, firstIndex: 0,  baseVertex: 0 },
  { name: 'Pyramide', indexCount: 18, firstIndex: 36, baseVertex: 24 },
];

pass.setPipeline(pipeline);
pass.setVertexBuffer(0, sharedVertexBuffer);
pass.setIndexBuffer(sharedIndexBuffer, 'uint32');

for (const m of meshes) {
  // Die Indizes jedes Meshes beginnen bei 0 - baseVertex verschiebt sie
  pass.drawIndexed(m.indexCount, 1, m.firstIndex, m.baseVertex, 0);
}

// Wichtig · Fallstricke

  • Kein Exception-Wurf: Ungültige Aufrufe werfen keine JavaScript-Ausnahme. Das gilt zum Beispiel, wenn der Indexpuffer fehlt, ein Vertexpuffer fehlt oder firstIndex + indexCount über das Ende des Indexpuffers hinausgeht. Stattdessen wird der Encoder ungültig, und der Fehler erscheint erst bei finish() bzw. submit() als Validierungsfehler. Solche Fehler fängt man mit device.pushErrorScope('validation') / popErrorScope() oder über das Ereignis uncapturederror ab.
  • Indexformat: Das Format in setIndexBuffer() ("uint16" oder "uint32") muss zu den Daten passen. Bei Strip-Topologien ("triangle-strip", "line-strip") muss zusätzlich primitive.stripIndexFormat in der Pipeline übereinstimmen. Der Wert 0xFFFF bzw. 0xFFFFFFFF dient dort zum Neustart des Strips (primitive restart).
  • Ausrichtung: firstIndex wird in Indizes gezählt, nicht in Bytes. Der Offset in setIndexBuffer() wird dagegen in Bytes angegeben und muss ein Vielfaches der Indexgröße (2 oder 4 Byte) sein.
  • Für firstInstance ungleich 0 kann das Feature "indirect-first-instance" nötig sein, allerdings nur bei drawIndexedIndirect(). Bei direktem drawIndexed() ist es immer erlaubt.
  • Browser-Kompatibilität: WebGPU steht nur in sicheren Kontexten (HTTPS) zur Verfügung und ist noch nicht in allen Browsern bzw. auf allen Plattformen aktiv. Vor der Nutzung sollte man navigator.gpu prüfen und einen Fallback (z. B. WebGL) bereithalten.