Three.js 粒子系统学习小记:礼花效果实现

背景知识

在3D建模过程中,当我们需要创建很多细小的物体时,并不会一个个地创建这些物体,而是通过创建粒子,粒子可以模拟很多效果,例如烟花、火焰、雨滴、雪花、云朵等等。Three.js提供了各种的粒子系统创建方式。从官网例子的demo来看,可以总结分为两类,分别是Points和Sprite。

Points粒子系统的创建

首先看看threejs官网对Points的解释:

A class for displaying points. The points are rendered by the WebGLRenderer using gl.POINTS.

也就是说,points必须要通过WebGLRenderer渲染方式来创建(重点强调这个是因为官网的demo中粒子的创建一般通过两种方式WebGLRendererCanvasRenderer

renderer = new THREE.WebGLRenderer( );

points粒子系统的创建过程一般可以总结为三步:

1.创建一个几何对象Geometry(也可以是外部导入的模型),然后基于几何体自身的顶点集合geometry.vertices创建粒子(即 将网格转化为粒子),每个顶点将代表粒子系统中的每个粒子。

var geometry = new THREE.SphereGeometry( 500, 50, 50 );

2.创建粒子材质,Points对应PointsMaterial,来实现每个粒子的图案。实现的方式可以是加载图片纹理(demo地址)或者canvas纹理,又或者不采用纹理直接创建正方体粒子(demo地址)。

var material = new THREE.PointsMaterial( { ... } ) ;

3.通过以上两步的创建一个Points类的物体,这个物体则代表了整个粒子系统。

var mesh = new THREE.Points( geometry, material );

下图展示了将一个球形网格模型转化成一个球形粒子系统:

另外,也可以创建一个自定义的几何体为其添加顶点集合。

Sprite粒子系统的创建

A sprite is a plane that always faces towards the camera, generally with a partially transparent texture applied.

threejs官网如是说,sprite是一直面向camera的平面,并且我们可以用其创建基于屏幕坐标移动、定位和缩放的对象,而不影响三维场景中的其他物体(做到互不影响必须单独创建一个用于sprite对象的camera和render)。另外sprite对象还有一个特点是不能生成阴影。

创建过程:

1.创建粒子材质,如果渲染器是canvasRender则可以直接引用canvas画布,另外也可以加载图片纹理和canvas纹理。

var spriteMaterial = new THREE.SpriteMaterial( { ... } );

2.创建粒子

var sprite = new THREE.Sprite( spriteMaterial );

3.另外还可以为粒子设置position(如果将每个粒子设置为一个几何体的每个顶点,则效果和point粒子系统相似)。

sprite.position.x = 0;
sprite.position.y = 0;
sprite.position.z = 0;

4.为了方便控制,还可以将粒子加进同一个组内,变成一个粒子系统。

for ( var i = 0; i < len; i++ ) { //len表示粒子数目
    ...
    group.add( sprite );
}

粒子材质

先说说每个粒子材质图形的创建,一般是通过canvas描绘或通过加载图片的方式来格式化粒子:

1.直接引用画布,当通过CanvasRenderer渲染时:

renderer = new THREE.CanvasRenderer();

你可以直接在每个粒子的材质对象里直接引用HTML5画布。例如:

//画点
var PI2 = Math.PI * 2;
var program = function ( context ) {
    context.beginPath();
    context.arc( 0, 0, 0.5, 0, PI2, true );
    context.fill();
};
//为每个点附上材质
var material = new THREE.SpriteCanvasMaterial( {
    color: Math.random() * 0x808008 + 0x808080,
    program: program
} );

上文提到,points对象只能通过WebGLRender进行渲染,所以pointsmaterial和这种方式是无缘了。WebGLRender渲染时的粒子如果需要用canvas实现,则必须加多一步将canvas转化为纹理,在通过map属性加载进来。

2.加载图片纹理:

var textureLoader = new THREE.TextureLoader();
var sprite = textureLoader.load( "textures/sprites/snowflake.png" );
var material = new THREE.PointsMaterial( { size: size, map: sprite, blending: THREE.AdditiveBlending, depthTest: false, transparent : true } );

从上面的代码可以看到,粒子材质的属性还有很多,详情点击:pointsMaterial spriteMaterial

礼花效果实现

应用上面的知识点,小编做了一个礼花的小demo,礼花的展示效果大致分为三步:

  1. 绽放前,飞线动画向上运动。
  2. 绽放出球形礼花。
  3. 往下往外坠落礼花消失。

实现过程如下:

  1. 创建粒子,在这个例子中小编应用了canvasRender结合spriteMaterial,用canvas画出每个粒子(圆点),然后为它们设置初始位置都在同一点。
var PI2 = Math.PI * 2;
//画点
var program = function ( context ) {

    context.beginPath();
    context.arc( 0, 0, 0.5, 0, PI2, true );
    context.fill();

};
group = new THREE.Group();
scene.add( group );

for ( var i = 0; i < vl; i++ ) {
    //为每个点附上材质
    var material = new THREE.SpriteCanvasMaterial( {
        color: Math.random() * 0x808008 + 0x808080,
        program: program
    } );

    particle = new THREE.Sprite( material );
    particle.position.x = 0;
    particle.position.y = -500;
    particle.position.z = 0;
    particle.scale.x = particle.scale.y = Math.random() * 4 + 2;
    ...
    group.add(particle);
}
  1. 飞线动画实现

在每一帧的render中,判断每个粒子的y坐标小于一定值时,以不同的速度按照正弦曲线的轨迹向上运动,形成飞线动画的效果。

function fsin(x){     //正弦函数
    return 50*Math.sin(0.8*x*Math.PI/180);
}
delta = 10 * clock.getDelta();
var speed = 80;
delta = delta < 2 ? delta : 2;
var dur = new Date().getTime() - t1; 
if (dur < 1800) {     //控制飞线动画时间
    var k = 0;
    group.traverse(function(child) {
        if (child.position.y < 0) {
            child.position.y += delta * speed * Math.random();
            child.position.x = fsin(child.position.y);    
        }            
    });
}
renderer.render( scene, camera );
  1. 绽放效果是结合tweenMax实现的,在粒子初始化的时候,为了实现绽放时的球形效果,定义了一个球体几何体,得到球体的总顶点数作为粒子的总数,用tweenMax设置了每个粒子在绽放到最大时的位置,即了相应的球体的顶点位置再增减一些随机数,并设置随机的绽放时间,来达到错落有致的效果。
//创建一个球型用作最后的形状
var geometry = new THREE.SphereGeometry( 500, 50, 50 );
var vl = geometry.vertices.length;

for ( var i = 0; i < vl; i++ ) {

    ...
    particle = new THREE.Sprite( material );
    ...

    var timerandom = 1*Math.random();
    //为每个点加动画
    TweenMax.to(
        particle.position,
        timerandom,
        {x:geometry.vertices[i].x+(0.5-Math.random())*100,y:geometry.vertices[i].y+(0.5-Math.random())*100,z:geometry.vertices[i].z+Math.random()*100,delay:1.8,} 
    );

    group.add( particle );
}

4.礼花消失,同样是使用tweenMax

TweenMax.to(
          particle.position,
           2*timerandom,
          {y:'-600',z:'300',delay:1.8+timerandom,} 
 );

demo展示:地址

整个花的形状:地址

花的形状是用极坐标函数写的:传送门

项目代码地址:

https://github.com/kiroroyoyo/threejsexample/tree/master/particle

学习心得

  1. 在threejs的粒子系统中,每个粒子其实是一张图片或者一个canvas而不是3D的物体。
  2. 当粒子量级非常大时,可以用BufferGeometry来代替Geometry的顶点,因为它可以将数据存储在缓冲区中,减少数据传递到GPU的成本,同时因为在缓冲区,所以更适合静态的物体。
  3. threejs版本更新了很多次,粒子系统的创建也改了很多次名字,从THREE.ParticleSystem到THREE.PointCloud到THREE.Points,在学习实例时应注意。本文例子应用的是目前最新的r84。

参考文章:

  1. https://threejs.org/docs/
  2. https://www.solutiondesign.com/blog/-/blogs/webgl-and-three-js-particles
  3. 《Three.js开发指南》

谢谢阅读,如有问题请各位大大指正!

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏魂祭心

原 canvas绘制clock

5004
来自专栏我和未来有约会

Silverlight第三方控件专题

这里我收集整理了目前网上silverlight第三方控件的专题,若果有所遗漏请告知我一下。 名称 简介 截图 telerik 商 RadC...

4355
来自专栏转载gongluck的CSDN博客

cocos2dx 打灰机

#include "GamePlane.h" #include "PlaneSprite.h" #include "BulletNode.h" #include...

6906
来自专栏张善友的专栏

LINQ via C# 系列文章

LINQ via C# Recently I am giving a series of talk on LINQ. the name “LINQ via C...

2965
来自专栏大内老A

The .NET of Tomorrow

Ed Charbeneau(http://developer.telerik.com/featured/the-net-of-tomorrow/) Exciti...

38110
来自专栏pangguoming

Spring Boot集成JasperReports生成PDF文档

由于工作需要,要实现后端根据模板动态填充数据生成PDF文档,通过技术选型,使用Ireport5.6来设计模板,结合JasperReports5.6工具库来调用渲...

1.4K7
来自专栏跟着阿笨一起玩NET

c#实现打印功能

3592
来自专栏一个爱瞎折腾的程序猿

sqlserver使用存储过程跟踪SQL

USE [master] GO /****** Object: StoredProcedure [dbo].[sp_perfworkload_trace_s...

2820
来自专栏闻道于事

js登录滑动验证,不滑动无法登陆

js的判断这里是根据滑块的位置进行判断,应该是用一个flag判断 <%@ page language="java" contentType="text/html...

8368
来自专栏张善友的专栏

Silverlight + Model-View-ViewModel (MVVM)

     早在2005年,John Gossman写了一篇关于Model-View-ViewModel模式的博文,这种模式被他所在的微软的项目组用来创建Expr...

3268

扫码关注云+社区