目标
- 让场景中的物体动起来
- 设置性能监视器
动起来的场景
1、动起来的原理
如何才能让场景的物体动起来?
利用现实例子来说明:我们看风景的时候,风景怎么动起来。
第一种方法:风景自己动起来。
第二种方法:我们自己动起来(应该说眼睛的移动)。
很好理解,这里代入一下,风景就是 Object 自身,也就是物体自己。我们的眼睛就相当于与 Camera。
2、渲染循环
关于物体移动还有一个关键的地方,就是要渲染物体运动的每个动作,才能显示给观看者,也就是需要不停的调用渲染器的 render() 函数。
如果改变了物体的位置或者颜色等属性,就必须重新调用 render() 函数,才能重新绘制新的场景到浏览器中。不然浏览器是不会显示改变后的图像的。
如果不断改变物体的属性,那么就需要不断的绘制新的场景,所以最好的方式就是让画面执行循环,不断调用 render 重绘,这个渲染就是 渲染循环,在游戏里也称为:游戏循环
为了实现循环,在 javascript 中,需要用到一个特殊函数:requestAnimationFrame
调用这个函数,是传递一个 callback 参数,则在下一个动画帧时,会调用 callback 这个函数了
1 2 3 4 |
function animate() { render(); requestAnimationFrame( animate ); // 填上这个函数 animate } |
这样就会不断调用 animate() 函数,而这个函数又会调用 requestAnimationFrame ,形成循环。在 render() 函数中不断 改变物体 或者 改变摄像机位置,并渲染他们,就能实现动画了。
3、我们动(相机动)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>moveCamera</title> <style type="text/css"> div#canvas-frame { border: none; cursor: pointer; width: 100%; height: 600px; background-color: #EEEEEE; } </style> <script src="js/Three.js"></script> </head> <body onload="threeStart();"> <div id="canvas-frame"></div> <script> var renderer, camera, scene, light; var geometry, material, mesh; var WIDTH = document.getElementById( 'canvas-frame' ).clientWidth, HEIGHT = document.getElementById( 'canvas-frame' ).clientHeight; function init() { // Init Renderer renderer = new THREE.WebGLRenderer({ antialias : true }); renderer.setSize( WIDTH, HEIGHT ); document.getElementById( 'canvas-frame' ).appendChild( renderer.domElement ); renderer.setClearColor( 0xFFFFFF, 1.0 ); // Init Camera camera = new THREE.PerspectiveCamera( 45, WIDTH / HEIGHT, 1, 10000 ); camera.position.set( 0, 1000, 0 ); camera.up.set( 0, 0, 1 ); camera.lookAt( 0, 0, 0 ); // Init Scene scene = new THREE.Scene(); scene.backgorund = new THREE.Color( 0x111111 ); // Init Light light = new THREE.AmbientLight( 0xFFFFFF ); light.position.set( 100, 100, 200 ); scene.add( light ); light = new THREE.PointLight( 0x00FF00 ); light.position.set( 0, 0, 300 ); scene.add( light ); // Init Object geometry = new THREE.CylinderGeometry( 100, 150, 400 ); material = new THREE.MeshLambertMaterial( { color: 0x0033FF } ); mesh = new THREE.Mesh( geometry, material ); mesh.position = new THREE.Vector3( 0, 0, 0 ); scene.add( mesh ); } function animation() { // rendener.renderClear(); camera.position.x = camera.position.x + 1; // 平移 renderer.render( scene, camera ); requestAnimationFrame( animation ); } function threeStart() { init(); animation(); } </script> </body> </html> |
重点在于在 animation() 函数中,加入了:
1 |
camera.position.x = camera.position.x + 1; |
相机向右移动,相当于物体向左移动了。
4、风景动(物体动)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>测试空白页</title> <style type="text/css"> div#canvas-frame { border: none; cursor: pointer; width: 100%; height: 600px; background-color: #EEEEEE; } </style> <script src="js/Three.js"></script> </head> <body onload="threeStart();"> <div id="canvas-frame"></div> <script> var renderer, camera, scene, light; var geometry, material, mesh; var WIDTH = document.getElementById( 'canvas-frame' ).clientWidth, HEIGHT = document.getElementById( 'canvas-frame' ).clientHeight; function init() { // Init Renderer renderer = new THREE.WebGLRenderer({ antialias : true }); renderer.setSize( WIDTH, HEIGHT ); document.getElementById( 'canvas-frame' ).appendChild( renderer.domElement ); renderer.setClearColor( 0xFFFFFF, 1.0 ); // Init Camera camera = new THREE.PerspectiveCamera( 45, WIDTH / HEIGHT, 1, 10000 ); camera.position.set( 0, 1000, 0 ); camera.up.set( 0, 0, 1 ); camera.lookAt( 0, 0, 0 ); // Init Scene scene = new THREE.Scene(); scene.backgorund = new THREE.Color( 0x111111 ); // Init Light light = new THREE.AmbientLight( 0xFF0000 ); light.position.set( 100, 100, 200 ); scene.add( light ); light = new THREE.PointLight( 0x00FF00 ); light.position.set( 0, 0, 300 ); scene.add( light ); // Init Object geometry = new THREE.CylinderGeometry( 100, 150, 400 ); material = new THREE.MeshLambertMaterial( { color: 0x0033FF } ); mesh = new THREE.Mesh( geometry, material ); mesh.position = new THREE.Vector3( 0, 0, 0 ); scene.add( mesh ); } function animation() { // rendener.renderClear(); mesh.position.x -= 1; renderer.render( scene, camera ); requestAnimationFrame( animation ); } function threeStart() { init(); animation(); } </script> </body> </html> |
基本只有一行代码不一样:
1 |
mesh.position.x -= 1; |
使物体不断向左移动。
性能监视器
关于性能:在 3D 渲染的世界里,有一个指标直接衡量性能,就是帧数的概念。
帧数:图形处理器每秒钟能够刷新的次数,通常用 FPS(Frames Per Second)来表示。
当物体快速移动时,人眼所看到的影像消失后,人眼仍能够保持其影像 1/24 秒左右的图像,这种现象被称为 视觉暂留现象 ,是人眼具有的一种性质。
毫无疑问,帧数越高,画面感就会越好,所以大多数游戏都有着超过 30FPS 的指标。
1、性能监视器 Stats
在 Threejs 中,性能由一个性能监视器来管理。
2、使用 Stats
在 Threejs 中,性能监视器 Stats 被封装在一个类中,这个类就叫 Stats,封装在 threejs/libs/stats.min.js 中,下面是使用方法的伪代码(不实用但有参考价值):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var stats = new Stats(); stats.setMode(1); // 0: fps, 1: ms stats.domElement.style.position = 'absolute'; stats.domElement.style.left = '0px'; stats.domElement.style.right = '0px'; document.body.appendChild( stats.domElement ); setInterval( functino() { stats.begin(); // 你的每一帧代码 stats.end(); }, 1000 / 60 ); |
通过执行代码前调用 stats.begin() 函数,执行完后调用 stats.end() 函数,就可以统计出这段代码执行的平均帧数了。
3、实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>stats</title> <style type="text/css"> div#canvas-frame { border: none; cursor: pointer; width: 100%; height: 600px; background-color: #EEEEEE; } </style> <script src="js/three.min.js"></script> <script src="js/threejs/libs/stats.min.js"></script> <script> var renderer, camera, scene, light; var stats; var geometry; function init() { var WIDTH = document.getElementById( 'canvas-frame' ).clientWidth; HEIGHT = document.getElementById( 'canvas-frame' ).clientHeight; // Init Stats stats = new Stats(); stats.domElement.style.position = 'absolute'; stats.domElement.style.left = '0px'; stats.domElement.style.right = '0px'; document.getElementById( 'canvas-frame' ).appendChild( stats.domElement ); // Init Renderer renderer = new THREE.WebGLRenderer({ antialias : true }); renderer.setSize( WIDTH, HEIGHT ); document.getElementById( 'canvas-frame' ).appendChild( renderer.domElement ); renderer.setClearColor( 0xFFFFFF, 1.0 ); // Init Camera camera = new THREE.PerspectiveCamera( 45, WIDTH / HEIGHT, 1, 10000 ); camera.position.set( 0, 1000, 0 ); camera.up.set( 0, 0, 1 ); camera.lookAt( 0, 0, 0 ); // Init Scene scene = new THREE.Scene(); scene.backgorund = new THREE.Color( 0x111111 ); // Init Light light = new THREE.AmbientLight( 0xFFFFFF ); light.position.set( 100, 100, 200 ); scene.add( light ); light = new THREE.PointLight( 0x00FF00 ); light.position.set( 0, 0, 300 ); scene.add( light ); // Init Object geometry = new THREE.CylinderGeometry( 100, 150, 400 ); material = new THREE.MeshLambertMaterial( { color: 0x0033FF } ); mesh = new THREE.Mesh( geometry, material ); mesh.position = new THREE.Vector3( 0, 0, 0 ); scene.add( mesh ); } function animation() { // rendener.renderClear(); // camera.position.x += 1; mesh.position.x -= 1; renderer.render( scene, camera ); requestAnimationFrame( animation ); stats.update(); } function threeStart() { init(); animation(); } </script> </head> <body onload="threeStart();"> <div id="canvas-frame"></div> </body> </html> |
首先创建一个 stats 对象:
1 |
stats = new Stats(); |
添加到 HTML 网页中:
1 2 3 4 5 |
stats.domElement.style.position = 'absolute'; stats.domElement.style.left = '0px'; stats.domElement.style.top = '0px'; |
调用 stats.update() 函数来统计时间和帧数:
1 |
stats.update(); |