Three.js拨云见雾(6)——动画和移动的摄像机

1,504 阅读3分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

动画

如果希望我们的场景动起来,那么首先要解决的问题是如何在特定的时间间隔重新渲染场景。考虑到动画的实现方案,我们可以选择setIntervalsetTimeout以及requestAnimationFrame三种。我们来看一下他们各自的特点以及为什么不使用setIntervalsetTimeout

一般用setInterval(fn, interval)/setTimeout(fn, interval)这两个方法会以指定的时间间隔触发指定的方法,这个两个方法的缺点是在触发指定方法时不会考虑当前正在发生的事情。即使动画没有显示或者隐藏在某个标签下,这个两个函数依然会触发指定的方法,这样会耗费某些资源。另外,这两个函数一旦被调用就会刷新屏幕,不管这个时刻对浏览器来说是不是恰当的时机,这也就意味着较高的CPU使用率和性能问题。

现代浏览器通过requestAnimationFrame函数为稳定而连续的渲染场景提供了良好的解决方案。通过这个函数,可以向浏览器提供一个回调函数。我们无须定义回调间隔,浏览器自行决定最佳回调时机,正常情况下是60fps。我们需要做的是在这个回调函数中在这个回调函数中完成一帧绘制操作,然后将剩下的工作交给浏览器,它负责使场景尽量高效和平顺地进行。使用requestAnimationFrame()方法实现的动画运行得更加平滑,对于CPU和GPU更加友好,而且不必担心渲染的时机问题。这个功能使用起来也非常简单:

function render(){
    renderer.render(scene, camera)
    requestAnimationFrame(render)
}

我们只需要在场景初始化完成后调用一次render()方法即可。在render()方法内部我们使用requestAnimationFrame()方法来触发下一次的场景渲染。这样,浏览器可以保证以合适的时间间隔(通常是60次每秒)执行render方法。

小结

我们可以通过改变物体的旋转、缩放、位置、材质、顶点、面以及其他所能想到的属性来实现复杂的动画。three.js会在下一次的render循环中渲染修改后的属性。

使用摄像机

three.js中提供了一些摄像机控件,使用这些控件,我们可以控制场景中的摄像机。这些控件在Three.js发布包中,在examples/js/controls目录中找到。这里会以OrbitControls(轨道控制器)为例,带大家快速的入门摄像机控件。

轨道控制器(OrbitControls)控件可以在特定的场景中模拟轨道中卫星,可以使用鼠标和键盘在场景中游走。

OrbitControls.js控件提供了一个构造函数THREE.OrbitControls(),执行构造函数THREE.OrbitControls()浏览器会同时干两件事,一是给浏览器定义了一个鼠标、键盘事件,自动检测鼠标键盘的变化,如果变化了就会自动更新相机的数据, 执行该构造函数同时会返回一个对象,可以给该对象添加一个监听事件,只要鼠标或键盘发生了变化,就会触发渲染函数。说了这么多,我们来看一下代码实现:

function render() { 
    //执行渲染操作
    renderer.render(scene,camera);
} 
render(); 
//创建控件对象 
let controls = new THREE.OrbitControls(camera,renderer.domElement);
//请求再次执行渲染函数render
requestAnimationFrame(render);
//已经通过requestAnimationFrame(render);周期性执行render函数,没必要再通过监听鼠标事件执行render函数
//controls.addEventListener('change', render);

如果代码中通过requestAnimationFrame()实现渲染器渲染方法render()的周期性调用,当通过OrbitControls操作改变相机状态的时候,没必要在通过controls.addEventListener('change', render)监听鼠标事件调用渲染函数,因为requestAnimationFrame()就会不停的调用渲染函数。