前两天写粒子的时候发现,目前版本(r106)里的粒子都是用THREE.Points来生成的,这个类十分的简单。
这个类本身是用来生成点的,也就是说,用它只是生成了一堆的点,这个类简单到自己的函数只有俩,(即不算父类继承的),这在我眼里根本不能算是粒子系统,只能说有粒子 = = 。
我心里的粒子系统,是要有粒子发射器,粒子控制和管理的,然鹅,一个Point并不够啊~~~!!
Three里面原本应该是有粒子系统的,不知道为什么删掉了,变成了现在的样子。
好在我左刨刨,右刨刨,在例子里面发现了一个这样的案例。
晕~我刚刚去查官方案例,已经被删除了,我下载的案例包里还是有的,github上的代码也被移除了。难道因为版权么?
这个和我心里面的粒子系统是比较接近的,当然和大型引擎的没法比,不过自己改改还是可以的,好多参数可以调。
我看了一下这个JS,核心代码500行,不算多,里面还包括shader,注释,还有一些我注释掉了也没啥变化的代码。可能是机器性能在提升,所以之前能够提升性能现在不明显了么?不是太确定,先留个尾巴吧。
在这个例子里面,主要有两个js,一个是核心处理代码GPUParticleSystem.js,一个调用代码webgl_gpu_particle_system.js。
先看调用代码 webgl_gpu_particle_system.js
调用也不复杂,一个new,两个option,外加update里面的一些更新操作。
先是new一个system出来。
particleSystem = new THREE.GPUParticleSystem( {
maxParticles: 250000,
} );
两个option,可以配合GUI来实时改变一些参数。
options = {
position: new THREE.Vector3(),
positionRandomness: .3,
velocity: new THREE.Vector3(),
velocityRandomness: .5,
color: 0xaa88ff,
colorRandomness: .2,
turbulence: .5,
lifetime: 2,
size: 5,
sizeRandomness: 1,
smoothPosition:true
};
spawnerOptions = {
spawnRate: 15000,
horizontalSpeed: 1.5,
verticalSpeed: 1.33,
timeScale: 1
};
宽泛的说,上面一个options 是控制微观粒子为主,下面一个spawnerOptions 是控制宏观系统为主。
position:控制整个粒子系统的运行轨迹
positionRandomness:控制粒子位置的扰动程度,数值越大,扰动的程度越大,数值越小,位置越集中
velocity:这个速度是每个粒子所拥有的速度,代码里有一个maxVel变量为2,个人推测这个值不应该超过这个值,不过超过也不会报错,效果有点,狂野,粒子的速度越大,每一帧的刷新都可能离前一个位置越远,因此粒子间会更加的分散。下面是速度为v3(3,3,3)的效果。
velocityRandomness:速度的干扰系数
color:粒子颜色值
colorRandomness:颜色值的干扰系数
turbulence:摆尾的干扰值,越大尾巴摆的越欢实。
lifetime:粒子的生命时长
size:粒子大小
sizeRandomness:大小的干扰
smoothPosition:位置平滑开关,如果速度过大,会出现后面的点跟不上前面点的情况,出现明显的断点,如下图,这时候稍微平滑一下有助于改善这种状况,不过亲测效果不是很明显。
spawnRate:控制显示的粒子的数量,下图分别是1500,和30000的效果。
horizontalSpeed:水平移动速度
verticalSpeed:垂直移动速度
速度越大,下一帧的粒子群较上一帧的粒子群位置变换越大,造成走完整个路径的时间缩短,视觉感觉变快,速度过大,可造成粒子断点的现象。如往上数倒数第3张。
timeScale:控制时间的速度,数值大,时间变快,视觉上速度变快,数值小,时间慢,速度慢,小于0会倒退,也很有趣。
更新操作在function animate()里面,主要代码如下:
var delta = clock.getDelta() * spawnerOptions.timeScale;
tick += delta;
if ( tick < 0 ) tick = 0;
if ( delta > 0 ) {
options.position.x = Math.sin( tick * spawnerOptions.horizontalSpeed ) * 20;
options.position.y = Math.sin( tick * spawnerOptions.verticalSpeed ) * 10;
options.position.z = Math.sin( tick * spawnerOptions.horizontalSpeed + spawnerOptions.verticalSpeed ) * 5;
for ( var x = 0; x < spawnerOptions.spawnRate * delta; x ++ ) {
// Yep, that's really it. Spawning particles is super cheap, and once you spawn them, the rest of
// their lifecycle is handled entirely on the GPU, driven by a time uniform updated below
particleSystem.spawnParticle( options );
}
}
particleSystem.update( tick );
delta:clock.getDelta()在每次更新中获得时间间隔,我console的结果是在0.016上下浮动。乘以时间伸缩量“scale”就是当前这帧所需要的时间间隔。
tick: 记录从开始运行到当前帧的总时间。包括时间“scale”在内。
options.position:根据时间和速度进行更新,sin的值域范围在-1到1之间,所以xyz的值分别限定在±20,±10,±5。
看一下这个for循环,是一个很有意思的地方。
spawnerOptions.spawnRate按照默认的配置是在400-600之间,也就是说,每次只更新这些个粒子的位置。还有一些粒子的位置是之前或者之后更新的。
比如说我总共想实例化250000个粒子,每次改变500个粒子的位置,假设500个是一组,那么一共改变500次才能把所有粒子动一遍,而运行到第100次的时候,会出现一条粒子线,前20个会因为过了2秒的生命周期而看不见,20-40的会因为生命周期所剩不多而变得透明,其余的400个会因为还没动而留在原地。(数字只是举例,不准确)大概是这个意思。
如果 spawnRate 这个值变小,那么一组粒子的数量会变少,可能是200个,生命周期一样的情况下,分组总数变多,但是在2秒以内的组数还是那么多,看起来粒子的数量就变少了。
particleSystem.spawnParticle( options ); 用来更新粒子的位置颜色等信息。
particleSystem.update( tick ); 传入总时间,并在GPU中计算每个粒子当前时间的状态。
下一篇会写一些,自己看GPUParticleSystem的体会。先挖个坑~ 哈哈
核心代码部分,请见粒子(二)