16. 发射粒子-产生梦幻的效果

16. 发射粒子-产生梦幻的效果

本教程翻译自官方教程Particles,各位也可以进入本站镜像站点查看

本文目录

  1. 粒子Particles
  2. 创建粒子
    1. 粒子系统ParticleSystem
    2. 粒子预热
    3. 粒子纹理
    4. 粒子发射
    5. 粒子局部空间local space
    6. 世界中心偏移
    7. 位置和分布
  3. 简单粒子示例
  4. 粒子系统配置微调
    1. 生命周期Lifetime
    2. 体积大小Size
    3. 粒子颜色Particle Colors
    4. 混合模式blending
    5. 发射频率Rates
    6. 发射方向Direction
    7. 重力Gravity
    8. 旋转相关Rotation
    9. 速度Speed
    10. 速度随时间变化
    11. 随时间变化的速度极限
    12. 阻力系数Drag factor
    13. 发射速率随时间变化
    14. 初始体积随时间变化
    15. 坡度渐变Ramp gradients
    16. 对齐Alignment
  5. 可调整设置的粒子示例
  6. 形状发射器
    1. 单点发射器Point
    2. 立方体发射器Box
    3. 球体发射器Sphere
    4. 半球发射器Hemisphere
    5. 圆柱体发射器Cylinder
    6. 圆锥体发射器Cone
    7. 任意形状发射器Mesh
    8. 自定义发射器Custom
    9. 粒子的噪声纹理
  7. GPU粒子渲染
    1. 随机纹理
    2. 停止GPU粒子系统
    3. 不支持的功能
    4. Playground示例
  8. Snippet服务调用
  9. 下一步
  10. 延伸阅读
  11. 基础-难度L1
  12. 中级-难度L2
  13. 贡献者

粒子Particles

    本章将讨论BabylonJS中的粒子系统。粒子通常是一些小的精灵图,用于模拟难以复制的现象,例如:火焰、烟雾、水或者抽象的视觉效果,类似魔法闪光、仙尘。这些效果都是通过在一个区域不断的发射粒子来实现的,自从Babylon 3.2版本开始,新增了特殊的例子发射器,可以约束例子在一定的形状范围内,例如立方体、球体或者圆锥体。当然,我们也能写自定义函数来控制粒子和发射区域。

    GPU粒子是粒子家族的新成员,可以在支持GPU粒子的浏览器下使用,可以大大提高粒子的性能,性能越高,能够同时存在屏幕上的粒子就越多,效果就越炫酷。


粒子示意图

Playground 粒子实例

创建粒子

粒子系统ParticleSystem

    想要表演上例的魔法特技,我们首先要创建一个ParticleSystem对象:

var particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene);

    第二个参数表示粒子系统的粒子容量,即同时存在于3d空间中的例子数量,超出的话多余粒子会主动消失。粒子系统还需要设置一个纹理,以便于粒子能够可见,此外,我们还需要设置粒子发射器的位置以及粒子从起点开始延伸的范围。

    请注意我们也可以使用粒子助手ParticleHelper来创建默认配置的粒子系统: BABYLON.ParticleHelper.CreateDefault(emitter)

    设置完成后,我们可以启动粒子系统:

particleSystem.start();

    如果要停止粒子发射:

particleSystem.stop();

    如果想要延时启动粒子系统,可以设置 particleSystem.startDelay = 3000 ,这个值以毫秒作为单位。

    你也能用另一种方法来延时启动粒子系统,particleSystem.start(3000) ,如果也设置了startDelay属性,则前者会覆盖后者的属性。

    请注意,如果使用 stop 方法来停止粒子系统时,新的粒子将不会产生,但是已经发射出的粒子还会存在一段时间,具体时间根据设置来决定。加入我们想要立即清空粒子系统所有的已发射粒子,则可以调用 reset 方法,即 particleSystem.reset()

    我们还可以设置粒子系统的运行时间,设置后粒子系统将在规定的时间内自动停止,但是就想上面说到的,粒子系统停止后,新粒子不产生,但已经产生的粒子会持续一段时间。自动停止的代码如下:

particleSystem.targetStopDuration = 5;

    一旦停止了粒子系统,我们还可以设置 disposeOnStop 的属性为true,来自动销毁它。这对于创建一个 一次性粒子系统 非常有用,可以与 targetStopDuration 属性来配合实现。

particleSystem.disposeOnStop = true;

粒子预热

    从Babylon.js v3.3版本开始,我们可以指定一个预热周期,以确保粒子系统在开始渲染以前,就直接达到某个渲染状态。因为粒子根据发射速率和速度等原因,有一个持续增多的过程,假如我们想省略掉中间过程,直接看到粒子达到最大容量时的状态,这个预热周期非常有用。

    为了设置预热周期,我们需要设置两个属性:

  • system.preWarmCycles: 获取或设置一个值,是指在首次渲染之前必须执行多少个循环(或帧)(必须在启动粒子系统之前设置该值)。 默认值为0(即无预热)。
  • system.preWarmStepOffset: 获取或设置一个值,是指在预热模式下使用的 时间步长 乘数(默认为1倍),可以理解为一个加速度,如果我们要预热2000个循环(帧),这就可能会出现一个延时,所以我们设置为100倍,那么就会用20个循环的时间达到渲染2000个循环的效果。

    我们可以这样设置预热周期:

system.preWarmCycles = 100;
system.preWarmStepOffset = 5;
system.start();

    以上代码将执行100次粒子循环,并且以平时5倍的速度来进行渲染。越想要更多的循环,粒子系统启动得越慢,所以我们需要设置一个步长来减少循环的时间。但是请记住,假如粒子的生命周期小于时间步长,那么太大的 时间步长preWarmStepOffset 会带来问题。

