webgl之3d动画
之前的几篇文章都是静态的,而这里主要介绍如何使物体动起来,并且学会使用性能监视器来监测性能。
而如果要让物体动起来,实际上我们是有两种方法的,第一种是让物体真的动起来,另外一种是让摄像机动起来这样物体相对来说也就动起来了。另外,实际上在让物体动起来的过程中,我们是不断通过调用 renderer.render(scene, camera)这个函数实现的,那么怎么才能不断调用这个函数呢?这就需要用到 requestAnimationFrame函数了,这个函数接受一个函数作为参数,并且会在每秒内调用60次,那么最终屏幕就会在一秒内渲染60次,这样就可以形成动画了。
一、物体运动
首先,我们先让物体动起来,如下所示,就是一个让物体运动起来的动画:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>three.js</title>
<style>
* {
margin: ;
padding: ;
}
</style>
<script src="./three.js"></script>
</head> <body>
<script>
var scene = new THREE.Scene(); var axes = new THREE.AxesHelper();
scene.add(axes); var camera = new THREE.PerspectiveCamera(, window.innerWidth/window.innerHeight, , );
camera.position.x = ;
camera.position.y = ;
camera.position.z = ;
camera.up.x = ;
camera.up.y = ;
camera.up.z = ; // camera.up.z = 1, 所以渲染出来的结果是z轴朝上。
camera.lookAt(scene.position); var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0x111111);
renderer.setSize(window.innerWidth, window.innerHeight); var cubeGeometry = new THREE.CubeGeometry(, , );
var meshCube = new THREE.MeshBasicMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, meshCube);
cube.position.x = ;
cube.position.y = ;
cube.position.z = ;
scene.add(cube);
document.body.append(renderer.domElement); var isDestination = false; function animation() {
var interval = ;
if (!isDestination) {
cube.position.x = cube.position.x + interval;
} else {
cube.position.x = cube.position.x - interval;
}
if (cube.position.x == ) {
isDestination = true;
}
if (cube.position.x == ) {
isDestination = false;
}
renderer.render(scene, camera);
requestAnimationFrame(animation);
} animation();
</script>
</body>
</html>
即首先创建场景,然后创建坐标轴并加入到场景中,接着创建相机,注意相机所接受的参数比较多,且相机需要指定position位置以及up位置,且使用lookAt函数,接下来创建一个渲染器,指定背景颜色和宽、高,然后创建一个物体,最后需要将渲染器加入到document.body中,接着是一个动画,然后运行即可。但是,我们可以发现虽然上面代码完成了,但是封装的不好,我们可以尝试着将其用函数封装,如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>three.js</title>
<style>
* {
margin: ;
padding: ;
}
</style>
<script src="./three.js"></script>
</head> <body>
<script>
var scene;
function initScene() {
scene = new THREE.Scene();
} var axes;
function initAxes() {
axes = new THREE.AxesHelper();
scene.add(axes);
} var camera;
function initCamera() {
camera = new THREE.PerspectiveCamera(, window.innerWidth/window.innerHeight, , ); camera.position.x = ;
camera.position.y = ;
camera.position.z = ; camera.up.x = ;
camera.up.y = ;
camera.up.z = ; camera.lookAt(scene.position);
} var renderer;
function initRenderer() {
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x111111);
document.body.append(renderer.domElement);
} var cube;
function initObject() {
var cubeGeometry = new THREE.CubeGeometry(, , );
var meshCube = new THREE.MeshBasicMaterial({color: 0xff0000});
cube = new THREE.Mesh(cubeGeometry, meshCube);
cube.position.x = ;
cube.position.y = ;
cube.position.z = ;
scene.add(cube);
} var isDestination = false;
function animation() {
var interval = ;
var destination = ;
var direction = "y"; if (!isDestination) {
cube.position[direction] += interval;
} else {
cube.position[direction] -= interval;
}
if (cube.position[direction] == destination) {
isDestination = true;
}
if (cube.position[direction] == ) {
isDestination = false;
}
renderer.render(scene, camera);
requestAnimationFrame(animation);
} function threeStart() {
initScene();
initAxes();
initCamera();
initRenderer();
initObject();
animation();
} threeStart();
</script>
</body>
</html>
如上所示,通过函数封装,程序的逻辑性更好了一些,并且仅仅暴露了比如scene、camera、axes、renderer等必要的变量,而其他的变量不会对全局造成污染,而最后的animation函数,我们定义了direction为"x",这样就可以通过这里的修改控制cube运动的坐标轴了,这一点利用的就是JavaScript调用属性的特点。最后我们统一将初始化调用函数写在了threeStart中,这样,就可以通过threeStart函数调用开启这个项目了,最终得到的效果如下所示:

