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链接,本文完。


评论加载中...

Disqus提供评论支持,如果评论长时间未加载,请飞跃长城。