three.js粒子系统封装

粒子系统

import * as THREE from 'three/build/three.module';

export default class SpriteParticleSystem extends THREE.Object3D {

  constructor(param) {
    super();
    this.cloud = param.cloud;
    this.rate = param.rate !== undefined ? param.rate : 3;
    this.num = param.num !== undefined ? param.num : 30;
    this.texture = param.texture;
    this.scaleR = param.scaleR !== undefined ? param.scaleR : [1, 10];
    this.speedR = param.speedR !== undefined ? param.speedR : [10, 10];
    this.terminalSpeed = param.terminalspeed !== undefined ? param.terminalspeed : null;
    this.rspeedR = param.rspeedR !== undefined ? param.rspeedR : [-0.2, 0.2];
    this.lifespanR = param.lifespanR !== undefined ? param.lifespanR : [3, 5];
    this.forces = [];
    this.xScaleBase = this.texture.image.width;
    this.yScaleBase = this.texture.image.height;

    this.particles = [];
    this.lastUpdateTime = 0;
    this.nextParticleIndex = 0;

    this.init();
  }

  init () {
    var mat = new THREE.SpriteMaterial({
      map: this.texture,
      // useScreenCoordinates: false,
      depthWrite: false,
      color: 0xffffff,
      fog: true
    });
    for (var i = 0; i < this.num; ++i) {
      var smat = mat.clone();
      var sprite = new THREE.Sprite(smat);
      sprite.visible = false;
      this.cloud.add(sprite);
      var particle = {};
      particle.sprite = sprite;
      particle.death = -1;
      this.particles.push(particle);
    }
  }

  nextSpawnInterval (rate) {
    return (2.0 * Math.random()) / rate;
  }

  uniformRandom (a, b) {
    return a + (b - a) * Math.random();
  }

  addForce (force) {
    this.forces.push(force);
  }

  spawnOne (now) {
    var p = this.particles[this.nextParticleIndex];
    this.nextParticleIndex = (this.nextParticleIndex + 1) % this.particles.length;
    p.birth = now;
    p.death = now + this.uniformRandom(this.lifespanR[0], this.lifespanR[1]);
    var sprite = p.sprite;
    sprite.visible = true;
    sprite.position.setFromMatrixPosition(this.matrixWorld);
    sprite.material.rotation = this.uniformRandom(0, 2 * Math.PI);
    p.speed = this.uniformRandom(this.speedR[0], this.speedR[1]);
    var theta = this.uniformRandom(0, 2 * Math.PI);
    var phi = this.uniformRandom(-Math.PI, Math.PI);
    p.vel = new THREE.Vector3();
    p.vel.x = p.speed * Math.cos(phi) * Math.cos(theta);
    p.vel.y = p.speed * Math.cos(phi) * Math.sin(theta);
    p.vel.z = p.speed * Math.sin(phi);
    p.rspeed = this.uniformRandom(this.rspeedR[0], this.rspeedR[1]);
  }

  start () {
    this.nextSpawnT = this.lastUpdateTime + this.nextSpawnInterval(this.rate);
  }

  stop () {
    this.nextSpawnT = null;
  }

  update (dt) {
    if (dt === 0) return;
    this.lastUpdateTime += dt;
    var now = this.lastUpdateTime;

    for (var i = 0; i < this.particles.length; ++i) {
      var p = this.particles[i];
      if (now > p.death) {
        p.sprite.opacity = 0;
        p.sprite.visible = false;
        continue;
      }
      var sprite = p.sprite;
      sprite.material.rotation += dt * p.rspeed;
      for (var j = 0; j < this.forces.length; j++) {
        var dv = this.forces[j].clone();
        dv.multiplyScalar(dt);
        p.vel.add(dv);
      }

      if (this.terminalSpeed !== null) {
        var speed = p.vel.length();
        if (speed > this.terminalSpeed) p.vel.multiplyScalar(this.terminalSpeed / speed);
      }

      var dpos = p.vel.clone();
      dpos.multiplyScalar(dt);
      sprite.position.add(dpos);

      var fracLife = (now - p.birth) / (p.death - p.birth);
      var scale = this.scaleR[0] + fracLife * (this.scaleR[1] - this.scaleR[0]);
      sprite.scale.set(scale * this.xScaleBase, scale * this.yScaleBase, 1.0);
      sprite.material.opacity = 1.0 - fracLife;
    }

    while (this.nextSpawnT && now >= this.nextSpawnT) {
      this.spawnOne(this.nextSpawnT);
      this.nextSpawnT += this.nextSpawnInterval(this.rate);
    }
  }

}

使用


    initParticle(position) {
      let smoke = new THREE.Object3D();
      smoke.scale.set(0.06, 0.06, 0.06);

      let spritesParticle = new SpriteParticleSystem({
        cloud: smoke,
        rate: 1, // 粒子速度
        num: 60, // 粒子数量
        texture: textureLoader.load(
  require('../../public/resource/img/particle/smoke.png'),
  texture => {
    texture.needsUpdate = true;
  }
);, // 粒子材质
        scaleR: [0.01, 0.2], // 缩放
        speedR: [0.01, 0.1],
        rspeedR: [-0.01, 0.5],
        lifespanR: [4, 4],
        terminalSpeed: 3
      });
      spritesParticle.addForce(new THREE.Vector3(0, 5, 0));
      spritesParticle.start();
      particleArr.push(spritesParticle);
      smoke.position.set(position.x, position.y, position.z);
      return smoke;
    },