迄今为止我们绘制的3D场景都是静止的,死气沉沉啊有木有?现在我们可以 给它们注入点生命力了。
怎么画动画
如果有看过 之前 的 笔记 ,应该有同学留意到像这样的代码:
1 2 3 4 5 6 7 | function render() { requestAnimationFrame(render); controls.update(); renderer.render(scene, camera); } render(); |
没错 render 函数就是我们画动画的地方。因为我们调用了 requestAnimationFrame 函数将 render 注册为动画渲染回调函数,所以浏览器在认为画面需要重绘的时候就会 调用 render. 我们可以在 render 中微调各个对象的位置、形状、贴图坐标等,那么 连续的渲染结果就能构成动画了。
不过使用 requestAnimationFrame 有个微妙的问题: render 函数的调用时间间隔不 是固定的,为了让动画速度保持恒定,我们还需要根据每次调用的不同时间间隔计算 动画微调的幅度。 Three.js 提供了方便的 Clock 类用于支持此类操作:
1 2 3 4 5 6 7 8 9 10 11 | var clock = THREE.Clock(); function render() { requestAnimationFrame(render); var delta = clock.getDelta(); // .... 根据 delta 大小决定动画幅度 .... controls.update(); renderer.render(scene, camera); } |
这里取得的 delta 就是以秒为单位的间隔时间。
绵延不绝的地形
让我们从上次画的那个其实不怎么像的地形开始。我想要一个“低空飞过”的 动画效果,但是——如果大家有去看源码就会知道——这个地形 Mesh 只有 64x64 那么小,别说飞了,就是走也能走出去,除非我们一直原地画圈圈。
于是,我这样投机取巧了一下:
也就是说,移动的是地形,而不是我们的视角(视角一直静止在原点附近),而且 当地形快要罩不住原点的时候,我们生成一个 地形的镜像 ,将它放在地形移 动方向的后方。因为是镜像,所以能和原本的地形 Mesh “无缝连接”。然后,在视 角距离原本的地形 Mesh 足够远的时候,反正也看不到了,我们就可以将原来那个 地形移除以节省资源。
因为 Three.js 没有提供直接生成镜像的接口,所以这里我们对 Geometry 对象使 用了 Object3D.applyMatrix(...) ,直接对几何结构进行矩阵变换,变换矩阵 是这个样子的:
1 2 3 4 5 6 7 | var mirrorMatrix = new THREE.Matrix4(); mirrorMatrix.set( -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ); |
可见只是将 X 轴坐标反转而已。
纸飞机
为了体现场景的宏大(?),我决定在镜头前面画一只小小的纸飞机。为什么是纸 飞机?因为只有4个面很好画……
这里我用了 MeshBasicMaterial 的 vertexColors 选项,让材质颜色由每个顶点 单独确定,于是可以画出点简单的阴影效果——看过 上一篇笔记 源码的同学应该 也注意到了,地形的阴影也是这样搞出来的。