Playground 一个粒子系统预热功能的例子

粒子纹理

    采用一个纹理到粒子中,如下图所示:

    可以设置sparticleTexture属性:

particleSystem.particleTexture = new BABYLON.Texture("PATH TO IMAGE", scene);

    我们还可以应用蒙版到纹理上,让改变纹理的颜色或者透明度:

particleSystem.textureMask = new BABYLON.Color4(0.1, 0.8, 0.8, 1.0);

    产生的效果如下图所示:

    如果想发射出多个不同的粒子纹理,那么可以用多个粒子系统来实现,粒子系统可以指定相同的发射对象,这样就像同一个例子系统发射出来的效果。

粒子发射

    粒子发射器可以指定一个点vector3,也可以指定一个物体,这时候物体的位置就作为发射器的位置。

particleSystem.emitter = new BABYLON.Vector3(-1, 2, 3);

var source = BABYLON.Mesh.CreateBox("source", 1.0, scene);
particleSystem.emitter = source;//设置emitter发射器

粒子局部空间local space

    如果发射器设置为了物体,则可以通过设置 ** particleSystem.isLocal = true** 来让粒子在生成在物体的局部空间中(所以旋转和位移能够改变整个粒子系统的发射方向)。

请注意MeshParticleEmitter形状发射器不支持GPU粒子,形状发射器是啥请继续看下文

Playground 粒子局部空间演示

世界中心偏移

    从Babylon.js v4.0开始,我们能够为粒子设置一个相对世界坐标的位置偏移:

particleSystem.worldOffset = new BABYLON.Vector3(100, 20, -453);

    以上代码使用了世界中心偏移来移动粒子(通常这个属性与相机相关,当我们需要固定相机在原点时,可以用来调整粒子发射器的偏移量)

位置和分布

    粒子从发射器中扩散开来,默认是处于一个立方体形状的内部,其体积可以根据发射器的位置设置下、左、前角和上、右、后脚来确定。以上设置由粒子的minEmitBox和maxEmitBox属性来决定。

particleSystem.minEmitBox = new BABYLON.Vector3(-2, -3, 4); 
particleSystem.maxEmitBox = new BABYLON.Vector3(4, 2, 3); 

    立方体形状能够沿着坐标的方向折叠为一条直线,例如X轴:

particleSystem.minEmitBox = new BABYLON.Vector3(-1, 0, 0); 
particleSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 0); 

简单粒子示例

    现在我们已经学会创建一个例子系统了,但是这肯定不够,做不出炫酷的效果,下面的例子简单展示了一些粒子在空间中扩散开来,然后再消失:

Playground 小范围扩散的例子

Playground 大范围扩散的例子

    我们接下来将学习设置更多的属性,来让粒子变得更加的炫酷有趣,请继续往下读哦

粒子系统配置微调

    下面我们将开始学习如何调整粒子的一些设置,例如:生命周期、体积、颜色,以及粒子的发射速率、方向、重力等等。而且我们还可以通过这些设置,影响粒子的旋转、移动速度,还能让粒子组成一个特定形状。可以在下面的章节找到对应的Playground示例,自己也可以尝试更改其中一些参数,以便于更好的学习粒子系统。

生命周期Lifetime

    一个粒子在从发射出来到消失,这段时间就是一个粒子的生命周期,单位是秒。在生命周期的末尾,粒子会消失,然后粒子系统会重新发射出新的粒子,以此不断往复来形成不间断的粒子效果。而且更进一步,生命周期也可以被设置为随机时间,在一个最大和最小值之间随机变动。

// Life time of each particle (random between...)
particleSystem.minLifeTime = 0.3;
particleSystem.maxLifeTime = 1.5;

    从Babylon.js v3.3版本开始,我们能定义粒子系统的停止时间,也就是说可以同时启动多个粒子系统,然后让它们在不同时间停止发射,以此来实现很多特殊效果。我们可以设置 system.targetStopDuration = 0.5 ,表示粒子系统0.5秒后停止发射,设置了 targetStopDuration 后我们就可以定义粒子生命周期的渐变时间了:

particleSystem.addLifeTimeGradient(0, 0.5);
particleSystem.addLifeTimeGradient(1, 0);

     addLifeTimeGradient 第一个参数定义渐变位置(0表示粒子系统开始,1表示粒子系统停止)。第二个参数是粒子寿命,单位是秒。上述代码意味着在粒子系统开始发射时,粒子将接收设置为0.5的LifeTime生命时间。当系统接近targetStopDuration设置的停止时间时,发射出的粒子将会设置LifeTime寿命接近于0。

     关于 addLifeTimeGradient,我们能够添加多次,只要第一个参数在0-1之间就可以任意添加。所以我们可以自由发挥:

particleSystem.addLifeTimeGradient(0, 0.5);
particleSystem.addLifeTimeGradient(0.5, 0.1);
particleSystem.addLifeTimeGradient(0.8, 0.4);
particleSystem.addLifeTimeGradient(1, 0);

     以上代码表示,粒子系统开始发射时,发射出的粒子的LifeTime生命时间将设置为0.5,等到粒子系统运行到一半时,发射出的粒子被设置LifeTime生命时间为0.1,当运行到80%时,发射出的粒子被设置LifeTime生命时间为0.4, 当系统接近targetStopDuration设置的停止时间时,发射出的粒子将会设置LifeTime寿命接近于0。

