VertexArray

How to 3D

Chapter 1: Points and Lines

VertexArray

The in variables of a vertex shader must be bound to the vertex buffers. In WebGL, this binding is called a vertex array object (VAO). The abbreviation—pronounced vow by some—is apt because a VAO is a marriage between the VertexAttributes and the ShaderProgram. As you might guess, managing a VAO requires low-level operations whose yuck we will put behind an abstraction.

Create the file lib/vertex-array.ts and copy in this code:

import {ShaderProgram} from './shader-program.js';
import {VertexAttributes} from './vertex-attributes.js';

export class VertexArray {
  attributes: VertexAttributes;
  vertexArray: WebGLVertexArrayObject;
  isBound: boolean;

  constructor(program: ShaderProgram, attributes: VertexAttributes) {
    this.isBound = false;
    this.vertexArray = gl.createVertexArray()!;
    this.attributes = attributes;

    gl.bindVertexArray(this.vertexArray);
    for (let attribute of attributes) {
      let location = program.getAttributeLocation(attribute.name);
      if (location < 0) {
        console.debug(`${attribute.name} is not used in the shader.`);
      } else {
        gl.bindBuffer(gl.ARRAY_BUFFER, attribute.buffer);
        gl.vertexAttribPointer(location, attribute.ncomponents, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(location);
      }
    }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.indexBuffer);

    this.unbind();
  }

  bind() {
    gl.bindVertexArray(this.vertexArray);
    this.isBound = true;
  }

  destroy() {
    gl.deleteVertexArray(this.vertexArray);
  }

  unbind() {
    gl.bindVertexArray(null);
    this.isBound = false;
  }

  drawSequence(mode: number) {
    gl.drawArrays(mode, 0, this.attributes.vertexCount);
  }

  drawIndexed(mode: number) {
    gl.drawElements(mode, this.attributes.indexCount, gl.UNSIGNED_INT, 0);
  }
}
import {ShaderProgram} from './shader-program.js';
import {VertexAttributes} from './vertex-attributes.js';

export class VertexArray {
  attributes: VertexAttributes;
  vertexArray: WebGLVertexArrayObject;
  isBound: boolean;

  constructor(program: ShaderProgram, attributes: VertexAttributes) {
    this.isBound = false;
    this.vertexArray = gl.createVertexArray()!;
    this.attributes = attributes;

    gl.bindVertexArray(this.vertexArray);
    for (let attribute of attributes) {
      let location = program.getAttributeLocation(attribute.name);
      if (location < 0) {
        console.debug(`${attribute.name} is not used in the shader.`);
      } else {
        gl.bindBuffer(gl.ARRAY_BUFFER, attribute.buffer);
        gl.vertexAttribPointer(location, attribute.ncomponents, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(location);
      }
    }
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, attributes.indexBuffer);

    this.unbind();
  }

  bind() {
    gl.bindVertexArray(this.vertexArray);
    this.isBound = true;
  }

  destroy() {
    gl.deleteVertexArray(this.vertexArray);
  }

  unbind() {
    gl.bindVertexArray(null);
    this.isBound = false;
  }

  drawSequence(mode: number) {
    gl.drawArrays(mode, 0, this.attributes.vertexCount);
  }

  drawIndexed(mode: number) {
    gl.drawElements(mode, this.attributes.indexCount, gl.UNSIGNED_INT, 0);
  }
}

The marriage ceremony is short and sweet, consisting only of a constructor call:

vao = new VertexArray(shaderProgram, attributes);
vao = new VertexArray(shaderProgram, attributes);

Add this assignment after creating the shader program in main.ts. Declare it as a global with this statement:

let vao: VertexArray;
let vao: VertexArray;

To draw a model, you must activate or bind both the shader program and the VAO and then tell the GPU how to assemble the vertices from the vertex buffers into geometric primitives.

This code draws the vertices as individual points:

shaderProgram.bind();
vao.bind();
vao.drawSequence(gl.POINTS);
vao.unbind();
shaderProgram.unbind();
shaderProgram.bind();
vao.bind();
vao.drawSequence(gl.POINTS);
vao.unbind();
shaderProgram.unbind();

Add it to the end of the render function.

When you run your application, do you see two dots? The dots may be too small to see. On some browsers, you can increase their size by adding this line to main in the vertex shader, near the assignment to gl_Position:

gl_PointSize = 10.0;
gl_PointSize = 10.0;

Once you can see the points, try the following experiments:

We're rendering stuff! The rest of the course is just complicating the stuff with more vertices, colors, and motion.

← Shader ProgramVector3 →