Start · Sprachen · JavaScript · Referenz · WebGLVertexArrayObject

WebGLVertexArrayObject

Schnittstelle

Repräsentiert ein Vertex-Array-Objekt (VAO) in WebGL 2, das die komplette Vertex-Attribut-Konfiguration samt Index-Puffer bündelt und per <code>bindVertexArray()</code> umschaltbar macht.

seit JavaScript WebGL 2 (Baseline: alle modernen Browser seit 2021 Kategorie: webapi

Signatur

interface WebGLVertexArrayObject extends WebGLObject

Beschreibung

Ein Vertex-Array-Objekt (VAO) speichert den Zustand, den man sonst vor jedem Zeichenaufruf neu setzen müsste: welche Vertex-Attribute aktiviert sind (enableVertexAttribArray()), wie sie aus welchem Puffer gelesen werden (vertexAttribPointer(), vertexAttribIPointer()), Instanzierungs-Teiler (vertexAttribDivisor()) sowie den gebundenen ELEMENT_ARRAY_BUFFER. Das Objekt selbst ist undurchsichtig – es hat keine eigenen Eigenschaften oder Methoden, sondern dient als Handle für den WebGL-Kontext.

Erzeugt wird ein VAO mit WebGL2RenderingContext.createVertexArray(), aktiviert mit bindVertexArray(vao) und freigegeben mit deleteVertexArray(vao). Mit isVertexArray(vao) lässt sich prüfen, ob es ein gültiges, bereits gebundenes VAO ist. Einen Konstruktor gibt es nicht – new WebGLVertexArrayObject() wirft einen TypeError.

Typischer Einsatz: Pro Mesh (Würfel, Terrain, Sprite-Batch) wird einmalig beim Laden ein VAO eingerichtet. Im Render-Loop genügt dann ein einziger bindVertexArray()-Aufruf vor drawArrays()/drawElements(). Das spart viele API-Aufrufe, reduziert Fehler durch „vergessenen“ Zustand und verbessert die Performance spürbar bei vielen Objekten.

Wichtig: Der ARRAY_BUFFER-Binding-Punkt selbst gehört nicht zum VAO-Zustand – gespeichert wird nur, welcher Puffer zum Zeitpunkt von vertexAttribPointer() gebunden war. Der ELEMENT_ARRAY_BUFFER hingegen ist Teil des VAO.

Rückgabewert

Typ
WebGLVertexArrayObject
Beschreibung
Instanzen entstehen ausschließlich über gl.createVertexArray() (bzw. ext.createVertexArrayOES() in WebGL 1). Der Rückgabewert kann null sein, wenn der Kontext verloren ist.

Beispiele

VAO erzeugen und Vertex-Attribute einrichten

const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl2');
if (!gl) throw new Error('WebGL 2 wird nicht unterstützt');

// Einfaches Shader-Programm
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, `#version 300 es
in vec2 aPos;
void main() { gl_Position = vec4(aPos, 0.0, 1.0); }`));
gl.attachShader(program, compile(gl.FRAGMENT_SHADER, `#version 300 es
precision mediump float;
out vec4 color;
void main() { color = vec4(1.0, 0.5, 0.0, 1.0); }`));
gl.linkProgram(program);

// VAO anlegen und binden – ab jetzt wird der 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, 'aPos');
gl.enableVertexAttribArray(loc);
gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);

gl.bindVertexArray(null); // Einrichtung abschließen

// Zeichnen: nur noch Programm + VAO binden
gl.useProgram(program);
gl.bindVertexArray(vao);
gl.drawArrays(gl.TRIANGLES, 0, 3);
console.log(vao instanceof WebGLVertexArrayObject, gl.isVertexArray(vao));
true true

Mehrere Meshes im Render-Loop umschalten (mit Index-Puffer)

// Annahme: gl (WebGL2) und program mit Attribut 'aPos' existieren bereits
const createMesh = (positions, 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(positions), gl.STATIC_DRAW);
  gl.enableVertexAttribArray(0);
  gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);

  // ELEMENT_ARRAY_BUFFER wird 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.1, -0.1, 0.1, -0.1, 0.9, -0.9, 0.9], [0, 1, 2, 0, 2, 3]);
const tri = createMesh([0.1, -0.9, 0.9, -0.9, 0.5, -0.1], [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 WebGL-1-Fallback über Erweiterung

const getVaoApi = (gl) => {
  if (typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext) {
    return {
      create: () => gl.createVertexArray(),
      bind: (v) => gl.bindVertexArray(v),
      remove: (v) => gl.deleteVertexArray(v),
    };
  }
  const ext = gl.getExtension('OES_vertex_array_object');
  if (!ext) return null;
  return {
    create: () => ext.createVertexArrayOES(),
    bind: (v) => ext.bindVertexArrayOES(v),
    remove: (v) => ext.deleteVertexArrayOES(v),
  };
};

const gl = document.createElement('canvas').getContext('webgl2')
  ?? document.createElement('canvas').getContext('webgl');
const vaoApi = getVaoApi(gl);

const vao = vaoApi.create();
vaoApi.bind(vao);
// ... Attribute einrichten ...
vaoApi.bind(null);

// Beim Entfernen des Meshes Ressourcen freigeben
vaoApi.remove(vao);
console.log('VAO gelöscht');
VAO gelöscht