This post is a continuation of a series of posts about WebGL. The first started with fundamentals. and the previous was about 3D cameras. If you haven't read those please view them first.
How do we animate something in WebGL?
Actually this isn't specific to WebGL but generally if you want to animate something in JavaScript you need to change something over time and draw again.
We can take one of our previous samples and animate it as follows.
*var fieldOfViewRadians = degToRad(60);
*var rotationSpeed = 1.2;
*requestAnimationFrame(drawScene);
// Draw the scene.
function drawScene() {
* // Every frame increase the rotation a little.
* rotation[1] += rotationSpeed / 60.0;
...
* // Call drawScene again next frame
* requestAnimationFrame(drawScene);
}
And here it is
There's a subtle problem though. The code above has a
rotationSpeed / 60.0
. We divided by 60.0 because we assumed the browser
will respond to requestAnimationFrame 60 times a second which is pretty common.
That's not actually a valid assumption though. Maybe the user is on a low-powered device like an old smartphone. Or maybe the user is running some heavy program in the background. There are all kinds of reasons the browser might not be displaying frames at 60 frames a second. Maybe it's the year 2020 and all machines run at 240 frames a second now. Maybe the user is a gamer and has a CRT monitor running at 90 frames a second.
You can see the problem in this example
In the example above we want to rotate all of the 'F's at the same speed. The 'F' in the middle is running full speed and is frame rate independent. The one on the left and the right are simulating if the browser was only running at 1/8th max speed for the current machine. The one on the left is NOT frame rate independent. The one on the right IS frame rate independent.
Notice because the one on the left is not taking into account that the frame rate might be slow it's not keeping up. The one on the right though, even though it's running at 1/8 the frame rate it is keeping up with the middle one running at full speed.
The way to make animation frame rate independent is to compute how much time it took between frames and use that to calculate how much to animate this frame.
First off we need to get the time. Fortunately requestAnimationFrame
passes
us the time since the page was loaded when it calls us.
I find it easiest if we get the time in seconds but since the requestAnimationFrame
passes us the time in milliseconds (1000ths of a second) we need to multiply by 0.001
to get seconds.
So, we can then compute the delta time like this
*var then = 0;
requestAnimationFrame(drawScene);
// Draw the scene.
*function drawScene(now) {
* // Convert the time to seconds
* now *= 0.001;
* // Subtract the previous time from the current time
* var deltaTime = now - then;
* // Remember the current time for the next frame.
* then = now;
...
Once we have the deltaTime
in seconds then all our calculations can be in how
many units per second we want something to happen. In this case
rotationSpeed
is 1.2 which means we want to rotate 1.2 radians per second.
That's about 1/5 of a turn or in other words it will take about 5 seconds to
turn around completely regardless of the frame rate.
* rotation[1] += rotationSpeed * deltaTime;
Here's that one working.
You aren't likely to see a difference from the one at the top of this page unless you are on a slow machine but if you don't make your animations frame rate independent you'll likely have some users that are getting a very different experience than you planned.
Next up how to apply textures.