请注意一定要设置targetStopDuration,不然粒子系统无法自动停止,也就不存在粒子系统的存在时间这个说法

     还可以通过为每个渐变提供两个值来定义更复杂的构造:

particleSystem.addLifeTimeGradient(0, 0.5, 0.8); //第1个渐变时间
particleSystem.addLifeTimeGradient(0.5, 0.3, 0.7);//第2个渐变时间
particleSystem.addLifeTimeGradient(1.0, 0, 0.1);//第3个渐变时间

     上述代码多出了第三个参数,这表示当达到渐变位置时,将在两个值之间,也就是第二和第三个参数随机选择粒子的LifeTime寿命。

     要删除TimeGradient渐变,可以调用particleSystem.removeLifeTimeGradient(0.5),这表示删除了第2个渐变时间。

体积大小Size

    粒子的大小也能够被设置为在给定范围内随机变化。

// Size of each particle (random between...)
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.5;

    上面的代码会等比例的缩放粒子,加入你想让粒子在宽高两个方向进行不同的随机改变,则可以使用 ScaleX/Y 方法:

// Scale of each particle (random between...)
particleSystem.minScaleX = 0.1;
particleSystem.maxScaleX = 0.5;

particleSystem.minScaleY = 0.2;
particleSystem.maxScaleY = 0.4;

    从Babylon.js v3.3开始,我们还可以让粒子根据生命周期来进行大小渐变。
    要添加大小渐变,只需调用以下代码:

particleSystem.addSizeGradient(0, 0.5);

    上面的例子中,第一个参数定义渐变位置(0表示粒子刚被发射出来,而1表示粒子消失)。第二个参数表示粒子的大小,上面的例子就表示粒子以0.5的体积发射出来,并在最终消失时达到3的体积。建议至少为0和1都各自定义一个渐变:

particleSystem.addSizeGradient(0, 0.5);
particleSystem.addSizeGradient(1.0, 3);

     关于 addSizeGradient,我们能够添加多次,只要第一个参数在0-1之间就可以任意添加。

     还可以通过为每个渐变提供两个值来定义更复杂的构造:

particleSystem.addSizeGradient(0, 0.5, 0.8);
particleSystem.addSizeGradient(1.0, 3, 4);

     在这种情况下,当达到渐变值时,将在第二和第三两个值之间随机选择粒子的大小。

     要删除渐变,可以调用particleSystem.removeSizeGradient(0.5)。

     当我们改变粒子体积的时候,可以对Pivot支点进行移动(也称为变换中心),默认粒子的缩放是根据中心点进行,也可以当粒子从上方或者下方进行缩放,改变Pivot支点位置的方法如下所示:

particleSystem.translationPivot = new BABYLON.Vector2(0, -0.5); // In this case the scale will come from the bottom of the particle

Playground 关于粒子大小渐变的例子,支点的位置放到了粒子下部

粒子颜色Particle Colors

    粒子系统可以设置三个颜色,其中两个颜色在粒子存在期间组合显示(或混合),第三个颜色在粒子消失之前呈现。设置方法如下:

particleSystem.color1 = new BABYLON.Color4(0.7, 0.8, 1.0, 1.0);
particleSystem.color2 = new BABYLON.Color4(0.2, 0.5, 1.0, 1.0);
particleSystem.colorDead = new BABYLON.Color4(0, 0, 0.2, 0.0);

    从Babylon.js v3.3开始,我们可以定义颜色渐变,定义之后color1, color2 和 colorDead三个属性值将失效。

    添加颜色渐变的代码如下所示:

particleSystem.addColorGradient(0, new BABYLON.Color4(1, 1, 1, 0));

    上面的例子中,第一个参数定义渐变位置(0表示粒子刚被发射出来,而1表示粒子消失)。第二个参数表示粒子的颜色,上面的例子就表示粒子发射时呈现白色。建议至少为0和1都各自定义一个渐变:

particleSystem.addColorGradient(0, new BABYLON.Color4(1, 1, 1, 0));
particleSystem.addColorGradient(1.0, new BABYLON.Color4(1, 1, 1, 1));

    可以根据需要添加任意数量的渐变,只要渐变值在0到1之间即可。

    还可以通过为每个渐变提供两种颜色来定义更复杂的结构:

particleSystem.addColorGradient(0, new BABYLON.Color4(1, 1, 1, 0), new BABYLON.Color4(1, 0, 1, 0));
particleSystem.addColorGradient(1.0, new BABYLON.Color4(1, 1, 1, 1), new BABYLON.Color4(1, 0, 1, 1));

    上述代码表示,在达到渐变条件时,将在第二和第三个参数之间随机选择粒子的颜色。

    使用particleSystem.removeColorGradient(0.5).方法来移除颜色渐变。

Playground 关于粒子颜色渐变的例子

混合模式blending

    粒子与场景的颜色混合有多种方式,使用blendMode来进行设置。

particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;

particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_STANDARD;
  • BLENDMODE_ONEONE-直接添加颜色,忽略透明度alpha的影响;
  • BLENDMODE_STANDARD-添加颜色并考虑粒子的透明度alpha影响(即:场景颜色*(1-alpha)+ 粒子颜色* alpha)
  • BLENDMODE_ADD-添加颜色,但只考虑粒子颜色的透明度alpha(即:场景颜色+粒子颜色* alpha)。
  • BLENDMODE_MULTIPLY-将颜色相乘并加上(1-alpha)(即color * particleColor + 1-alpha)。 实例演示
  • BLENDMODE_MULTIPLYADD-用BLENDMODE_MULTIPLY模式处理,然后再使用BLENDMODE_ADD模式渲染两次。 实例演示

