three.js入门及飘雪实现
three.js是一个JavaScript的3D库,可用于构建WebGL应用。2011年写过一篇《一个二进制的Web新世界》,从那时起就一直断断续续有接触这方面的资料。本文是three.js的一些入门,并用其实现一个飘雪的效果。
其实,对于web应用来说,3D一直以来都较少使用,不过2016年被称为是VR年,作为网页端的WebVR也趁此机会迅速发展了起来,而其核心库正是three.js。three.js的官方文档和Demo目前已经较为完善,由于一直在迭代演进,API的变化还是比较明显的,所以很多网上的例子相当多已经过时,市面上的书目也是如此。当然THREE渲染器运行后,会在控制台输出版本信息,当前是THREE.WebGLRenderer 83dev
,后面的列子也会以当前的这个版本作为环境。这里也不讨论浏览器的兼容性,借以现代浏览器为准。
在页面上引用three.min.js就可以开始3D之旅了。three.js的主要几个概念分别是scene(场景),camera(相机),renderer(渲染器):
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000);
renderer = new THREE.WebGLRenderer();
renderer.render(scene, camera);
场景容纳物体(点,平面,几何体),相机决定了视角,渲染器使之呈现。物体被赋予各种各样的材质和光照,从而呈现出不同的表面和阴影。three.js中有非常多的几何体,材质以及光源种类,有茫茫多的参数,但这些认知信息都并不重要,需要看的时候可以慢慢研究。直接进入飘雪吧~
飘雪的效果并不少见,每逢圣诞节这个需求就一直存在。目前在网上使用最多的,是Seb Lee-Delisle所写的版本。通常营销活动时间短暂有往往需要噱头,拿来主义司空见惯。去年京东搞圣诞活动使用的也是这个版本的轻微改良版。不过这个版本的飘雪在当前的代码环境是无法运行的,API改变了太多。所以如果单单使用three.js 46配合这个版本的飘雪效果也着实不错。
但这里,我会重写它并作为一个three.js入门的例子。
预先准备好雪花的图片,这里是个32x32的png带羽化的圆的填充,为了便于观察所以是黑色底,实际最后是透明底色。其实用六边形雪花也很好看,个人还是更喜欢这种柔和点的雪。
首先是一些将会使用到变量,基本看名字都知道是干嘛的。而函数randomRange()
返回给定上下限的随机值。对于飘雪这种粒子,随机值无处不在。
var container, camera, scene, renderer, particles = [], material, map, textureLoader;
var halfX = window.innerWidth / 2;
var halfY = window.innerHeight / 2;
var mouseX = 0,
mouseY = 0;
var fallSpeen = 2;
var amount = 1000;
function randomRange(min, max) {
return ((Math.random() * (max - min)) + min);
}
在初始化时(init()
中),先准备好HTML容器,并设置好场景和相机:
container = document.createElement('div');
document.body.appendChild(container);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 2000);
camera.position.z = 100;
scene = new THREE.Scene();
读取雪花图片作为材质:
textureLoader = new THREE.TextureLoader();
map = textureLoader.load('snow-32.png');
material = new THREE.SpriteMaterial({map: map});
然后使用雪花材质在场景中创建指定数量的雪花粒子。指定粒子随机分布充满在一个2000x2000x2000的立方体中,并对粒子做随机缩放。指定每个粒子的下落速度v
,并随机偏移每个粒子的下落矢量。最后将粒子添加到场景中。
for ( i = 0; i < amount; i ++ ){
var particle = new THREE.Sprite( material );
var randomScale = randomRange(10,20)
particle.position.x = randomRange(-1000, 1000);
particle.position.y = randomRange(-1000, 1000);
particle.position.z = randomRange(-1000, 1000);
particle.scale.x = particle.scale.y = particle.scale.z = randomScale;
particle.v = new THREE.Vector3(0, -fallSpeen, 0);
particle.v.z = (1 * randomRange(-1, 1));
particle.v.x = (1 * randomRange(-1, 1));
particles.push(particle);
scene.add( particle );
}
初始化渲染器并指定参数,初始化完毕,然后开始动画animate()
renderer = new THREE.WebGLRenderer({alpha: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
animate();
在animate()
中,通过requestAnimationFrame()
方法持续调用render()
来反复渲染形成动画。
function animate() {
requestAnimationFrame(animate);
render();
}
而在render()
方法中,则只需要不断更新所有雪花的位置即可。通过判断边界值,使得所有雪花一直处在相机监视下的“立方体”容器中。
function render() {
for(var i = 0; i < particles.length; i++){
var particle = particles[i];
var pp = particle.position;
pp.add(particle.v);
if(pp.y < -1000) pp.y = 1000;
if(pp.x > 1000) pp.x = -1000;
else if(pp.x < -1000) pp.x = 1000;
if(pp.z > 1000) pp.z=-1000;
else if(pp.z < -1000) pp.z = 1000;
}
renderer.render( scene, camera );
}
three.js的大部分动画就有类似这样的流程,初始化场景,相机,添加物体,指定光照和材质,然后渲染。如果需要动画则重复渲染更新,通过requestAnimationFrame()
方法浏览器已经可以获得比setInterval()
好的多的流畅度,通过WebGL渲染器替代Canvas渲染器,可以使用显卡而非CPU来渲染场景,获得更低的资源占用。而这些就是three.js的最基本的内容。
最后再放一下DEMO链接,本文完。