二、相机运动
如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>three.js</title>
<style>
* {
margin: ;
padding: ;
}
</style>
<script src="./three.js"></script>
</head> <body>
<script>
var scene;
function initScene() {
scene = new THREE.Scene();
} var axes;
function initAxes() {
axes = new THREE.AxesHelper();
scene.add(axes);
} var camera;
function initCamera() {
camera = new THREE.PerspectiveCamera(, window.innerWidth/window.innerHeight, , ); camera.position.x = ;
camera.position.y = ;
camera.position.z = ; camera.up.x = ;
camera.up.y = ;
camera.up.z = ; camera.lookAt(scene.position);
} var renderer;
function initRenderer() {
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x111111);
document.body.append(renderer.domElement);
} var cube;
function initObject() {
var cubeGeometry = new THREE.CubeGeometry(, , );
var meshCube = new THREE.MeshBasicMaterial({color: 0xff0000});
cube = new THREE.Mesh(cubeGeometry, meshCube);
cube.position.x = ;
cube.position.y = ;
cube.position.z = ;
scene.add(cube);
} var isDestination = false;
function animation() { var interval = ;
if (!isDestination) {
camera.position.x -= interval;
camera.position.y -= interval;
camera.position.z -= interval;
} else {
camera.position.x += interval;
camera.position.y += interval;
camera.position.z += interval;
} if (camera.position.x == ) {
isDestination = true;
}
if (camera.position.x == ) {
isDestination = false;
} renderer.render(scene, camera);
requestAnimationFrame(animation);
} function threeStart() {
initScene();
initAxes();
initCamera();
initRenderer();
initObject();
animation();
} threeStart();
</script>
</body>
</html>
这里的思路也非常简单,就是给camera做了一个动画,效果如下所示:

ok,到这里,我们就了解了使得场景运动起来的两种方法,但是,我们应该如何监测他们的性能呢,下面来说一说。
三、性能评估
在3D世界里,我们经常用的是帧数来评价性能,帧数就是图形处理器每秒钟可以刷新的次数,用fps(Frames Per Second)来表示,毫无疑问,帧数越高,那么动画就会越流畅,为了监视FPS,就需要学习性能监视器。通常,我们使用stats(github地址/stats.min.js)进行监视,而stats是非常有名的JavaScript性能监视库,它提供了一些简单的信息来帮助你检测你的代码性能:
- FPS 即上一秒渲染的帧数(Frames),显然这个值越大,说明上一秒钟内刷新的次数越多,那么性能越好。
 - MS 即渲染一帧需要的毫秒数,显然,MS越小越好。
 - MB 是分配内存的字节数。
 - CUSTOM是指用户自定义的面板。
 
如下所示:

