In this lesson, we take a look at a simple way of animating vertices in WebGL by simply changing the values of vertices.
[00:00] Drawing static shapes and lines in WebGL is OK, but I'm sure you're much more interested in doing some animation. Let's start making things move. In general, in a 3D program, forms are built up using 3D vertices.
[00:13] Those forms are moved, rotated, or scaled using 3D matrix transformations. We'll get to that, but first, we're going to do something a bit simpler. We're going to change the vertices themselves. Rather than just having three predefined vertices, let's create a whole bunch.
[00:30] I'll create a vertexCount variable up at the top of the file, and set this to 5,000. Another thing we can do here is limit this example to two dimensions. Although WebGL is 3D by default, it can be used as a fast 2D renderer.
[00:44] In fact, that's what we've really done so far, manually setting all these Z values of the vertices to zero. We can force it to be 2D by default. We only have to supply X and Y values, and have that Z value automatically set to be zero.
[00:58] In createVertices, I'll get rid of this code, and create an empty array. I'll loop through vertexCount number of times, and push two values onto the array, one for X, one for Y. These will both be randomly assigned a value from -1 to +1.
[01:17] Know now that each vertex is defined by only two values. In the vertex shader, cords is specified as a vec4, but we're only passing in two values. When this is assigned to gl_Position, WebGL will be smart enough to put a zero in there for Z, just like it knows to insert a one for the W parameter.
[01:37] In the createVertices function, when we call gl.vertexAttribPointer, we're saying that each vertex has three values. We need to change that to tell it that it now has only two. I'll just set the drawing mode to gl.POINTS, and tell it that we have vertexCount number of points, instead of just three.
[02:00] We run this, and we see that we have 5,000. Let's animate them. In the draw function, I'll first loop through from zero to vertexCount times two, because that's how many individual values we have in the array, each vertex using two elements.
[02:18] I'm going to increment by two so I'm getting each group of XY coordinates. I could loop by one here because we're doing the same thing to both values, but this will make the next step a little bit easier. In that, I'm going to randomly change each coordinate by a small amount.
[02:35] I'll do this to vertices i for X, and i+1 for Y. This should cause each point to randomly move around a little bit on each frame. I'll add a call to requestAnimationFrame, passing in draw. This will cause the draw function to run over and over, synced with the browser's screen refresh.
[02:57] We run this, and nothing happens. The problem is we changed the vertices array in the JavaScript code, but that doesn't affect what WebGL sees. Remember that back in the createVertices function, we created a Float32Array with the vertices array.
[03:13] That's what we passed to gl.bufferData. This sends the vertex data into a vertex buffer on the GPU. If we want WebGL to have our updated vertex data, we need to send it over again. We can do this by duplicating the gl.bufferData call we made in createVertices, but this doesn't do anything either.
[03:32] That's because we unbound the buffer at the end of the createVertices function. When we say gl.bufferData, WebGL no longer knows what buffer we're talking about. We have two options here.
[03:46] We could rebind it just before this gl.bufferData call, and then unbind it again afterwards, or we could just not unbind it in the first place. Since this is a simple program, and we know we're going to be accessing the buffer continuously and forever, that seems like a pretty good solution.
[04:04] I'll just comment it out here. In a more complex application, you definitely want to do some better buffer management. Run it again, and we have jiggling points. You could, of course, build up more complex motion for the vertices, make them follow a path, or react to physics, or whatever else.
[04:22] We'll extend this example to include user interaction in the next video. While this works, the way we've coded it is not at all ideal. Look at that last parameter to gl.bufferData, gl.STATIC_DRAW. Remember that this is the buffer usage mode.
[04:38] Static draw means that we'll be using the data a lot, but not changing it often. Here, we're actually changing it many times per second. We should specify a different usage mode to reflect that. We'll set it to gl.DYNAMIC_DRAW.
[04:54] This mode means we intend to use the data a lot, and change it a lot. Once we've done that, we shouldn't really be calling gl.bufferData, but another method, gl.bufferSubData. This is used to update all or some subset of the data in the buffer.
[05:10] It takes the target type, which is again, gl.ARRAY_BUFFER, the offset, or where to start updating, which will be zero in this case, as we're updating all of the vertices, and the vertices array, wrapped in a Float32Array.
[05:24] Note that we don't need to specify the usage type again as it's already been set. We still have our animation. Realize here that while we are rewriting all of the vertex data, you could use this bufferSubData method to update as few as one or two vertices.
[05:39] Thus it could be very efficient if you're only updating a subset of your data. Play with this one for a while. You can change the point size, or change the color in the fragment shader, and get different effects.
[05:50] I'll stress again that this is not the most common type of animation in WebGL. Most often, you'll be using 3D transformation matrices to transforms sets of vertices via a vertex shader. We'll be covering that method of animation shortly.
Why the animation does not stop after one draw call?