Indexed Geometry

How to 3D

Chapter 2: Triangles and Transformations

Indexed Geometry

With gl.LINES and gl.TRIANGLES, vertices are implicitly grouped into line segments or triangles. The graphics card connects vertices together based only on their sequence in the vertex buffer. Sometimes you want more explicit control over how vertices connect together.

Draw the square coded up here on paper and examine how it is formed out of triangles:

function initialize() {
  const positions = [
    // Triangle 0
    0, 0, 0,
    1, 0, 0,
    1, 1, 0,

    // Triangle 1
    0, 0, 0,
    1, 1, 0,
    0, 1, 0,
  ];

  attributes = new VertexAttributes();
  attributes.addAttribute('position', 6, 3, positions);
  // ...
}

function render() {
  // ...
  vao.drawSequence(gl.TRIANGLES);
  // ...
}
function initialize() {
  const positions = [
    // Triangle 0
    0, 0, 0,
    1, 0, 0,
    1, 1, 0,

    // Triangle 1
    0, 0, 0,
    1, 1, 0,
    0, 1, 0,
  ];

  attributes = new VertexAttributes();
  attributes.addAttribute('position', 6, 3, positions);
  // ...
}

function render() {
  // ...
  vao.drawSequence(gl.TRIANGLES);
  // ...
}

Because the rectangle is being drawn with gl.TRIANGLES, both triangles must include the shared vertices \(\begin{bmatrix}0&0&0\end{bmatrix}\) and \(\begin{bmatrix}1&1&0\end{bmatrix}\). We could eliminate this redundancy by switching to a triangle fan. However, triangle fans support only a very particular form of connectivity in which the triangles radiate out from the central vertex. A more general solution is to use indexed geometry. With indexed geometry, each vertex appears exactly once in the vertex buffer, with no redundancy. The square's vertex buffer has this logical view:

vertex index x y z
0 0 0 0
1 1 0 0
2 0 1 0
3 1 1 0

The vertices are then explicitly grouped in an index buffer, which is a list of triplets of vertex indices. This index buffer groups the four vertices into two triangles:

triangle index a b c
0 0 1 3
1 0 3 2

The indices are listed in counterclockwise order.

Indexed geometry requires a few changes to the code:

function initialize() {
  const positions = [
    0, 0, 0, // Vertex 0
    1, 0, 0, // Vertex 1
    0, 1, 0, // Vertex 2
    1, 1, 0, // Vertex 3
  ];

  const indices = [
    0, 1, 3, // Triangle 0
    0, 3, 2, // Triangle 1
  ];

  attributes = new VertexAttributes();
  attributes.addAttribute('position', 4, 3, positions);
  attributes.addIndices(indices);
  // ...
}
function initialize() {
  const positions = [
    0, 0, 0, // Vertex 0
    1, 0, 0, // Vertex 1
    0, 1, 0, // Vertex 2
    1, 1, 0, // Vertex 3
  ];

  const indices = [
    0, 1, 3, // Triangle 0
    0, 3, 2, // Triangle 1
  ];

  attributes = new VertexAttributes();
  attributes.addAttribute('position', 4, 3, positions);
  attributes.addIndices(indices);
  // ...
}

There's no longer any redundancy in positions. An array of indices has been defined and added to the VBO with the call to addIndices. On your paper, label each vertex with its index. Then observe how the indices of these vertices are listed in indices to form triangles.

To draw an indexed shape, we replace the drawSequence call with drawIndexed:

function render() {
  // ...
  vao.drawIndexed(gl.TRIANGLES);
  // ...
}
function render() {
  // ...
  vao.drawIndexed(gl.TRIANGLES);
  // ...
}

The addIndices and drawIndexed calls do no error checking to ensure that the indices are valid. If you pass out-of-bounds indices, you will likely get a blank screen and an error from WebGL.

Indexed geometry usually consumes less storage and results in fewer calls to the vertex shader. The savings must be weighed against the added cost of storing the indices.

← Sequenced TrianglesInterpolation →