发射频率Rates

    发射频率决定了粒子每秒被发出的数量,数字越大,发射出的例子越多,看来越密集,就像一团粒子云。当粒子死亡消失时,新的例子再被发出,以此循环往复。如果粒子的生命周期足够长,并且发送速率足够快,那么会出现粒子系统间歇性发射粒子的现象。

particleSystem.emitRate = 1000;

    通过设置manualEmitCount 属性,来停止持续的发射粒子。

particleSystem.manualEmitCount = 300; //发射粒子达到300个后停止发射。

    以上代码设置了manualEmitCount=300后,粒子系统发射300个粒子就会自动停止。

发射方向Direction

    可以为粒子系统指定2个发射方向,如果仅指定一个方向,粒子将沿给定的大致方向随机传播。 当给出两个方向时,粒子通常将在两个方向内行进。

particleSystem.direction1 = new BABYLON.Vector3(-7, 8, 3);
particleSystem.direction2 = new BABYLON.Vector3(7, 8, -3);

    发射方向也会受到重力gravity属性的影响。

重力Gravity

    粒子系统可以设置重力属性,例如:设置Y方向的重力值为负,则粒子将慢慢沿着Y方向下降。

//设置所有粒子的重力(不一定向下,可以设定XYZ方向)
particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);

旋转相关Rotation

    可以定义粒子围绕Z轴旋转的角速度AngularSpeed范围,速度以弧度/秒为单位:

particleSystem.minAngularSpeed = 0;
particleSystem.maxAngularSpeed = Math.PI;

    也可以设置粒子初始的旋转角度:

particleSystem.minInitialRotation = 0;
particleSystem.maxInitialRotation = Math.PI;

    从Babylon.js v3.3版本开始,我们也能为角速度定义一个渐变因子。

    添加角速度渐变的代码如下所示:

particleSystem.addAngularSpeedGradient(0, 0.5);

    第一个参数定义渐变(0表示粒子出生,1表示粒子死亡)。第二个参数是要使用的角速度。在这种情况下,粒子出生时的角速度设置为0.5弧度/帧。建议至少为0和1定义渐变:

particleSystem.addAngularSpeedGradient(0, 0.5);
particleSystem.addAngularSpeedGradient(1.0, 3);

    只要渐变值在0到1之间,就可以根据需要添加任意数量的渐变。

    还可以通过为每个渐变提供两个参数来定义更复杂的效果:

particleSystem.addAngularSpeedGradient(0, 0.5, 0.8);
particleSystem.addAngularSpeedGradient(1.0, 3, 4);

    上述代码中,当达到某个渐变值时,将在第二和第三个参数随机选择粒子的角速度。

    要删除渐变,可以调用particleSystem.removeAngularSpeedGradient(0.5)。

速度Speed

    可以定义发射粒子的力道范围和整体运动速度(0.01是默认的更新速度,更新速度越快=动画速度越快)。

  particleSystem.minEmitPower = 1;
  particleSystem.maxEmitPower = 3;
  particleSystem.updateSpeed = 0.005;

速度随时间变化

    我们能使用渐变来定义粒子随时间变化的速度。这个速度可以理解为向粒子运动方向施加一个作用力。设置为2表示粒子的速度为基本速度的2倍。

要添加一个速度渐变,只需要调用以下代码:

particleSystem.addVelocityGradient(0, 0.5);

    第一个参数定义渐变位置(0表示粒子系统开始,1表示粒子系统停止)。第二个参数表示粒子的速度。根据上例的情况,粒子以0.5的速度被发射出来,0.5表示基本速度的一半。建议至少为0和1都各自定义一个渐变:

particleSystem.addVelocityGradient(0, 0.5);
particleSystem.addVelocityGradient(1.0, 3);

    我们可以根据需要添加任意数量的渐变,只要渐变值在0到1之间即可,上例中分添加了0和1两个渐变位置的速度,还可以设置诸如0, 0.3, 0.5, 0.8, 1类似5个渐变位置的速度。

    我们还可以为每个渐变位置提供两个速度值来定义更复杂的结构:

particleSystem.addVelocityGradient(0, 0.5, 0.8);
particleSystem.addVelocityGradient(1.0, 3, 4);

    在以上具有2个速度值的情况下,粒子达到渐变位置的速度将在两个值中随机进行选择。

这是一个粒子速度随时间变化的例子:实例演示

    如果要移除速度渐变,我们可以调用 particleSystem.removeVelocityGradient(0.5) 方法来实现。

随时间变化的速度极限

    我们也可以通过定义渐变来限制速度,就类似于一个时间点有一个最大值极限速度。 如果粒子当前速度达到了该极限,则对速度应用一个阻尼因子。 您可以使用particleSystem.limitVelocityDamping定义此因子。

要添加速度限制渐变,只需调用以下代码:

particleSystem.addLimitVelocityGradient(0, 0.5);

    第一个参数定义渐变位置(0表示粒子系统开始,1表示粒子系统停止)。第二个参数表示粒子的极限速度。根据上例的情况,将在发射后被立即检查其速度,如果该速度大于0.5,则将应用阻尼因子(因此,速度将是:速度*阻尼因子)

    我们可以根据需要添加任意数量的渐变,只要渐变值在0到1之间即可,上例中分添加了0和1两个渐变位置的速度,还可以设置诸如0, 0.3, 0.5, 0.8, 1类似5个渐变位置的速度。