那么如何使用呢?如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>stats</title>
<script src="./stats.js"></script>
</head>
<body>
<script>
var stats = new Stats();
stats.showPanel( ); // 0: fps, 1: ms, 2: mb, 3+ custom
document.body.appendChild( stats.dom ) function animate() {
stats.begin(); // monitored code gose here stats.end(); requestAnimationFrame(animate); } animate();
</script>
</body>
</html>
即首先引入stats.js库文件,然后实例化得到一个stats,接着通过stats.showPanel()显示我们希望看到的面板,然后添加到dom中,最后,我们就可以将动画相关代码写在stats.begin()和stats.end()之间,打开浏览器就可以看到效果了。
另外,stats.js库的github中说明,可以直接将下面的js代码粘贴到任何html网页的script标签中,然后就可以使用了,如下:
<script>
(function () {
var script = document.createElement('script'); script.onload = function () {
var stats = new Stats(); document.body.appendChild(stats.dom); requestAnimationFrame(function
loop() { stats.update(); requestAnimationFrame(loop) });
}; script.src = './stats.js'; document.head.appendChild(script);
})()
</script>
这段代码非常好理解,就是创建了一个script标签,然后在script标签加载完成之后创建了stats实例,接着添加到dom中,最后检测性能。注意,其中设定了script标签的src属性,根据需要自己设定即可。
这样,我们只要将上述代码加到之前我们写的动画页面中即可看到评估性能了,如下:

如上所示,我们可以看到在左上角就进行了性能检测,上一秒的FPS为60。而后面括号内的(60 - 60)说明FPS的变化范围在60 - 60之间,因为我们使用的requestAnimationFrame,所以FPS几乎始终为60。
四、游戏循环、帧循环、渲染循环
游戏循环、帧循环和渲染循环都是同一种循环。
我国早期的葫芦娃动画片,这种动画片不是3D引擎做的,而是画家做的剪纸,然后拍下的照片进行播放,然后再通过连续翻动的形式,就可以形成动画了,比如在抖音上,我们可以看到有人在本子上每一页都画了画,然后不停的翻动本子,然后这个动画场景就出来了。
游戏循环就是如下所示的方式:
while (true)
{
updateStatus();
draw();
}
即先更新状态,然后再draw就可以了。 更新状态的步骤中主要做的就是比如控制游戏中人物的位置以及背景的颜色等等。
而draw()的主要步骤就是先清空场景,然后再绘制,显然,我们是不可能在不清空这一帧就画下一帧。
通用的代码形式如下所示:
function animate() {
     render();
     requestAnimationFrame( animate );
}
其中render过程就做了更新状态以及draw的工作,然后在使用requestAnimationFrame调用这个函数,达成这个死循环,但是不会卡死,因为计算机只是会在空闲的时候来执行这些函数。并且这里requestAnimationFrame是每秒更新60帧。
注意:一般电影的播放在24帧每秒就可以做到不卡,而游戏需要做到48 - 60帧每秒才会不卡。这是因为电影的胶片会有一定的曝光,导致残影的存在,这样就可以使得其在24帧每秒保持不卡。
五、动画引擎 tween.js
上面介绍了通过移动相机或者移动物体来产生动画的效果,但是如果动画再复杂一些,我们用原生实现就回去比较麻烦,所以这里我么可以借助动画引擎tween.js来实现动画效果,它一般是和three.js结合比较紧密的。
tween.js的github中star在5k多一些,也是比较流行的,使用起来也比较方便,我们可以在tween.js的raw中下载,然后通过script标签引入就可以使用了,如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>tween</title>
<script src="./tween.js"></script>
</head>
<body>
<script>
var box = document.createElement('div');
box.style.setProperty('background-color', '#008800');
box.style.setProperty('width', '100px');
box.style.setProperty('height', '100px');
document.body.appendChild(box); function animate(time) {
requestAnimationFrame(animate);
TWEEN.update(time);
}
requestAnimationFrame(animate); var coords = {
x: ,
y:
}; var tween = new TWEEN.Tween(coords)
.to({x: , y: }, )
.easing(TWEEN.Easing.Quadratic.Out)
.onUpdate(function () {
box.style.setProperty('transform', 'translate(' + coords.x + 'px, ' + coords.y + 'px)');
})
.start();
</script>
</body>
</html>
如上所示,我们创建了一个div,然后创建了animate动画,接着我们指定原点在(0, 0)处,最后我们构建了一个Tween对象,然后指定它在1000ms时移动到(300, 200)坐标处,且指定了移动的动画方式,然后在update时不断改变其位置。
ok,这一部分的内容就到这里了。
webgl之3d动画的更多相关文章
- 基于HTML5的WebGL经典3D虚拟机房漫游动画
		
第一人称在 3D 中的用法要参考第一人称在射击游戏中的使用,第一人称射击游戏(FPS)是以第一人称视角为中心围绕枪和其他武器为基础的视频游戏类型 ; 也就是说,玩家通过主角的眼睛来体验动作.自从流派开 ...
 - 8个经典HTML5 3D动画赏析
		
HTML5技术已经越来越被我们所接受,特别是一些3D的动画特效.本文介绍的8个HTML5 3D动画并没有特别华丽的界面,但是比较实用,涉及到3D图片.3D图表.3D按钮等方面,一起来看看. 1.HTM ...
 - 8款效果惊艳的HTML5 3D动画
		
1.HTML5 WebGL水面水波荡漾特效 之前已经向各位分享过一款很逼真的HTML5水波荡漾特效,效果还算不错.今天再向大家分享一款更加给力的HTML5水波动画,画面上是一个大水池,水池底部是一颗大 ...
 - 超给力的HTML5 3D动画欣赏及源码下载
		
HTML5有着非常巨大的魅力,尤其是CSS3和Cavnas,可以帮助页面渲染得非常炫酷.值得一提的是,利用HTML5的3D特性可以帮助你更加方便地在网页上实现3D动画特效.本文分享的这些HTML5 3 ...
 - WebGL展示3D房屋内景
		
原文地址:WebGL展示3D房屋内景 由于生活和工作上的原因,从年前开始一直到处奔波,没有太多的时间去关注和学习WebGL图形学相关的技术, 不过陆陆续续都有学习使用blender进行3D建模 ...
 - 基于 HTML5 WebGL 的 3D 风机 Web 组态工业互联网应用
		
基于 HTML5 WebGL 的 3D 风机 Web 组态工业互联网应用 前言 在目前大数据时代背景之下,数据可视化的需求也变得越来越庞大,在数据可视化的背景之下,通过智能机器间的链接并最终将人机链接 ...
 - 分享一个WebGL开发的网站-用JavaScript + WebGL开发3D模型
		
这张图每位程序员应该都深有感触. 人民心目中的程序员是这样的:坐在电脑面前噼里啪啦敲着键盘,运键如飞. 现实中程序员是这样的:编码5分钟,调试两小时. 今天我要给大家分享一个用WebGL开发的网站,感 ...
 - 基于 H5 和 webGL 的 3d 智慧城市
		
前言 中共中央.国务院在今年12月印发了<长江三角洲区域一体化发展规划纲要>(下文简称<纲要>),并发出通知,要求各地区各部门结合实际认真贯彻落实. <纲要>强调, ...
 - [MISSAJJ原创] UITableViewCell移动及翻转出现的3D动画效果[58同城cell移动效果]
		
2015-11-20 很喜欢在安静的状态, 听着音乐,敲着键盘, 和代码们浓情对话, 每一份代码的积累, 都让自己觉得很充实快乐!Y(^_^)Y. 看到58同城app的cell有动画移动出现的特效,很 ...
 
随机推荐
- 移动赋值运算符(c++11)
			
1.概念 1)移动赋值运算符是一个重载的赋值运算符,参数为自身类的右值引用,返回值自身类的左值引用,由于不抛出任何异常,用noexcept指定(如果定义在类的外面,那么定义也要用noexcept指定) ...
 - Java编程从头开始---老妪能解
			
