16. 发射粒子-产生梦幻的效果
本教程翻译自官方教程Particles,各位也可以进入本站镜像站点查看
本文目录
粒子Particles
本章将讨论BabylonJS中的粒子系统。粒子通常是一些小的精灵图,用于模拟难以复制的现象,例如:火焰、烟雾、水或者抽象的视觉效果,类似魔法闪光、仙尘。这些效果都是通过在一个区域不断的发射粒子来实现的,自从Babylon 3.2版本开始,新增了特殊的例子发射器,可以约束例子在一定的形状范围内,例如立方体、球体或者圆锥体。当然,我们也能写自定义函数来控制粒子和发射区域。
GPU粒子是粒子家族的新成员,可以在支持GPU粒子的浏览器下使用,可以大大提高粒子的性能,性能越高,能够同时存在屏幕上的粒子就越多,效果就越炫酷。
粒子示意图
创建粒子
粒子系统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 会带来问题。
粒子纹理
采用一个纹理到粒子中,如下图所示:
可以设置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粒子,形状发射器是啥请继续看下文
世界中心偏移
从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示例,自己也可以尝试更改其中一些参数,以便于更好的学习粒子系统。
生命周期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).方法来移除颜色渐变。
混合模式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粒子系统中,粒子生成函数(particlePositionGenerator
和particleDestinationGenerator
)通过粒子索引更新粒子,在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
贡献者
参与贡献? 联系alexads@foxmail.com