particleSystem.addLimitVelocityGradient(0, 0.5);
particleSystem.addLimitVelocityGradient(1.0, 3);

    我们还可以为每个渐变位置提供两个速度值来定义更复杂的结构:

particleSystem.addLimitVelocityGradient(0, 0.5, 0.8);
particleSystem.addLimitVelocityGradient(1.0, 3, 4);

    在以上具有2个极限速度值的情况下,粒子达到渐变位置的极限速度将在两个值中随机进行选择。

这是一个粒子的极限速度随时间变化的例子:实例演示

    如果要移除极限速度,我们可以调用 particleSystem.removeLimitVelocityGradient(0.5) 方法来实现。

阻力系数Drag factor

    我们也能定义一个渐变来控制粒子随时间变化的阻尼系数。这个系数用来模拟在粒子运动方向的摩擦力大小。例如阻尼系数设置为0.8,那么就相当于粒子在该方向只能根据常规情况下20%的速度来运动。

要添加阻尼系数渐变,只需调用以下代码:

particleSystem.addDragGradient(0, 0.5);

    第一个参数定义渐变位置(0表示粒子系统开始,1表示粒子系统停止)。第二个参数表示粒子的阻尼系数。根据上例的情况,粒子位置将是 particle.position = particle.direction * (1.0 - 0.5)

    我们可以根据需要添加任意数量的渐变,只要渐变值在0到1之间即可,上例中分添加了0和1两个渐变位置的速度,还可以设置诸如0, 0.3, 0.5, 0.8, 1类似5个渐变位置的速度。

particleSystem.addDragGradient(0, 0.5);
particleSystem.addDragGradient(1.0, 3);

    我们还可以为每个渐变位置提供两个阻尼系数来定义更复杂的结构:

particleSystem.addDragGradient(0, 0.5, 0.8);
particleSystem.addDragGradient(1.0, 0, 0.1);

    在以上具有2个阻尼系数值的情况下,粒子达到渐变位置的阻尼系数值将在两个值中随机进行选择。

这是一个粒子的阻尼系数随时间变化的例子:实例演示

    如果要移除阻尼系数,我们可以调用 particleSystem.removeDragGradient(0.5) 方法来实现。

发射速率随时间变化

    发射速率也可以由渐变来定义。根据时间变化,由渐变设置的发射速率将覆盖system.emitRate属性的值。

要添加发射速率渐变,只需调用以下代码:

particleSystem.addEmitRateGradient(0, 10);

请注意,发射速率渐变的启用需要满足一个条件,那就是需要设置粒子系统的生命周期,也就是必须设置particleSystem.targetStopDuration属性,addEmitRateGradient才能生效。因为通常来说粒子系统是不断发射出粒子而不会停止,这导致无法确定渐变位置,所以需要设置粒子系统自动停止的时间targetStopDuration属性,让系统能够分配渐变位置。

    第一个参数定义渐变位置(0表示粒子系统开始,1表示粒子系统停止)。第二个参数表示需要启用的发射速率。根据上例的情况,粒子系统一开始运行,就可以每帧发射10个粒子。

    我们可以根据需要添加任意数量的渐变,只要渐变值在0到1之间即可,上例中分添加了0和1两个渐变位置的速度,还可以设置诸如0, 0.3, 0.5, 0.8, 1类似5个渐变位置的速度。

particleSystem.addEmitRateGradient(0, 10);
particleSystem.addEmitRateGradient(1.0, 500);

    我们还可以为每个渐变位置提供两个发射速率来定义更复杂的结构:

particleSystem.addEmitRateGradient(0, 5, 10);
particleSystem.addEmitRateGradient(1.0, 800, 1000);

这是一个粒子的发射速率随时间变化的例子:实例演示

    如果要移除发射速率,我们可以调用 particleSystem.removeEmitRateGradient(0.5) 方法来实现。

初始体积随时间变化

    要使用渐变来控制粒子的初始体积大小,可以调用以下代码:

particleSystem.addStartSizeGradient(0, 2);

请注意,初始体积渐变的启用需要满足一个条件,那就是需要设置粒子系统的生命周期,也就是必须设置particleSystem.targetStopDuration属性,addEmitRateGradient才能生效。因为通常来说粒子系统是不断发射出粒子而不会停止,这导致设置初始发射大小没有意义,所以需要设置粒子系统自动停止的时间targetStopDuration属性,让系统能够分配渐变位置。

    第一个参数定义渐变位置(0表示粒子系统开始,1表示粒子系统停止)。第二个参数表示需要启用的初始体积大小。根据上例的情况,粒子系统一开始运行,发射的粒子为默认体积的2倍。 (例如,如果将粒子体积size设置为2并将start size设置为3,则粒子最终大小将为6)。建议至少为0和1定义一个渐变:

particleSystem.addStartSizeGradient(0, 10);
particleSystem.addStartSizeGradient(1.0, 500);

    我们还可以为每个渐变位置提供两个发射速率来定义更复杂的结构:

particleSystem.addStartSizeGradient(0, 5, 10);
particleSystem.addStartSizeGradient(1.0, 800, 1000);

    在以上具有2个初始体积值的情况下,粒子达到渐变位置的极限速度将在两个值中随机进行选择。

这是一个粒子的初始体积随时间变化的例子:实例演示

    如果要移除初始体积,我们可以调用 particleSystem.removeStartSizeGradient(0.5) 方法来实现。

坡度渐变Ramp gradients

    可以使用坡度渐变基于alpha更改粒子的颜色。它是非常强大的,而且只需要简单设置一下即可。

    首先我们需要声明坡度渐变:

