Signatur
Beschreibung
Mit setVertexBuffer() bindest du innerhalb eines Render-Passes einen Puffer mit Vertex-Daten (z. B. Positionen, Farben, UV-Koordinaten) an einen bestimmten Slot. Der Slot-Index entspricht der Position im Array vertex.buffers der Render-Pipeline, die mit setPipeline() gesetzt wurde. Dort legst du auch fest, wie die Bytes im Puffer interpretiert werden (arrayStride, attributes, stepMode).
Die Bindung bleibt für alle folgenden Aufrufe von draw(), drawIndexed(), drawIndirect() und drawIndexedIndirect() im selben Pass bestehen, bis du den Slot neu belegst. So kannst du etwa mehrere Objekte mit unterschiedlichen Puffern nacheinander zeichnen, ohne die Pipeline zu wechseln.
Mit offset und size kannst du nur einen Teil des Puffers binden. Das ist nützlich, wenn mehrere Meshes oder Attribut-Ströme in einem großen Puffer liegen. Übergibst du null als buffer, wird die Bindung des Slots aufgehoben.
Der Puffer muss mit dem Verwendungs-Flag GPUBufferUsage.VERTEX erstellt worden sein. Fehler werden nicht sofort geworfen, sondern machen den Encoder ungültig. Sichtbar werden sie erst beim end() bzw. finish() als Validierungsfehler.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $slot Pflicht | number | Index des Vertex-Buffer-Slots (ganzzahlig, 0 bis device.limits.maxVertexBuffers - 1, meist 0–7). Entspricht dem Index in vertex.buffers des Pipeline-Deskriptors. |
|
| $buffer Pflicht | GPUBuffer | null | Der zu bindende Puffer mit Verwendung GPUBufferUsage.VERTEX. null hebt die Bindung des Slots auf. |
|
| $offset | number | 0 | Startposition in Bytes innerhalb des Puffers. Muss ein Vielfaches von 4 sein. |
| $size | number | buffer.size - offset | Anzahl der Bytes ab offset, die als Vertex-Daten gelten. Wenn du keinen Wert angibst, reicht der Bereich bis zum Pufferende. |
Rückgabewert
undefined).Beispiele
Dreieck mit einem Vertex-Buffer zeichnen
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = document.querySelector('canvas').getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({ device, format });
// x, y pro Vertex
const vertices = new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]);
const vertexBuffer = device.createBuffer({
size: vertices.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(vertexBuffer, 0, vertices);
const module = device.createShaderModule({ code: `
@vertex fn vs(@location(0) pos: vec2f) -> @builtin(position) vec4f {
return vec4f(pos, 0, 1);
}
@fragment fn fs() -> @location(0) vec4f { return vec4f(1, 0.4, 0, 1); }
`});
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module, entryPoint: 'vs',
buffers: [{ arrayStride: 8, attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x2' }] }]
},
fragment: { module, entryPoint: 'fs', targets: [{ format }] }
});
const encoder = device.createCommandEncoder();
const pass = encoder.beginRenderPass({
colorAttachments: [{ view: context.getCurrentTexture().createView(), loadOp: 'clear', storeOp: 'store', clearValue: { r: 0, g: 0, b: 0, a: 1 } }]
});
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, vertexBuffer); // Slot 0 = buffers[0]
pass.draw(3);
pass.end();
device.queue.submit([encoder.finish()]);
Getrennte Puffer für Position und Farbe (zwei Slots)
// Pipeline-Ausschnitt: zwei Buffer-Layouts
const vertexBuffers = [
{ arrayStride: 8, attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x2' }] }, // Slot 0
{ arrayStride: 12, attributes: [{ shaderLocation: 1, offset: 0, format: 'float32x3' }] } // Slot 1
];
// Im Render-Pass:
pass.setPipeline(pipeline);
pass.setVertexBuffer(0, positionBuffer);
pass.setVertexBuffer(1, colorBuffer);
pass.draw(3);
Mehrere Meshes aus einem gemeinsamen Puffer per Offset
// Ein großer Puffer enthält zwei Meshes hintereinander
const meshA = { offset: 0, size: 96, count: 12 }; // 12 Vertices à 8 Byte
const meshB = { offset: 96, size: 48, count: 6 };
pass.setPipeline(pipeline);
for (const mesh of [meshA, meshB]) {
pass.setVertexBuffer(0, sharedBuffer, mesh.offset, mesh.size);
pass.draw(mesh.count);
}
Instanzdaten in einem zweiten Slot
// Pipeline: buffers[1] hat stepMode: 'instance'
// Pro Instanz ein vec2f-Versatz (8 Byte)
const offsets = new Float32Array([-0.5, 0, 0, 0, 0.5, 0]);
const instanceBuffer = device.createBuffer({
size: offsets.byteLength,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST
});
device.queue.writeBuffer(instanceBuffer, 0, offsets);
pass.setVertexBuffer(0, vertexBuffer);
pass.setVertexBuffer(1, instanceBuffer);
pass.draw(3, 3); // 3 Vertices, 3 Instanzen
// Wichtig · Fallstricke
- Verwendungs-Flag: Ohne
GPUBufferUsage.VERTEXist der Aufruf ungültig. Wenn du Daten mitqueue.writeBuffer()hochladen willst, brauchst du zusätzlichCOPY_DST. - Ausrichtung:
offsetundsizemüssen Vielfache von 4 sein, undoffset + sizedarfbuffer.sizenicht überschreiten. - Slot-Limit:
slotmuss kleiner alsdevice.limits.maxVertexBufferssein (Standard: 8). - Fehler kommen verzögert: Ungültige Aufrufe werfen keine Ausnahme. Sie machen den Encoder ungültig, und der Fehler erscheint erst bei
end()bzw.finish(). Zum Debuggen eignen sichdevice.pushErrorScope('validation')undpopErrorScope(). - Bindung muss zur Pipeline passen: Jeder Slot, den die aktuelle Pipeline in
vertex.buffersbeschreibt, muss vordraw()gebunden sein. Außerdem muss der gebundene Bereich groß genug für die Anzahl der Vertices bzw. Instanzen sein. - Indexpuffer getrennt: Indizes bindest du nicht hier, sondern mit
setIndexBuffer(). - Kompatibilität: WebGPU ist nur in sicheren Kontexten (HTTPS) verfügbar. Die Unterstützung in Firefox und Safari ist noch nicht auf allen Plattformen vollständig. Prüfe vorher mit
if (!navigator.gpu), ob die API vorhanden ist. - Die Methode gibt es gleichnamig auch in
GPURenderBundleEncoder.