此文上接WebGL系列文章,第一篇是基础概念。 前一篇是三维相机,如果没读建议从那开始。
如何使用WebGL实现动画?
事实上这个问题和WebGL并不相关,如果你想实现动画只需要利用JavaScript 随着时间改变一些值然后绘制。
我们可以看看之前使用的动画的例子。
*var fieldOfViewRadians = degToRad(60);
*var rotationSpeed = 1.2;
*requestAnimationFrame(drawScene);
// 绘制场景
function drawScene() {
* // 每一帧旋转一点
* rotation[1] += rotationSpeed / 60.0;
...
* // 下一帧继续调用 drawScene
* requestAnimationFrame(drawScene);
}
这是结果
这里还有一些小细节,上方的代码中有rotationSpeed / 60.0
,
我们除以 60.0 是假设浏览器一秒钟调用 60 次 requestAnimationFrame。
这并不是一个有效的假设,也许用户使用的是一个低效的设备,比如旧的智能手机。 或者用户在后台运行了一个复杂的程序。有多种原因可能导致浏览器不是60帧每秒。 也许在 2020 年所有的设备每秒 240 帧,也许用户是一个游戏玩家,在CRT显示器上每秒 90 帧。
你可以在这个例子中发现问题
在上方的例子中我们想让所有的 'F' 以相同的速度转动。 中间的 'F' 全速运行,并且帧率独立。左边和右边的模拟的是当前设备 1/8 的速率, 左边的不是帧率独立,右边的是帧率独立。
注意到由于左边的使用的计数,所以帧率变慢不能跟上正常速度。 右边的尽管以 1/8 的帧率运行,它还是能和中间的保持相同的速度。
让帧率独立的方法是计算两帧之间的时间,用这个时间计算当前帧的动画量。
首先需要得到时间,幸好页面加载调用 requestAnimationFrame
时会传入时间。
我觉得如果时间的单位是秒会简单一些,由于 requestAnimationFrame
传递给我们的时间是毫秒
(千分之一秒),我们需要将它乘以 0.001 得到秒。
所以就可以像这样计算出时间差
*var then = 0;
requestAnimationFrame(drawScene);
// 绘制场景
*function drawScene(now) {
* // 转换时间为秒
* now *= 0.001;
* // 减去上一次的时间得到时间差
* var deltaTime = now - then;
* // 记住这次时间
* then = now;
...
一旦有了 deltaTime
就可以决定每秒转多少个单位,例子中是 1.2 意思就是每秒转动 1.2 弧度。
大约是 1/5 圈或者说 5 秒可以转一整圈,不管帧率是多少。
* rotation[1] += rotationSpeed * deltaTime;
这是这个版本
除非你的机器比较慢,不然可能看不出和该页第一个的区别, 但是如果不使动画帧率独立,不同的用户可能会得到预期之外的结果。
接下来讲如何使用纹理。