system.addRampGradient(0.0, new BABYLON.Color3(1, 1, 1));
system.addRampGradient(0.09, new BABYLON.Color3(209/255, 204/255, 15/255));
system.addRampGradient(0.18, new BABYLON.Color3(221/255, 120/255, 14/255));
system.addRampGradient(0.28, new BABYLON.Color3(200/255, 43/255, 18/255));
system.addRampGradient(0.47, new BABYLON.Color3(115/255, 22/255, 15/255));
system.addRampGradient(0.88, new BABYLON.Color3(14/255, 14/255, 14/255));
system.addRampGradient(1.0, new BABYLON.Color3(14/255, 14/255, 14/255));

    这些渐变将用于构建渐变颜色纹理。所以需要开启一个选项:

system.useRampGradients = true;

    默认情况下,粒子的alpha值(构建于textureAlpha * particleColorAlpha)被以下的公式用来获取坡度颜色(Alpha是坡度渐变列表中的索引值):finalColor = textureColor * particleColor * rampColor[alphaIndex]。

    为了获得更多控制权,我们可以使用remap函数来更改此alpha索引的重映射:

system.addColorRemapGradient(0, 0, 0.1);
system.addColorRemapGradient(0.2, 0.1, 0.8);
system.addColorRemapGradient(0.3, 0.2, 0.85);
system.addColorRemapGradient(0.35, 0.4, 0.85);
system.addColorRemapGradient(0.4, 0.5, 0.9);
system.addColorRemapGradient(0.5, 0.95, 1.0);
system.addColorRemapGradient(1.0, 0.95, 1.0);

    上例中,颜色重映射渐变定义了随时间变化的最大和最小值(也可以只定义一个值)。然后,使用以下公式将alpha索引从[min,max]重新映射为[0,1]:finalAlphaIndex =clamp((alphaIndex - min)/(max - min),0.0,1.0)。

    最终,您还可以使用以下方法重新映射每个像素生成的alpha值:

system.addAlphaRemapGradient(0, 0, 0.1);
system.addAlphaRemapGradient(1.0, 0.1, 0.8);

    alpha重新映射将使用以下公式计算最终的alpha值:finalAlpha = clamp((textureAlpha * particleColorAlpha * rampColor.a - min) / (max - min), 0.0, 1.0).

这是一个坡度渐变的综合例子:实例演示

对齐Alignment

    默认情况下所有的粒子都渲染为广告牌模式billboards(不管相机如何的转动,粒子永远正对着屏幕)。我们也可以禁用广告牌模式,让粒子保持它自己的位子,不自动随着相机调整:system.isBillboardBased = false。

这里是一个system.isBillboardBased = false的实例,各位可以自己改改看看变化:实例演示

    广告牌模式下,我们可以使用如下代码决定粒子正对的方向(所有轴、还是Y轴):

system.billboardMode = BABYLON.ParticleSystem.BILLBOARDMODE_Y;

    您也可以使用拉伸的广告牌,这类似于完整的广告牌模式,但具有附加的旋转,以使粒子与其方向对齐。话不多说,看实例最能理解这里的意思:

BILLBOARDMODE_Y:实例演示

BILLBOARDMODE_STRETCHED:实例演示

可调整设置的粒子示例

调整粒子发射器的发射范围:实例演示

调整发射寿命,速率,功率和更新速度 :实例演示

形状发射器

    自从Babylonjs 3.2版本开始,我们能够定义粒子发射的区域,也就是让粒子从指定好的区域中发射出去,可以设置的区域如下:

  • Point:从一个点发射出去
  • Box:从一个立方体的区域随机发射
  • Sphere:从一个球体区域随机发射
  • Hemisphere:从一个半球区域随机发射
  • Cylinder:从一个圆柱体区域随机发射
  • Cone:从一个圆锥体区域随机发射
  • Mesh:从一个指定形状的区域随机发射
  • Custom:从一个自定义区域随机发射

    可以通过以上的区域实现特定功能的粒子发射器。

单点发射器Point

    要创建一个单点粒子发射器,只需要执行以下代码:

var pointEmitter = particleSystem.createPointEmitter(new BABYLON.Vector3(-7, 8, 3), new BABYLON.Vector3(7, 8, -3));

    createPointEmitter方法接受两个参数,分别是:

  • direction1: Vector3,
  • direction2: Vector3

    以上两个参数用来控制粒子从单个点发射出来的方向,函数返回的pointEmitter对象可用于更改这两个属性的值。

pointEmitter.direction1 = new BABYLON.Vector3(-5, 2, 1);
pointEmitter.direction2 = new BABYLON.Vector3(5, 2, 1); 

单点发射器:实例演示

立方体发射器Box

    可以通过以下代码创建立方体发射器:

var boxEmitter = particleSystem.createBoxEmitter(new BABYLON.Vector3(-7, 8, 3), new BABYLON.Vector3(7, 8, -3), new BABYLON.Vector3(-1, 0, 0), new BABYLON.Vector3(1, 0, 0));

    createBoxEmitter方法接受4个参数,依次为:

  • direction1: Vector3,
  • direction2: Vector3,
  • minEmitBox: Vector3,
  • maxEmitBox: Vector3

    direction参数控制发射方向,EmitBox限制立方体的体积,返回的boxEmitter对象可用于更改这些属性的值。

boxEmitter.direction1 = new BABYLON.Vector3(-5, 2, 1);
boxEmitter.direction2 = new BABYLON.Vector3(5, 2, 1); 
boxEmitter.minEmitBox = new BABYLON.Vector3(-2, -3, -4);
boxEmitter.maxEmitBox = new BABYLON.Vector3(2, 3, 4);