思想导向: 今天想要分享的是最基础的东西就是如何写一个简单的代码,很多人都是小白,需要的其实并不是很高端的理论,框架和思维模式啊,设计方法啊,这些对于一个新人来说实在是好高骛远,说的那么高端,结果要学 ...
 - 工作总结(二):Web Design
			
PHP框架:CakePHP 前端框架:Bootstrap Payment Data Transfer:https://developer.paypal.com/docs/classic/paypal- ...
 - c#+web下载文件夹
			
最近公司在做工程项目,实现文件夹下载. 网上找了很久,发现网上的代码都有相似的问题,不过最终还是让我找到了一个符合的项目. 工程: 进行项目文件夹下载功能分析,弄清楚文件夹下载的原理,提供的数据支持. ...
 - redis的repl-ping-slave-period和repl-ping-replica-period
			
网上很多Redis方面的文章,会涉及到repl-ping-slave-period和repl-ping-replica-period这两个重要参数,从一些中文解释来看,意思差不多,即:SLAVE周期性 ...
 - MIT Molecular Biology 笔记6  转录的调控
			
视频 https://www.bilibili.com/video/av7973580?from=search&seid=16993146754254492690 教材 Molecular ...
 - spoj high
			
matrixtree定理裸体,学了行列式的n^3解法,(应该是能应用于所有行列式): 代码是参考某篇题解的... #include<iostream> #include<cstrin ...
 - mysql_变量
			
set names gbk; 变量 变量分为两种:系统变量,自定义变量 系统变量:系统定义好的,大部分情况用户不需要使用系统变量,如autocommit,auto_increment_incremen ...
 - 11.DataGrid分页
			
前台页面: 后台程序:
 - HDU3480_区间DP平行四边形优化
			
HDU3480_区间DP平行四边形优化 做到现在能一眼看出来是区间DP的问题了 也能够知道dp[i][j]表示前 i 个节点被分为 j 个区间所取得的最优值的情况 cost[i][j]表示从i ...