Skip to content

Using Instancing

Shawn Hargreaves edited this page Jun 16, 2016 · 4 revisions

Using Instancing

Overview

  • Allows apps to efficiently render the same piece of geometry multiple times in a scene
  • Works in OpenGL ES 2.0 (via an extension) and in OpenGL ES 3.0

Example

Some instanced trees

Without instancing, this scene would typically require 4 draw calls per tree (one for the leaves, one for the bark, one for the grass and one for the ground underneath). Since there are 7 trees, this means 7*4 = 28 draw calls in total.

With instancing, this scene can be rendered with just 4 draw calls.


Introduction

In graphics, "instancing" allows applications to efficiently render the same piece of geometry multiple times in a scene. Support for instancing is available in ANGLE (OpenGL ES 2.0) via the extension ANGLE_instanced_arrays, and natively in OpenGL ES 3.0+.

The OpenGL ES 2.0 extension adds two new drawing methods:

void glDrawArraysInstancedANGLE(enum mode, int first, sizei count, sizei primcount);

void glDrawElementsInstancedANGLE(enum mode, sizei count, enum type, const void *indices, sizei primcount);

They are analogous to glDrawArrays and glDrawElements. The key difference is that the geometry is drawn 'primcount' number of times. Each copy of the geometry is called an 'instance'.

To customize each instance of the geometry (e.g. to put each instance in a different position in the scene), the extension adds a concept of an attribute ‘divisor’. This is configured via a new method:

void glVertexAttribDivisorANGLE(uint index, uint divisor);

If you set a divisor N (N > 0) for a certain attribute, then the data for that attribute will move to the next element in the attribute's buffer after N instances of the geometry drawn. By default, or if you choose divisor 0, the attribute will select the appropriate element of the attribute's buffer for the each vertex of the geometry.

The OpenGL ES 3.0 methods are identical to the OpenGL ES 2.0 extension's methods, except they have the 'ANGLE' suffix removed. OpenGL ES 3.0 also adds support for the built-in vertex shader variable 'gl_InstanceID'.


Example Walkthrough

Let’s say that we want to draw 7 instanced trees like the picture above. We begin by drawing one tree using glDrawArrays. Our shaders use two attributes:

attribute vec4 aVertexPosition; // the position of each vertex in the tree
attribute vec2 aVertexTexCoord; // the texture coordinate of each vertex in the tree

To "instance" the tree, we add a third attribute to the shaders:

attribute vec4 aInstancedTreePosition; // the position of each tree in our scene

In the vertex shaders, we add aVertexPosition to aInstancedTreePosition and output it to gl_Position.

We then switch our drawing to use glDrawArraysInstancedANGLE instead of glDrawArrays, and we call glVertexAttribDivisorANGLE for the aInstancedTreePosition attribute with divisor 1. This tells ANGLE that the data for aInstancedTreePosition should be updated for each tree drawn.


Source Code

A sample for instancing can be found in /samples/angle/simple_instancing. This can be run by opening samples/samples.sln in Visual Studio.


Limitations on D3D11 Feature Level 9_3

On D3D11 Feature Level 9_3 (e.g. Windows Phone devices), applications cannot call glVertexAttribDivisorANGLE on the attribute in location 0. To work around this, you should rearrange the attribute definitions in your vertex shader to ensure that the attribute in location 0 has divisor 0.