立方体发射器:实例演示

球体发射器Sphere

    我们可以依据给定的半径来创建一个球体发射器,例如下面的例子,半径为1.2:

var sphereEmitter = particleSystem.createSphereEmitter(1.2);

    返回的sphereEmitter对象可用于更改半径值。
    粒子沿球体表面的法线方向发射,发现就是垂直于球体表面的线,即从球体中心穿过表面点的线。法线

法线方向的球体发射器:实例演示

    使用sphereEmitter.radiusRange属性,我们可以定义粒子发射的位置,值为0表示仅在球体表面发射粒子,值为1表示整个球体内都可以发射粒子。

    也可以创建一个带方向发射的球体发射器:

var sphereEmitter = particleSystem.createDirectedSphereEmitter(1.2, new BABYLON.Vector3(1, 1, 1), new BABYLON.Vector3(2, 8, 2));

    createDirectedSphereEmitter 方法接受3个参数,依次为:

  • radius: Number,
  • direction1: Vector3,
  • direction2: Vector3,

    radius控制发射半径,direction参数控制发射方向,返回的sphereEmitter对象可用于更改这些属性的值。

sphereEmitter.radius = 3.4;
sphereEmitter.direction1 = new BABYLON.Vector3(-5, 2, 1);
sphereEmitter.direction2 = new BABYLON.Vector3(5, 2, -1);

自定义方向的球体发射器:实例演示

半球发射器Hemisphere

     我们可以依据给定的半径来创建一个半球形的发射器,例如下面的例子,半径为1.2:

var hemisphericEmitter = particleSystem.createHemisphericEmitter(1.2);

     创建函数返回的hemisphericEmitter对象能够随时更改半径的大小。

     粒子会沿着半球表面的法线方向发射,即:从半球中心到表面任意点的线。

半球发射器:实例演示

    使用hemisphericEmitter.radiusRange属性,我们可以定义粒子发射的位置,值为0表示仅在半球表面发射粒子,值为1表示整个半球内都可以发射粒子。

圆柱体发射器Cylinder

    我们能够通过给定radius半径, height高度, radiusRange半径范围, directionRandomizer方向随机4个参数来创建一个圆柱体粒子发射器,代码如下:

var cylinderEmitter = particleSystem.createCylinderEmitter(1,1,0,0);

     创建函数返回的cylinderEmitter对象能够随时更改radius半径、height高度等的值。

     粒子沿圆柱体表面法线方向发射,即:从圆柱体内部向外发射粒子。

圆柱体发射器:实例演示

    使用cylinderEmitter.radiusRange属性,我们可以定义粒子发射的位置,值为0表示仅在圆柱体表面发射粒子,值为1表示整个圆柱体内都可以发射粒子。使用cylinderEmitter.directionRandomizer可以控制粒子发射方向的随机程度,以决定粒子的发射方向。

    createDirectedCylinderEmitter方法按中的参数遵守以下顺序:

  • radius: Number,圆柱体的半径
  • height: Number,圆柱体高度
  • radiusRange: Number,沿表面还是内部发射
  • direction1: Vector3,和direction2一起决定粒子的发射方向
  • direction2: Vector3,

    返回的cylinderEmitter对象可用于更改以上属性的值:

cylinderEmitter.radius = 3.4;
cylinderEmitter.direction1 = new BABYLON.Vector3(-5, 2, 1); 
cylinderEmitter.direction2 = new BABYLON.Vector3(5, 2, -1); 

圆柱体发射器:实例演示

圆锥体发射器Cone

    要创建一个圆锥体粒子发射器,只要使用如下代码:

var coneEmitter = particleSystem.createConeEmitter(2, Math.PI / 3);

    createConeEmitter 方法具有两个参数,按照先后顺序如下:

  • radius: Number;
  • angle: Number, 以弧度为单位,决定圆锥的倾斜角度
        上例中,沿着Y轴创建了一个圆锥,它的顶点在底部,等于就像一个陀螺放在地面。

    使用coneEmitter.radiusRange属性,我们可以定义粒子发射的位置,值为0表示仅在半球表面发射粒子,值为1表示整个半球内都可以发射粒子。

    相比其他物体,圆锥有cubeEmitter.heightRange属性,它可以定义圆锥在顶点和底部之间发射粒子的方式。 0表示仅在顶点的表面上,值为1表示整个圆锥高度都会发射。

    一下是仅从圆锥体外部发射粒子的示例:

圆锥体发射器:实例演示

    createConeEmitter方法返回的cubeEmitter对象可用于再次更改属性的值。

coneEmitter.radius = 3.4;
coneEmitter.angle = Math.PI / 2; 

    如果设置cubeEmitter.emitFromSpawnPointOnly = true,则可以强制粒子从起点(圆锥体的顶点)发射。可以制造一个大炮射击的效果。
圆锥体发射器示例:实例演示
圆锥体发射器旋转发射:实例演示

任意形状发射器Mesh

    你可以使用MeshParticleEmitter来创建任意形状的粒子发射器

var meshEmitter = new BABYLON.MeshParticleEmitter(sphere);

    默认情况下,发射粒子的方向是各个Mesh面的法方向。你也可以关闭默认方向,自定义粒子发射方向。

meshEmitter.useMeshNormalsForDirection = false;
meshEmitter.direction1 = new BABYLON.Vector3(0, 1, 0);
meshEmitter.direction2 = new BABYLON.Vector3(0, -1, 0);

