Signatur
Beschreibung
Die Methode createVertexArray() erzeugt ein leeres Vertex Array Object (VAO). Ein VAO speichert den gesamten Zustand, der beschreibt, wie Vertex-Daten aus Buffern gelesen werden: alle Aufrufe von vertexAttribPointer(), vertexAttribIPointer(), enableVertexAttribArray(), vertexAttribDivisor() sowie den gebundenen ELEMENT_ARRAY_BUFFER.
Der typische Ablauf: VAO erstellen, mit bindVertexArray(vao) binden, Buffer und Attribute einmalig konfigurieren und anschließend das VAO lösen (bindVertexArray(null)). Beim Zeichnen reicht dann ein einziger bindVertexArray()-Aufruf, um die komplette Konfiguration wiederherzustellen. Das spart viele API-Aufrufe pro Frame und macht den Code übersichtlicher, besonders bei vielen Objekten mit unterschiedlichen Vertex-Layouts.
In WebGL 1 war diese Funktionalität nur über die Erweiterung OES_vertex_array_object (createVertexArrayOES()) verfügbar. In WebGL 2 ist sie fester Bestandteil der API.
Rückgabewert
WebGLVertexArrayObject. Ist der Kontext verloren (context lost), wird null zurückgegeben.Beispiele
VAO für ein Dreieck einrichten und zeichnen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) throw new Error('WebGL 2 wird nicht unterstützt');
const vsSource = `#version 300 es
in vec2 a_position;
void main() { gl_Position = vec4(a_position, 0.0, 1.0); }`;
const fsSource = `#version 300 es
precision mediump float;
out vec4 outColor;
void main() { outColor = vec4(1.0, 0.4, 0.1, 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);
// VAO erstellen und binden – ab jetzt wird Attribut-Zustand darin gespeichert
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]), gl.STATIC_DRAW);
const loc = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
gl.bindVertexArray(null); // Konfiguration abgeschlossen
// Zeichnen: ein Bind genügt
gl.useProgram(program);
gl.bindVertexArray(vao);
gl.drawArrays(gl.TRIANGLES, 0, 3);
console.log(gl.isVertexArray(vao));
Mehrere Meshes mit eigenem VAO und Index-Buffer
// Annahme: gl (WebGL2) und program existieren, Attribut 'a_position' an Location 0
gl.bindAttribLocation(program, 0, 'a_position');
gl.linkProgram(program);
const createMesh = (vertices, indices) => {
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
// Der Index-Buffer wird ebenfalls im VAO gespeichert
const ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
gl.bindVertexArray(null);
return { vao, vbo, ibo, count: indices.length };
};
const quad = createMesh([-0.9, -0.9, -0.1, -0.9, -0.1, -0.1, -0.9, -0.1], [0, 1, 2, 0, 2, 3]);
const tri = createMesh([0.1, 0.1, 0.9, 0.1, 0.5, 0.9], [0, 1, 2]);
const render = () => {
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
for (const mesh of [quad, tri]) {
gl.bindVertexArray(mesh.vao);
gl.drawElements(gl.TRIANGLES, mesh.count, gl.UNSIGNED_SHORT, 0);
}
gl.bindVertexArray(null);
requestAnimationFrame(render);
};
requestAnimationFrame(render);
Aufräumen und Fallback auf WebGL 1
const canvas = document.createElement('canvas');
const gl2 = canvas.getContext('webgl2');
let createVAO, bindVAO, deleteVAO;
if (gl2) {
createVAO = () => gl2.createVertexArray();
bindVAO = (vao) => gl2.bindVertexArray(vao);
deleteVAO = (vao) => gl2.deleteVertexArray(vao);
} else {
const gl1 = canvas.getContext('webgl');
const ext = gl1?.getExtension('OES_vertex_array_object');
if (!ext) throw new Error('Keine VAO-Unterstützung');
createVAO = () => ext.createVertexArrayOES();
bindVAO = (vao) => ext.bindVertexArrayOES(vao);
deleteVAO = (vao) => ext.deleteVertexArrayOES(vao);
}
const vao = createVAO();
bindVAO(vao);
// ... Attribute konfigurieren ...
bindVAO(null);
// Nicht mehr benötigte VAOs explizit freigeben
deleteVAO(vao);
console.log(gl2 ? gl2.isVertexArray(vao) : 'WebGL 1');
// Wichtig · Fallstricke
- Erst binden, dann konfigurieren: Ein frisch erstelltes VAO ist leer. Attribut-Aufrufe wirken nur auf das aktuell gebundene VAO – wer
bindVertexArray(vao)vergisst, verändert das Standard-VAO (null). ARRAY_BUFFERist nicht Teil des VAO-Zustands: Gespeichert wird nur, welcher Buffer beim Aufruf vonvertexAttribPointer()gebunden war. DerELEMENT_ARRAY_BUFFERdagegen wird direkt im VAO gespeichert – ihn also nicht lösen, solange das VAO noch gebunden ist.- Nach der Einrichtung lösen:
bindVertexArray(null)verhindert, dass späterer Code versehentlich ein VAO verändert. - Speicher: VAOs werden nicht zuverlässig vom Garbage Collector freigegeben; mit
deleteVertexArray()explizit löschen. - Kontextverlust: Nach
webglcontextlostliefert die Methodenull; alle VAOs müssen nachwebglcontextrestoredneu erstellt werden. - Kompatibilität: Nur mit einem
webgl2-Kontext verfügbar (alle aktuellen Browser, Safari ab 15). Für WebGL 1 die ErweiterungOES_vertex_array_objectnutzen.