注意:任意形状粒子发射器Mesh不支持GPU粒子系统
实例演示

自定义发射器Custom

    你可以通过两个函数创建自定义粒子发射器。

var customEmitter = new BABYLON.CustomParticleEmitter();

 var id = 0;
 customEmitter.particlePositionGenerator = (index, particle, out) => {
     out.x = Math.cos(id) * 5;
     out.y = Math.sin(id) * 5;
     out.z = 0;
     id += 0.01;
 }

 customEmitter.particleDestinationGenerator = (index, particle, out) => {
     out.x = 0;
     out.y = 0;
     out.z = 0;
 }

    通过自定义粒子发射器,你可以定义每个粒子的位置和目的地。
    在GPU粒子系统中,粒子生成函数(particlePositionGeneratorparticleDestinationGenerator)通过粒子索引更新粒子,在CPU粒子系统中,粒子生成函数通过粒子实例更新粒子。
实例演示

粒子噪声纹理Noise texture

    从Babylon.js 3.3版本开始,你可以使用噪音纹理(Noise Texture)来"扰乱"粒子的位置。噪音纹理(Noise Texture)是一种改变粒子方向的技术。

var noiseTexture = new BABYLON.NoiseProceduralTexture("perlin", 256, scene);
noiseTexture.animationSpeedFactor = 5;
noiseTexture.persistence = 2;
noiseTexture.brightness = 0.5;
noiseTexture.octaves = 2;

particleSystem.noiseTexture = noiseTexture;
particleSystem.noiseStrength = new BABYLON.Vector3(100, 100, 100);

你也可以通过particleSystem.noiseStrength来设定噪音纹理在各个方向上的"扰乱"力度。
实例演示

GPU粒子系统

    从Babylon.js 3.2开始,你可以使用WebGL2的新功能形变反馈缓存(the transform feedback buffer)来极大的增强粒子系统的性能。普通的粒子系统使用CPU来处理动画,使用GPU来渲染画面。而GPU粒子系统可以通过WebGL2的API调用GPU处理动画和渲染画面,在GPU粒子系统中,所有的计算都通过GPU实现。
    不过这个功能只有在支持WebGL2的环境下才可以使用,你可以使用BABYLON.GPUParticleSystem.IsSupported来决定使用使用GPU粒子系统。GPU粒子系统的使用方法和普通粒子系统基本一致。

var particleSystem = new BABYLON.GPUParticleSystem("particles", { capacity:1000000 }, scene)

    因为不再涉及CPU,你可以大胆使用大量活跃粒子(比如1000000)。假如你想现在GPU使用的话,你也可以使用particleSystem.activeParticleCount来定义活跃粒子的数目。
注意:子发射器(sub emmitter)不支持GPU粒子系统

随机纹理Random Texture

    通过GPU是无法获取随机数的,对此Babylon创建了一个由大量随机数填充的纹理,通过此纹理来模拟随机数。粒子系统的更新函数读取Babylon的随机数纹理,来渲染随机粒子动画。默认情况下,最大随机纹理尺寸为16K, 你可以通过以下语句来降低随机纹理尺寸。

var particleSystem = new BABYLON.GPUParticleSystem("particles", { capacity:1000000, randomTextureSize: 4096 }, scene);

    因为GPU粒子系统和普通粒子系统几乎使用完全一样的API,所以你可以随时切换粒子系统的类型。不过需要注意的是,CPU图像处理性能低于GPU,无法像GPU一样处理大量粒子,所以从GPU粒子系统切换至普通(CPU)粒子系统是可能需要减少粒子的数量。

停止GPU粒子系统

    在GPUParticleSystem对象下调用system.stop()时,粒子系统会停止产生新的粒子。但是粒子依然会被渲染,即使它是不可见的。
    彻底的停止GPUParticleSystem需要调用dispose()

不支持的功能

由于GPU的特性,以下功能不支持:

  • 手动控制发射数量(ManualEmitCount)
  • 自定义特效(Custom effects)
  • 自动终止(disposeOnStop)
  • 双值渐变(Dual values per gradient (仅支持单值))
  • 渐变发射速率(Emit rate gradients)
  • 渐变初始尺寸(Start size gradients)
  • 任意形状发射器(Mesh emitter)

Playground示例

实例演示

Snippet服务调用

    从Babylon.js 4.2开始,你可以使用见检视器(Inspector)来编辑粒子系统。然后可将粒子系统存储到Babylon.js的Snippet服务器上。当你有Snippet ID时,你可以方便的通过ID调用存储的粒子系统:

var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 0.2, segments: 32}, scene);
BABYLON.ParticleHelper.CreateFromSnippetAsync("T54JV7", scene, false).then(system => {
    system.emitter = sphere;
});

    你可以把Snippet ID声明为_BLANK,这样系统会创建一个空的粒子系统。

BABYLON.ParticleHelper.CreateFromSnippetAsync("_BLANK", scene, false);

下一步

    粒子系统是非常强大的多用途的工具,可以用来拟真许多现实世界的运动,特效等。在计算资源充裕时,大胆的使用它吧。
    下一步,我们将进行有趣的新教材:配置环境

延伸阅读

基础-难度L1

粒子概述
粒子助手
物体概述

如何创建一个动画粒子
如何使用子粒子

固态粒子系统
点云粒子系统

中级-难度L2

如何自定义粒子系统

贡献者

翻译: R.ED / Cow&Sheep
校对: Cow&Sheep
技术支持: Dushusir github

参与贡献? 联系alexads@foxmail.com