ParticleEngine.js实现烟雾效果

参考网址:http://stemkoski.github.io/Three.js/Particle-Engine.html

ParticleEngine.js源码依赖的three.js版本是60,而我使用的three.js的版本是112,新版本不支持为ShaderMaterial设置attributes,所以修改了ParticleEngine.js源码。

原始版本ParticleEngine.js代码:

/**
* @author Lee Stemkoski http://www.adelphi.edu/~stemkoski/
*/ /////////////////////////////////////////////////////////////////////////////// /////////////
// SHADERS //
///////////// // attribute: data that may be different for each particle (such as size and color);
// can only be used in vertex shader
// varying: used to communicate data from vertex shader to fragment shader
// uniform: data that is the same for each particle (such as texture) particleVertexShader =
[
"attribute vec3 customColor;",
"attribute float customOpacity;",
"attribute float customSize;",
"attribute float customAngle;",
"attribute float customVisible;", // float used as boolean (0 = false, 1 = true)
"varying vec4 vColor;",
"varying float vAngle;",
"void main()",
"{",
"if ( customVisible > 0.5 )", // true
"vColor = vec4( customColor, customOpacity );", // set color associated to vertex; use later in fragment shader.
"else", // false
"vColor = vec4(0.0, 0.0, 0.0, 0.0);", // make particle invisible. "vAngle = customAngle;", "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"gl_PointSize = customSize * ( 300.0 / length( mvPosition.xyz ) );", // scale particles as objects in 3D space
"gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n"); particleFragmentShader =
[
"uniform sampler2D texture;",
"varying vec4 vColor;",
"varying float vAngle;",
"void main()",
"{",
"gl_FragColor = vColor;", "float c = cos(vAngle);",
"float s = sin(vAngle);",
"vec2 rotatedUV = vec2(c * (gl_PointCoord.x - 0.5) + s * (gl_PointCoord.y - 0.5) + 0.5,",
"c * (gl_PointCoord.y - 0.5) - s * (gl_PointCoord.x - 0.5) + 0.5);", // rotate UV coordinates to rotate texture
"vec4 rotatedTexture = texture2D( texture, rotatedUV );",
"gl_FragColor = gl_FragColor * rotatedTexture;", // sets an otherwise white particle texture to desired color
"}"
].join("\n"); /////////////////////////////////////////////////////////////////////////////// /////////////////
// TWEEN CLASS //
///////////////// function Tween(timeArray, valueArray)
{
this.times = timeArray || [];
this.values = valueArray || [];
} Tween.prototype.lerp = function(t)
{
var i = 0;
var n = this.times.length;
while (i < n && t > this.times[i])
i++;
if (i == 0) return this.values[0];
if (i == n) return this.values[n-1];
var p = (t - this.times[i-1]) / (this.times[i] - this.times[i-1]);
if (this.values[0] instanceof THREE.Vector3)
return this.values[i-1].clone().lerp( this.values[i], p );
else // its a float
return this.values[i-1] + p * (this.values[i] - this.values[i-1]);
} /////////////////////////////////////////////////////////////////////////////// ////////////////////
// PARTICLE CLASS //
//////////////////// function Particle()
{
this.position = new THREE.Vector3();
this.velocity = new THREE.Vector3(); // units per second
this.acceleration = new THREE.Vector3(); this.angle = 0;
this.angleVelocity = 0; // degrees per second
this.angleAcceleration = 0; // degrees per second, per second this.size = 16.0; this.color = new THREE.Color();
this.opacity = 1.0; this.age = 0;
this.alive = 0; // use float instead of boolean for shader purposes
} Particle.prototype.update = function(dt)
{
this.position.add( this.velocity.clone().multiplyScalar(dt) );
this.velocity.add( this.acceleration.clone().multiplyScalar(dt) ); // convert from degrees to radians: 0.01745329251 = Math.PI/180
this.angle += this.angleVelocity * 0.01745329251 * dt;
this.angleVelocity += this.angleAcceleration * 0.01745329251 * dt; this.age += dt; // if the tween for a given attribute is nonempty,
// then use it to update the attribute's value if ( this.sizeTween.times.length > 0 )
this.size = this.sizeTween.lerp( this.age ); if ( this.colorTween.times.length > 0 )
{
var colorHSL = this.colorTween.lerp( this.age );
this.color = new THREE.Color().setHSL( colorHSL.x, colorHSL.y, colorHSL.z );
} if ( this.opacityTween.times.length > 0 )
this.opacity = this.opacityTween.lerp( this.age );
} /////////////////////////////////////////////////////////////////////////////// ///////////////////////////
// PARTICLE ENGINE CLASS //
/////////////////////////// var Type = Object.freeze({ "CUBE":1, "SPHERE":2 }); function ParticleEngine()
{
/////////////////////////
// PARTICLE PROPERTIES //
///////////////////////// this.positionStyle = Type.CUBE;
this.positionBase = new THREE.Vector3();
// cube shape data
this.positionSpread = new THREE.Vector3();
// sphere shape data
this.positionRadius = 0; // distance from base at which particles start this.velocityStyle = Type.CUBE;
// cube movement data
this.velocityBase = new THREE.Vector3();
this.velocitySpread = new THREE.Vector3();
// sphere movement data
// direction vector calculated using initial position
this.speedBase = 0;
this.speedSpread = 0; this.accelerationBase = new THREE.Vector3();
this.accelerationSpread = new THREE.Vector3(); this.angleBase = 0;
this.angleSpread = 0;
this.angleVelocityBase = 0;
this.angleVelocitySpread = 0;
this.angleAccelerationBase = 0;
this.angleAccelerationSpread = 0; this.sizeBase = 0.0;
this.sizeSpread = 0.0;
this.sizeTween = new Tween(); // store colors in HSL format in a THREE.Vector3 object
// http://en.wikipedia.org/wiki/HSL_and_HSV
this.colorBase = new THREE.Vector3(0.0, 1.0, 0.5);
this.colorSpread = new THREE.Vector3(0.0, 0.0, 0.0);
this.colorTween = new Tween(); this.opacityBase = 1.0;
this.opacitySpread = 0.0;
this.opacityTween = new Tween(); this.blendStyle = THREE.NormalBlending; // false; this.particleArray = [];
this.particlesPerSecond = 100;
this.particleDeathAge = 1.0; ////////////////////////
// EMITTER PROPERTIES //
//////////////////////// this.emitterAge = 0.0;
this.emitterAlive = true;
this.emitterDeathAge = 60; // time (seconds) at which to stop creating particles. // How many particles could be active at any time?
this.particleCount = this.particlesPerSecond * Math.min( this.particleDeathAge, this.emitterDeathAge ); //////////////
// THREE.JS //
////////////// this.particleGeometry = new THREE.Geometry();
this.particleTexture = null;
this.particleMaterial = new THREE.ShaderMaterial(
{
uniforms:
{
texture: { type: "t", value: this.particleTexture },
},
attributes:
{
customVisible: { type: 'f', value: [] },
customAngle: { type: 'f', value: [] },
customSize: { type: 'f', value: [] },
customColor: { type: 'c', value: [] },
customOpacity: { type: 'f', value: [] }
},
vertexShader: particleVertexShader,
fragmentShader: particleFragmentShader,
transparent: true, // alphaTest: 0.5, // if having transparency issues, try including: alphaTest: 0.5,
blending: THREE.NormalBlending, depthTest: true, });
this.particleMesh = new THREE.Mesh();
} ParticleEngine.prototype.setValues = function( parameters )
{
if ( parameters === undefined ) return; // clear any previous tweens that might exist
this.sizeTween = new Tween();
this.colorTween = new Tween();
this.opacityTween = new Tween(); for ( var key in parameters )
this[ key ] = parameters[ key ]; // attach tweens to particles
Particle.prototype.sizeTween = this.sizeTween;
Particle.prototype.colorTween = this.colorTween;
Particle.prototype.opacityTween = this.opacityTween; // calculate/set derived particle engine values
this.particleArray = [];
this.emitterAge = 0.0;
this.emitterAlive = true;
this.particleCount = this.particlesPerSecond * Math.min( this.particleDeathAge, this.emitterDeathAge ); this.particleGeometry = new THREE.Geometry();
this.particleMaterial = new THREE.ShaderMaterial(
{
uniforms:
{
texture: { type: "t", value: this.particleTexture },
},
attributes:
{
customVisible: { type: 'f', value: [] },
customAngle: { type: 'f', value: [] },
customSize: { type: 'f', value: [] },
customColor: { type: 'c', value: [] },
customOpacity: { type: 'f', value: [] }
},
vertexShader: particleVertexShader,
fragmentShader: particleFragmentShader,
transparent: true, alphaTest: 0.5, // if having transparency issues, try including: alphaTest: 0.5,
blending: THREE.NormalBlending, depthTest: true
});
this.particleMesh = new THREE.ParticleSystem();
} // helper functions for randomization
ParticleEngine.prototype.randomValue = function(base, spread)
{
return base + spread * (Math.random() - 0.5);
}
ParticleEngine.prototype.randomVector3 = function(base, spread)
{
var rand3 = new THREE.Vector3( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 );
return new THREE.Vector3().addVectors( base, new THREE.Vector3().multiplyVectors( spread, rand3 ) );
} ParticleEngine.prototype.createParticle = function()
{
var particle = new Particle(); if (this.positionStyle == Type.CUBE)
particle.position = this.randomVector3( this.positionBase, this.positionSpread );
if (this.positionStyle == Type.SPHERE)
{
var z = 2 * Math.random() - 1;
var t = 6.2832 * Math.random();
var r = Math.sqrt( 1 - z*z );
var vec3 = new THREE.Vector3( r * Math.cos(t), r * Math.sin(t), z );
particle.position = new THREE.Vector3().addVectors( this.positionBase, vec3.multiplyScalar( this.positionRadius ) );
} if ( this.velocityStyle == Type.CUBE )
{
particle.velocity = this.randomVector3( this.velocityBase, this.velocitySpread );
}
if ( this.velocityStyle == Type.SPHERE )
{
var direction = new THREE.Vector3().subVectors( particle.position, this.positionBase );
var speed = this.randomValue( this.speedBase, this.speedSpread );
particle.velocity = direction.normalize().multiplyScalar( speed );
} particle.acceleration = this.randomVector3( this.accelerationBase, this.accelerationSpread ); particle.angle = this.randomValue( this.angleBase, this.angleSpread );
particle.angleVelocity = this.randomValue( this.angleVelocityBase, this.angleVelocitySpread );
particle.angleAcceleration = this.randomValue( this.angleAccelerationBase, this.angleAccelerationSpread ); particle.size = this.randomValue( this.sizeBase, this.sizeSpread ); var color = this.randomVector3( this.colorBase, this.colorSpread );
particle.color = new THREE.Color().setHSL( color.x, color.y, color.z ); particle.opacity = this.randomValue( this.opacityBase, this.opacitySpread ); particle.age = 0;
particle.alive = 0; // particles initialize as inactive return particle;
} ParticleEngine.prototype.initialize = function()
{
// link particle data with geometry/material data
for (var i = 0; i < this.particleCount; i++)
{
// remove duplicate code somehow, here and in update function below.
this.particleArray[i] = this.createParticle();
this.particleGeometry.vertices[i] = this.particleArray[i].position;
this.particleMaterial.attributes.customVisible.value[i] = this.particleArray[i].alive;
this.particleMaterial.attributes.customColor.value[i] = this.particleArray[i].color;
this.particleMaterial.attributes.customOpacity.value[i] = this.particleArray[i].opacity;
this.particleMaterial.attributes.customSize.value[i] = this.particleArray[i].size;
this.particleMaterial.attributes.customAngle.value[i] = this.particleArray[i].angle;
} this.particleMaterial.blending = this.blendStyle;
if ( this.blendStyle != THREE.NormalBlending)
this.particleMaterial.depthTest = false; this.particleMesh = new THREE.ParticleSystem( this.particleGeometry, this.particleMaterial );
this.particleMesh.dynamic = true;
this.particleMesh.sortParticles = true;
scene.add( this.particleMesh );
} ParticleEngine.prototype.update = function(dt)
{
var recycleIndices = []; // update particle data
for (var i = 0; i < this.particleCount; i++)
{
if ( this.particleArray[i].alive )
{
this.particleArray[i].update(dt); // check if particle should expire
// could also use: death by size<0 or alpha<0.
if ( this.particleArray[i].age > this.particleDeathAge )
{
this.particleArray[i].alive = 0.0;
recycleIndices.push(i);
}
// update particle properties in shader
this.particleMaterial.attributes.customVisible.value[i] = this.particleArray[i].alive;
this.particleMaterial.attributes.customColor.value[i] = this.particleArray[i].color;
this.particleMaterial.attributes.customOpacity.value[i] = this.particleArray[i].opacity;
this.particleMaterial.attributes.customSize.value[i] = this.particleArray[i].size;
this.particleMaterial.attributes.customAngle.value[i] = this.particleArray[i].angle;
}
} // check if particle emitter is still running
if ( !this.emitterAlive ) return; // if no particles have died yet, then there are still particles to activate
if ( this.emitterAge < this.particleDeathAge )
{
// determine indices of particles to activate
var startIndex = Math.round( this.particlesPerSecond * (this.emitterAge + 0) );
var endIndex = Math.round( this.particlesPerSecond * (this.emitterAge + dt) );
if ( endIndex > this.particleCount )
endIndex = this.particleCount; for (var i = startIndex; i < endIndex; i++)
this.particleArray[i].alive = 1.0;
} // if any particles have died while the emitter is still running, we imediately recycle them
for (var j = 0; j < recycleIndices.length; j++)
{
var i = recycleIndices[j];
this.particleArray[i] = this.createParticle();
this.particleArray[i].alive = 1.0; // activate right away
this.particleGeometry.vertices[i] = this.particleArray[i].position;
} // stop emitter?
this.emitterAge += dt;
if ( this.emitterAge > this.emitterDeathAge ) this.emitterAlive = false;
} ParticleEngine.prototype.destroy = function()
{
scene.remove( this.particleMesh );
}
///////////////////////////////////////////////////////////////////////////////

修改后的ParticleEngine.js代码:

/**
* @author Lee Stemkoski http://www.adelphi.edu/~stemkoski/
*/ /////////////////////////////////////////////////////////////////////////////// /////////////
// SHADERS //
///////////// // attribute: data that may be different for each particle (such as size and color);
// can only be used in vertex shader
// varying: used to communicate data from vertex shader to fragment shader
// uniform: data that is the same for each particle (such as texture) import * as THREE from '../../build/three.module.js'; let particleVertexShader =
[
"attribute vec3 customColor;",
"attribute float customOpacity;",
"attribute float customSize;",
"attribute float customAngle;",
"attribute float customVisible;", // float used as boolean (0 = false, 1 = true)
"varying vec4 vColor;",
"varying float vAngle;",
"void main()",
"{",
"if ( customVisible > 0.5 )", // true
"vColor = vec4( customColor, customOpacity );", // set color associated to vertex; use later in fragment shader.
"else", // false
"vColor = vec4(0.0, 0.0, 0.0, 0.0);", // make particle invisible. "vAngle = customAngle;", "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
"gl_PointSize = customSize * ( 300.0 / length( mvPosition.xyz ) );", // scale particles as objects in 3D space
"gl_Position = projectionMatrix * mvPosition;",
"}"
].join("\n"); let particleFragmentShader =
[
"uniform sampler2D texture;",
"varying vec4 vColor;",
"varying float vAngle;",
"void main()",
"{",
"gl_FragColor = vColor;", "float c = cos(vAngle);",
"float s = sin(vAngle);",
"vec2 rotatedUV = vec2(c * (gl_PointCoord.x - 0.5) + s * (gl_PointCoord.y - 0.5) + 0.5,",
"c * (gl_PointCoord.y - 0.5) - s * (gl_PointCoord.x - 0.5) + 0.5);", // rotate UV coordinates to rotate texture
"vec4 rotatedTexture = texture2D( texture, rotatedUV );",
"gl_FragColor = gl_FragColor * rotatedTexture;", // sets an otherwise white particle texture to desired color
"}"
].join("\n"); /////////////////////////////////////////////////////////////////////////////// /////////////////
// TWEEN CLASS //
///////////////// function ParticleTween(timeArray, valueArray) {
this.times = timeArray || [];
this.values = valueArray || [];
} ParticleTween.prototype.lerp = function (t) {
var i = 0;
var n = this.times.length;
while (i < n && t > this.times[i])
i++;
if (i == 0) return this.values[0];
if (i == n) return this.values[n - 1];
var p = (t - this.times[i - 1]) / (this.times[i] - this.times[i - 1]);
if (this.values[0] instanceof THREE.Vector3)
return this.values[i - 1].clone().lerp(this.values[i], p);
else // its a float
return this.values[i - 1] + p * (this.values[i] - this.values[i - 1]);
} /////////////////////////////////////////////////////////////////////////////// ////////////////////
// PARTICLE CLASS //
//////////////////// function Particle() {
this.position = new THREE.Vector3();
this.velocity = new THREE.Vector3(); // units per second
this.acceleration = new THREE.Vector3(); this.angle = 0;
this.angleVelocity = 0; // degrees per second
this.angleAcceleration = 0; // degrees per second, per second this.size = 16.0; this.color = new THREE.Color();
this.opacity = 1.0; this.age = 0;
this.alive = 0; // use float instead of boolean for shader purposes
} Particle.prototype.update = function (dt) {
this.position.add(this.velocity.clone().multiplyScalar(dt));
this.velocity.add(this.acceleration.clone().multiplyScalar(dt)); // convert from degrees to radians: 0.01745329251 = Math.PI/180
this.angle += this.angleVelocity * 0.01745329251 * dt;
this.angleVelocity += this.angleAcceleration * 0.01745329251 * dt; this.age += dt; // if the tween for a given attribute is nonempty,
// then use it to update the attribute's value if (this.sizeTween.times.length > 0)
this.size = this.sizeTween.lerp(this.age); if (this.colorTween.times.length > 0) {
var colorHSL = this.colorTween.lerp(this.age);
this.color = new THREE.Color().setHSL(colorHSL.x, colorHSL.y, colorHSL.z);
} if (this.opacityTween.times.length > 0)
this.opacity = this.opacityTween.lerp(this.age);
} /////////////////////////////////////////////////////////////////////////////// ///////////////////////////
// PARTICLE ENGINE CLASS //
/////////////////////////// let Type = Object.freeze({ "CUBE": 1, "SPHERE": 2 }); let scene; function ParticleEngine(scene_) {
/////////////////////////
// PARTICLE PROPERTIES //
///////////////////////// scene = scene_; this.positionStyle = Type.CUBE;
this.positionBase = new THREE.Vector3();
// cube shape data
this.positionSpread = new THREE.Vector3();
// sphere shape data
this.positionRadius = 0; // distance from base at which particles start this.velocityStyle = Type.CUBE;
// cube movement data
this.velocityBase = new THREE.Vector3();
this.velocitySpread = new THREE.Vector3();
// sphere movement data
// direction vector calculated using initial position
this.speedBase = 0;
this.speedSpread = 0; this.accelerationBase = new THREE.Vector3();
this.accelerationSpread = new THREE.Vector3(); this.angleBase = 0;
this.angleSpread = 0;
this.angleVelocityBase = 0;
this.angleVelocitySpread = 0;
this.angleAccelerationBase = 0;
this.angleAccelerationSpread = 0; this.sizeBase = 0.0;
this.sizeSpread = 0.0;
this.sizeTween = new ParticleTween(); // store colors in HSL format in a THREE.Vector3 object
// http://en.wikipedia.org/wiki/HSL_and_HSV
this.colorBase = new THREE.Vector3(0.0, 1.0, 0.5);
this.colorSpread = new THREE.Vector3(0.0, 0.0, 0.0);
this.colorTween = new ParticleTween(); this.opacityBase = 1.0;
this.opacitySpread = 0.0;
this.opacityTween = new ParticleTween(); this.blendStyle = THREE.NormalBlending; // false; this.particleArray = [];
this.particlesPerSecond = 100;
this.particleDeathAge = 1.0; ////////////////////////
// EMITTER PROPERTIES //
//////////////////////// this.emitterAge = 0.0;
this.emitterAlive = true;
this.emitterDeathAge = 60; // time (seconds) at which to stop creating particles. // How many particles could be active at any time?
this.particleCount = this.particlesPerSecond * Math.min(this.particleDeathAge, this.emitterDeathAge); //////////////
// THREE.JS //
////////////// this.particleGeometry = new THREE.BufferGeometry();
this.particleTexture = null;
this.particleMaterial = new THREE.ShaderMaterial(
{
uniforms:
{
texture: { type: "t", value: this.particleTexture },
},
vertexShader: particleVertexShader,
fragmentShader: particleFragmentShader,
blending: THREE.NormalBlending,
depthTest: false,
side: THREE.DoubleSide,
transparent: true,
// alphaTest: 0.5, // if having transparency issues, try including: alphaTest: 0.5,
opacity: 1.0
});
this.particleMesh = new THREE.Mesh();
} ParticleEngine.prototype.setValues = function (parameters) {
if (parameters === undefined) return; // clear any previous tweens that might exist
this.sizeTween = new ParticleTween();
this.colorTween = new ParticleTween();
this.opacityTween = new ParticleTween(); for (var key in parameters)
this[key] = parameters[key]; // attach tweens to particles
Particle.prototype.sizeTween = this.sizeTween;
Particle.prototype.colorTween = this.colorTween;
Particle.prototype.opacityTween = this.opacityTween; // calculate/set derived particle engine values
this.particleArray = [];
this.emitterAge = 0.0;
this.emitterAlive = true;
this.particleCount = this.particlesPerSecond * Math.min(this.particleDeathAge, this.emitterDeathAge); this.particleGeometry = new THREE.Geometry();
this.particleMaterial = new THREE.ShaderMaterial(
{
uniforms:
{
texture: { type: "t", value: this.particleTexture },
},
vertexShader: particleVertexShader,
fragmentShader: particleFragmentShader,
blending: THREE.NormalBlending,
depthTest: false,
side: THREE.DoubleSide,
transparent: true,
// alphaTest: 0.5, // if having transparency issues, try including: alphaTest: 0.5,
opacity: 1.0
});
this.particleMesh = new THREE.Points();
} // helper functions for randomization
ParticleEngine.prototype.randomValue = function (base, spread) {
return base + spread * (Math.random() - 0.5);
} ParticleEngine.prototype.randomVector3 = function (base, spread) {
var rand3 = new THREE.Vector3(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5);
return new THREE.Vector3().addVectors(base, new THREE.Vector3().multiplyVectors(spread, rand3));
} ParticleEngine.prototype.createParticle = function () {
var particle = new Particle(); if (this.positionStyle == Type.CUBE)
particle.position = this.randomVector3(this.positionBase, this.positionSpread);
if (this.positionStyle == Type.SPHERE) {
var z = 2 * Math.random() - 1;
var t = 6.2832 * Math.random();
var r = Math.sqrt(1 - z * z);
var vec3 = new THREE.Vector3(r * Math.cos(t), r * Math.sin(t), z);
particle.position = new THREE.Vector3().addVectors(this.positionBase, vec3.multiplyScalar(this.positionRadius));
} if (this.velocityStyle == Type.CUBE) {
particle.velocity = this.randomVector3(this.velocityBase, this.velocitySpread);
}
if (this.velocityStyle == Type.SPHERE) {
var direction = new THREE.Vector3().subVectors(particle.position, this.positionBase);
var speed = this.randomValue(this.speedBase, this.speedSpread);
particle.velocity = direction.normalize().multiplyScalar(speed);
} particle.acceleration = this.randomVector3(this.accelerationBase, this.accelerationSpread); particle.angle = this.randomValue(this.angleBase, this.angleSpread);
particle.angleVelocity = this.randomValue(this.angleVelocityBase, this.angleVelocitySpread);
particle.angleAcceleration = this.randomValue(this.angleAccelerationBase, this.angleAccelerationSpread); particle.size = this.randomValue(this.sizeBase, this.sizeSpread); var color = this.randomVector3(this.colorBase, this.colorSpread);
particle.color = new THREE.Color().setHSL(color.x, color.y, color.z); particle.opacity = this.randomValue(this.opacityBase, this.opacitySpread); particle.age = 0;
particle.alive = 0; // particles initialize as inactive return particle;
} ParticleEngine.prototype.initialize = function () { let customVisible = [];
let customColor = [];
let customOpacity = [];
let customSize = [];
let customAngle = []; // link particle data with geometry/material data
for (var i = 0; i < this.particleCount; i++) {
// remove duplicate code somehow, here and in update function below.
this.particleArray[i] = this.createParticle();
this.particleGeometry.vertices[i] = this.particleArray[i].position; customVisible.push(this.particleArray[i].alive);
customColor.push(this.particleArray[i].color);
customOpacity.push(this.particleArray[i].opacity);
customSize.push(this.particleArray[i].size);
customAngle.push(this.particleArray[i].angle);
} // 原ParticleEngine.js依赖的是旧版本的three.js,新版本的three.js的ShaderMaterial不支持attributes
// 此处使用BufferGeometry作了修改
for (var i = 0; i < this.particleCount - 2; i += 3) {
let face = new THREE.Face3(i, i + 1, i + 2);
this.particleGeometry.faces.push(face);
} this.bufferGeometry = new THREE.BufferGeometry();
this.bufferGeometry.fromGeometry(this.particleGeometry); this.bufferGeometry.setAttribute('customVisible', new THREE.BufferAttribute(new Float32Array(customVisible), 1));
this.bufferGeometry.setAttribute('customColor', new THREE.BufferAttribute(new Float32Array(customColor.length * 3), 3).copyColorsArray(customColor));
this.bufferGeometry.setAttribute('customOpacity', new THREE.BufferAttribute(new Float32Array(customOpacity), 1));
this.bufferGeometry.setAttribute('customSize', new THREE.BufferAttribute(new Float32Array(customSize), 1));
this.bufferGeometry.setAttribute('customAngle', new THREE.BufferAttribute(new Float32Array(customAngle), 1)); this.particleMaterial.blending = this.blendStyle;
if (this.blendStyle != THREE.NormalBlending)
this.particleMaterial.depthTest = false; this.particleMesh = new THREE.Points(this.bufferGeometry, this.particleMaterial);
this.particleMesh.dynamic = true;
this.particleMesh.sortParticles = true;
scene.add(this.particleMesh);
this.isShow = true;
} ParticleEngine.prototype.update = function (dt) {
var recycleIndices = []; let customVisible = [];
let customColor = [];
let customOpacity = [];
let customSize = [];
let customAngle = [];
// update particle data
for (var i = 0; i < this.particleCount; i++) {
if (this.particleArray[i].alive) {
this.particleArray[i].update(dt);
this.particleGeometry.vertices[i] = this.particleArray[i].position; // check if particle should expire
// could also use: death by size<0 or alpha<0.
if (this.particleArray[i].age > this.particleDeathAge) {
this.particleArray[i].alive = 0.0;
recycleIndices.push(i);
} // update particle properties in shader
customVisible.push(this.particleArray[i].alive);
customColor.push(this.particleArray[i].color);
customOpacity.push(this.particleArray[i].opacity);
customSize.push(this.particleArray[i].size);
customAngle.push(this.particleArray[i].angle);
}
} // bufferGeometry需要更新
this.bufferGeometry.fromGeometry(this.particleGeometry); this.bufferGeometry.getAttribute('customVisible').copyArray(customVisible);
this.bufferGeometry.getAttribute('customColor').copyColorsArray(customColor);
this.bufferGeometry.getAttribute('customOpacity').copyArray(customOpacity);
this.bufferGeometry.getAttribute('customSize').copyArray(customSize);
this.bufferGeometry.getAttribute('customAngle').copyArray(customAngle); this.bufferGeometry.getAttribute('customVisible').needsUpdate = true;
this.bufferGeometry.getAttribute('customColor').needsUpdate = true;
this.bufferGeometry.getAttribute('customOpacity').needsUpdate = true;
this.bufferGeometry.getAttribute('customSize').needsUpdate = true;
this.bufferGeometry.getAttribute('customAngle').needsUpdate = true; // check if particle emitter is still running
if (!this.emitterAlive) return; // if no particles have died yet, then there are still particles to activate
if (this.emitterAge < this.particleDeathAge) {
// determine indices of particles to activate
var startIndex = Math.round(this.particlesPerSecond * (this.emitterAge + 0));
var endIndex = Math.round(this.particlesPerSecond * (this.emitterAge + dt));
if (endIndex > this.particleCount)
endIndex = this.particleCount; for (var i = startIndex; i < endIndex; i++)
this.particleArray[i].alive = 1.0;
} // if any particles have died while the emitter is still running, we imediately recycle them
for (var j = 0; j < recycleIndices.length; j++) {
var i = recycleIndices[j];
this.particleArray[i] = this.createParticle();
this.particleArray[i].alive = 1.0; // activate right away
this.particleGeometry.vertices[i] = this.particleArray[i].position;
} // stop emitter?
this.emitterAge += dt;
if (this.emitterAge > this.emitterDeathAge) this.emitterAlive = false;
} ParticleEngine.prototype.destroy = function () {
scene.remove(this.particleMesh);
this.isShow = false;
} ParticleEngine.prototype.show = function () {
scene.add(this.particleMesh);
this.isShow = true;
} ParticleEngine.prototype.hide = function () {
scene.remove(this.particleMesh);
this.isShow = false;
}
/////////////////////////////////////////////////////////////////////////////// export { ParticleEngine, Type, ParticleTween }

MySmoke.js创建烟雾代码:

//消防烟雾效果

import * as THREE from '../build/three.module.js';
import { ParticleEngine, Type as ParticleType, ParticleTween } from '../js/particle-engine/ParticleEngine.js' let particleEngine;
let clockForParticleEngine = new THREE.Clock();
let scene;
let position; /** 创建烟雾 */
function createSmoke(scene_, position_) {
scene = scene_;
position = position_; if (particleEngine) {
particleEngine.destroy();
} particleEngine = new ParticleEngine(scene);
particleEngine.setValues({
positionStyle: ParticleType.CUBE,
positionBase: new THREE.Vector3(position.x, position.y + 0, position.z),
positionSpread: new THREE.Vector3(10, 0, 10), velocityStyle: ParticleType.CUBE,
velocityBase: new THREE.Vector3(0, 200, 0),
velocitySpread: new THREE.Vector3(200, 150, 200),
accelerationBase: new THREE.Vector3(0, -100, 0), particleTexture: new THREE.TextureLoader().load('images/particle-engine/smokeparticle.png'), angleBase: 0,
angleSpread: 720,
angleVelocityBase: 0,
angleVelocitySpread: 720, sizeTween: new ParticleTween([0, 1], [64, 256]),
opacityTween: new ParticleTween([0.8, 2], [0.5, 0]),
colorTween: new ParticleTween([0.4, 1], [new THREE.Vector3(0, 0, 0.2), new THREE.Vector3(0, 0, 0.5)]), particlesPerSecond: 200,
particleDeathAge: 2.0,
emitterDeathAge: 60
});
particleEngine.initialize();
} /** 更新烟雾 */
function updateParticle() {
let delta = clockForParticleEngine.getDelta();
if (delta < 0.2) { // 1秒 / 5帧 = 0.2 秒/帧
particleEngine && particleEngine.update(delta * 0.5);
} else {
if (scene && position) {
let isShow = false;
if (particleEngine && particleEngine.isShow) {
isShow = true;
}
createSmoke(scene, position);
if (!isShow) {
particleEngine && particleEngine.hide();
}
}
}
} export { createSmoke, updateParticle, particleEngine }

火焰效果MyFire3.js代码:

/**
* 火焰
*/ import * as THREE from '../build/three.module.js'; let MyFire3 = function () { let fireVertexShader = `
attribute vec4 orientation;
attribute vec3 offset;
attribute vec2 scale;
attribute float life;
attribute float random; varying vec2 vUv;
varying float vRandom;
varying float vAlpha; float range(float oldValue, float oldMin, float oldMax, float newMin, float newMax) {
float oldRange = oldMax - oldMin;
float newRange = newMax - newMin;
return (((oldValue - oldMin) * newRange) / oldRange) + newMin;
} // From Inigo Quilez http://www.iquilezles.org/www/articles/functions/functions.htm
float pcurve(float x, float a, float b) {
float k = pow(a + b, a + b) / (pow(a, a) * pow(b, b));
return k * pow(x, a) * pow(1.0 - x, b);
} void main() {
vUv = uv;
vRandom = random; vAlpha = pcurve(life, 1.0, 2.0); vec3 pos = position; pos.xy *= scale * vec2(range(pow(life, 1.5), 0.0, 1.0, 1.0, 0.6), range(pow(life, 1.5), 0.0, 1.0, 0.6, 1.2)); vec4 or = orientation;
vec3 vcV = cross(or.xyz, pos);
pos = vcV * (2.0 * or.w) + (cross(or.xyz, vcV) * 2.0 + pos); gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`; let fireFragmentShader = `
uniform sampler2D uMap;
uniform vec3 uColor1;
uniform vec3 uColor2;
uniform float uTime; varying vec2 vUv;
varying float vAlpha;
varying float vRandom; void main() {
vec2 uv = vUv; float spriteLength = 10.0;
uv.x /= spriteLength;
float spriteIndex = mod(uTime * 0.1 + vRandom * 2.0, 1.0);
uv.x += floor(spriteIndex * spriteLength) / spriteLength; vec4 map = texture2D(uMap, uv); gl_FragColor.rgb = mix(uColor2, uColor1, map.r);
gl_FragColor.a = vAlpha * map.a;
}
`; let embersVertexShader = `
attribute float size;
attribute float life;
attribute vec3 offset; varying float vAlpha; // From Inigo Quilez http://www.iquilezles.org/www/articles/functions/functions.htm
float impulse(float k, float x) {
float h = k * x;
return h * exp(1.0 - h);
} void main() {
vAlpha = impulse(6.28, life); vec3 pos = position;
pos += offset * vec3(life * 0.7 + 0.3, life * 0.9 + 0.1, life * 0.7 + 0.3); vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
gl_PointSize = size * (80.0 / length(mvPosition.xyz));
gl_Position = projectionMatrix * mvPosition;
}
`; let embersFragmentShader = `
uniform sampler2D uMap;
uniform vec3 uColor; varying float vAlpha; void main() {
vec2 uv = vec2(gl_PointCoord.x, 1.0 - gl_PointCoord.y);
vec4 mask = texture2D(uMap, uv); gl_FragColor.rgb = uColor;
gl_FragColor.a = mask.a * vAlpha * 0.8;
}
`; let hazeVertexShader = `
attribute vec3 base;
attribute vec3 offset;
attribute vec4 orientation;
attribute vec2 scale;
attribute float life; varying float vAlpha;
varying vec2 vUv; // From Inigo Quilez http://www.iquilezles.org/www/articles/functions/functions.htm
float impulse(float k, float x) {
float h = k * x;
return h * exp(1.0 - h);
} float pcurve(float x, float a, float b) {
float k = pow(a + b, a + b) / (pow(a, a) * pow(b, b));
return k * pow(x, a) * pow(1.0 - x, b);
} void main() {
vUv = uv;
vAlpha = pcurve(life, 1.0, 2.0); vec3 pos = position; pos.xy *= scale * (life * 0.7 + 0.3); vec4 or = orientation;
vec3 vcV = cross(or.xyz, pos);
pos = vcV * (2.0 * or.w) + (cross(or.xyz, vcV) * 2.0 + pos); pos += base;
pos += offset * vec3(life * 0.7 + 0.3, life * 0.9 + 0.1, life * 0.7 + 0.3); gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);;
}
`; let hazeFragmentShader = `
uniform sampler2D uMap;
uniform sampler2D uMask;
uniform vec2 uResolution; varying float vAlpha;
varying vec2 vUv; void main() {
vec2 uv = gl_FragCoord.xy / uResolution;
vec2 mask = texture2D(uMask, vUv).ra - vec2(0.5);
uv -= mask * 0.1;
vec4 tex = texture2D(uMap, uv); gl_FragColor.rgb = tex.rgb;
gl_FragColor.a = vAlpha * 0.5;
}
`; function random(min, max, precision) {
var p = Math.pow(10, precision);
return Math.round((min + Math.random() * (max - min)) * p) / p;
} let _scene;
let _renderer;
let _camera;
let _controls;
let _rtt;
let _fire;
let _width;
let _height; this.objs = []; let _self = this; this._isShow = false; let _pos_x;
let _pos_y;
let _pos_z; this.config = function (scene_, renderer_, camera_, controls_) {
_width = 1920;
_height = 1040; _renderer = renderer_;
_scene = scene_;
_camera = camera_;
_controls = controls_; var _parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBAFormat,
stencilBuffer: false
};
_rtt = new THREE.WebGLRenderTarget(_width * 0.5, _height * 0.5, _parameters);
} this.setPosition = function (x, y, z) {
_pos_x = x;
_pos_y = y;
_pos_z = z;
} this.showFire = function () {
initFire();
initEmbers();
initHaze();
this._isShow = true;
} this.refresh = function () {
_self.loop();
_self.loop2();
_self.loop3();
} this.isShow = function () {
return this._isShow;
} this.hide = function () {
_self.objs.map(obj => {
_scene.remove(obj);
});
this._isShow = false;
} this.show = function () {
_self.objs.map(obj => {
_scene.add(obj);
});
this._isShow = true;
} //=====// Fire //========================================// function initFire() {
var _geometry, _shader, _mesh, _group;
var _num = 50; var _x = new THREE.Vector3(1, 0, 0);
var _y = new THREE.Vector3(0, 1, 0);
var _z = new THREE.Vector3(0, 0, 1); var _tipTarget = new THREE.Vector3();
var _tip = new THREE.Vector3();
var _diff = new THREE.Vector3(); var _quat = new THREE.Quaternion();
var _quat2 = new THREE.Quaternion(); (function () {
initGeometry();
initInstances();
initShader();
initMesh();
})(); function initGeometry() {
_geometry = new THREE.InstancedBufferGeometry();
_geometry.maxInstancedCount = _num; var shape = new THREE.PlaneBufferGeometry(200, 200);
shape.translate(0, 0.4, 0);
var data = shape.attributes; _geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(data.position.array), 3));
_geometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(data.uv.array), 2));
_geometry.addAttribute('normal', new THREE.BufferAttribute(new Float32Array(data.normal.array), 3));
_geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(shape.index.array), 1));
shape.dispose();
} function initInstances() {
var orientation = new THREE.InstancedBufferAttribute(new Float32Array(_num * 4), 4);
var randoms = new THREE.InstancedBufferAttribute(new Float32Array(_num), 1);
var scale = new THREE.InstancedBufferAttribute(new Float32Array(_num * 2), 2);
var life = new THREE.InstancedBufferAttribute(new Float32Array(_num), 1); for (let i = 0; i < _num; i++) {
orientation.setXYZW(i, 0, 0, 0, 1);
life.setX(i, i / _num + 1);
} _geometry.addAttribute('orientation', orientation);
_geometry.addAttribute('scale', scale);
_geometry.addAttribute('life', life);
_geometry.addAttribute('random', randoms);
} function initShader() {
var uniforms = {
uMap: {
type: 't',
value: null
},
uColor1: {
type: 'c',
value: new THREE.Color(0x961800)
}, // red
uColor2: {
type: 'c',
value: new THREE.Color(0x4b5828)
}, // yellow
uTime: {
type: 'f',
value: 0
},
}; _shader = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: fireVertexShader,
fragmentShader: fireFragmentShader,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
side: THREE.DoubleSide,
}); var textureLoader = new THREE.TextureLoader();
textureLoader.load('images/myFire3/flame.png', t => _shader.uniforms.uMap.value = t);
} function initMesh() {
_group = new THREE.Group();
_mesh = new THREE.Mesh(_geometry, _shader);
_mesh.frustumCulled = false;
_group.add(_mesh);
_scene.add(_group);
_self.objs.push(_group);
_fire = _group;
} _self.loop = function () {
let e = 100;
_shader.uniforms.uTime.value = e * 0.001; var life = _geometry.attributes.life;
var orientation = _geometry.attributes.orientation;
var scale = _geometry.attributes.scale;
var randoms = _geometry.attributes.random; for (let i = 0; i < _num; i++) {
var value = life.array[i];
value += 0.04; if (value > 1) {
value -= 1; _quat.setFromAxisAngle(_y, random(0, 3.14, 3));
_quat2.setFromAxisAngle(_x, random(-1, 1, 2) * 0.1);
_quat.multiply(_quat2);
_quat2.setFromAxisAngle(_z, random(-1, 1, 2) * 0.3);
_quat.multiply(_quat2);
orientation.setXYZW(i, _quat.x, _quat.y, _quat.z, _quat.w); scale.setXY(i, random(0.8, 1.2, 3), random(0.8, 1.2, 3));
randoms.setX(i, random(0, 1, 3));
} life.setX(i, value);
}
life.needsUpdate = true;
orientation.needsUpdate = true;
scale.needsUpdate = true;
randoms.needsUpdate = true; _group.position.x = _pos_x; //Math.sin(e * 0.002) * 1.4;
_group.position.y = _pos_y + 50; //Math.cos(e * 0.0014) * 0.2;
_group.position.z = _pos_z; //Math.cos(e * 0.0014) * 0.5; let tipOffset = 0.4;
_tipTarget.copy(_group.position);
_tipTarget.y += tipOffset;
_tip.lerp(_tipTarget, 0.1); _diff.copy(_tip);
_diff.sub(_group.position);
let length = _diff.length();
//_group.scale.y = (length / tipOffset - 1) * 0.4 + 1; _group.quaternion.setFromUnitVectors(_y, _diff.normalize());
}
} //=====// Embers //========================================// function initEmbers() {
var _geometry, _shader, _points;
var _num = 8; (function () {
initGeometry();
initShader();
initMesh();
})(); function initGeometry() {
_geometry = new THREE.BufferGeometry();
_geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(_num * 3), 3));
_geometry.addAttribute('offset', new THREE.BufferAttribute(new Float32Array(_num * 3), 3));
_geometry.addAttribute('size', new THREE.BufferAttribute(new Float32Array(_num), 1));
_geometry.addAttribute('life', new THREE.BufferAttribute(new Float32Array(_num), 1));
var scale = new THREE.InstancedBufferAttribute(new Float32Array(_num * 2), 2);
_geometry.addAttribute('scale', scale); for (var i = 0; i < _num; i++) {
_geometry.attributes.life.setX(i, random(0, 1, 3) + 1);
}
} function initShader() {
var uniforms = {
uMap: {
type: 't',
value: null
},
uColor: {
type: 'c',
value: new THREE.Color(0xffe61e)
},
}; _shader = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: embersVertexShader,
fragmentShader: embersFragmentShader,
blending: THREE.AdditiveBlending,
transparent: true,
depthTest: false,
}); var textureLoader = new THREE.TextureLoader();
textureLoader.load('images/myFire3/ember.png', t => _shader.uniforms.uMap.value = t);
} function initMesh() {
_points = new THREE.Points(_geometry, _shader);
_points.frustumCulled = false;
_scene.add(_points);
_self.objs.push(_points);
} _self.loop2 = function () {
var life = _geometry.attributes.life;
var position = _geometry.attributes.position;
var size = _geometry.attributes.size;
var offset = _geometry.attributes.offset;
var scale = _geometry.attributes.scale;
for (let i = 0; i < _num; i++) {
var value = life.array[i];
value += 0.02; if (value > 1) {
value -= 1; position.setXYZ(i, _pos_x, _pos_y + 0.1, _pos_z);
offset.setXYZ(i,
random(-150, 150, 3),
random(100, 300, 3),
random(-100, 100, 3)
);
size.setX(i, random(20, 100, 3)); scale.setXY(i, 50, 50);
} life.setX(i, value);
} life.needsUpdate = true;
position.needsUpdate = true;
size.needsUpdate = true;
offset.needsUpdate = true;
}
} //=====// Haze //========================================// function initHaze() {
var _geometry, _shader, _mesh; var _num = 4; var _z = new THREE.Vector3(0, 0, 1);
var _quat = new THREE.Quaternion();
var _quat2 = new THREE.Quaternion(); (function () {
initGeometry();
initInstances();
initShader();
initMesh();
window.addEventListener('resize', resizeHaze, false);
resizeHaze();
})(); function initGeometry() {
_geometry = new THREE.InstancedBufferGeometry();
_geometry.maxInstancedCount = _num; var shape = new THREE.PlaneBufferGeometry(0.1, 0.1);
var data = shape.attributes; _geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(data.position.array), 3));
_geometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(data.uv.array), 2));
_geometry.addAttribute('normal', new THREE.BufferAttribute(new Float32Array(data.normal.array), 3));
_geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(shape.index.array), 1));
shape.dispose();
} function initInstances() {
var base = new THREE.InstancedBufferAttribute(new Float32Array(_num * 3), 3);
var offset = new THREE.InstancedBufferAttribute(new Float32Array(_num * 3), 3);
var orientation = new THREE.InstancedBufferAttribute(new Float32Array(_num * 4), 4);
var scale = new THREE.InstancedBufferAttribute(new Float32Array(_num * 2), 2);
var rotation = new THREE.InstancedBufferAttribute(new Float32Array(_num), 1);
var life = new THREE.InstancedBufferAttribute(new Float32Array(_num), 1); for (let i = 0; i < _num; i++) {
orientation.setXYZW(i, 0, 0, 0, 1);
life.setX(i, i / _num + 1);
} _geometry.addAttribute('base', base);
_geometry.addAttribute('offset', offset);
_geometry.addAttribute('orientation', orientation);
_geometry.addAttribute('scale', scale);
_geometry.addAttribute('rotation', rotation);
_geometry.addAttribute('life', life);
} function initShader() {
let dpr = _renderer.getPixelRatio();
var uniforms = {
uMap: {
type: 't',
value: null
},
uMask: {
type: 't',
value: null
},
uResolution: {
type: 'v2',
value: new THREE.Vector2(_width * dpr, _height * dpr)
},
}; _shader = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: hazeVertexShader,
fragmentShader: hazeFragmentShader,
transparent: true,
depthTest: false,
}); var textureLoader = new THREE.TextureLoader();
textureLoader.load('images/myFire3/haze.png', t => _shader.uniforms.uMask.value = t);
} function initMesh() {
_mesh = new THREE.Mesh(_geometry, _shader);
_mesh.frustumCulled = false;
_scene.add(_mesh);
_self.objs.push(_mesh);
} function resizeHaze() {
let dpr = _renderer.getPixelRatio();
_shader.uniforms.uMap.value = _rtt.texture;
_shader.uniforms.uResolution.value.set(_width * dpr, _height * dpr);
} _self.loop3 = function () { let e = 100;
_mesh.visible = false;
//_renderer.render(_scene, _camera, _rtt);
_mesh.visible = true; var life = _geometry.attributes.life;
var base = _geometry.attributes.base;
var offset = _geometry.attributes.offset;
var scale = _geometry.attributes.scale;
var orientation = _geometry.attributes.orientation;
var rotation = _geometry.attributes.rotation;
for (let i = 0; i < _num; i++) {
var value = life.array[i];
value += 0.008; if (value > 1) {
value -= 1; rotation.setX(i, random(0, 3.14, 3)); base.setXYZ(i, _pos_x, _pos_y + 0.1, _pos_z);
offset.setXYZ(i,
random(-150, 150, 3),
random(100, 300, 3),
0
);
//scale.setXY(i, random(0.6, 1.2, 3), random(0.6, 1.2, 3));
scale.setXY(i, 50, 50);
} _quat.copy(_camera.quaternion);
_quat2.setFromAxisAngle(_z, rotation.array[i]);
_quat.multiply(_quat2);
orientation.setXYZW(i, _quat.x, _quat.y, _quat.z, _quat.w); life.setX(i, value);
} life.needsUpdate = true;
base.needsUpdate = true;
scale.needsUpdate = true;
offset.needsUpdate = true;
orientation.needsUpdate = true;
}
} } MyFire3.prototype.constructor = MyFire3; export { MyFire3 }

在主文件中添加如下代码实现火焰烟雾效果:

import { createSmoke, updateParticle, particleEngine } from '../js.my/MySmoke.js'
import { MyFire3 } from '../js.my/MyFire3.js'; let showFire = function () {
myFire3 = new MyFire3();
myFire3.config(scene, renderer, camera, controls);
myFire3.setPosition(417, 0, 1134);
if (planSelect.getPosition()) {
let pos = planSelect.getPosition();
myFire3.setPosition(pos.x, pos.y, pos.z);
createSmoke(scene, pos);
}
myFire3.showFire();
planTypeSelect.setFire(myFire3);
planTypeSelect.setSmoke(particleEngine);
} if (!myFire3) {
showFire();
} else {
if (myFire3.isShow()) {
myFire3.hide();
particleEngine.hide();
} else {
showFire();
particleEngine.show();
}
}

说明:planSelect.getPosition()选择应急预案获取事件位置信息,planTypeSelect选择预案类型,当预案类型变更时隐藏火焰和烟雾,不是消防类预案时不显示火焰和烟雾,所以需要把相关变量传给planTypeSelect。

效果图:

three.js 消防模拟火焰烟雾效果的更多相关文章

  1. 使用webgl(three.js)搭建一个3D智慧园区、3D建筑,3D消防模拟,web版3D,bim管理系统——第四课

    序:这段时间忙于奔波,好久没有更新了,今天更新一下,继续上节课的完善讲解,算是对前段时间的一个总结吧.披星戴月的时光也算有点应用效果了. 对于webgl(three.js)性能这一块我在上节课< ...

  2. 使用webgl(three.js)搭建一个3D建筑,3D消防模拟——第三课

    项目背景 消防安全一直是各大都市关注的重要课题,在消防体系中,特别是高楼消防体系中,消防系统整体布控与监控,火情有效准确定位,防火器材定位,人员逃生路径规划,火情预警,消防演习都是特别重要的环节.所以 ...

  3. js之滚动置顶效果

    0.js获取高度 ? 1 2 3 4 5 6 document.all   // 只有ie认识   document.body.clientHeight              // 文档的高,屏幕 ...

  4. 6 cocos2dx粒子效果,类图关系,系统原生粒子和自己定义粒子效果,粒子编译器软件,爆炸粒子效果,烟花效果,火焰效果,流星效果,漩涡粒子效果,雪花效果,烟雾效果,太阳效果,下雨效果

     1 粒子 演示样例 2 类图关系 3 系统原生粒子 CCParticleSystem 全部粒子系统的父类 CCParticleSystemPoint. CCParticleSystemQuad ...

  5. css3 模拟标牌震荡效果

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  6. arcgis api 4.x for js 结合 Echarts4 实现散点图效果(附源码下载)

    前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 4.x for js:esri 官网 api,里面详细的介绍 arcgis api 4.x 各个类 ...

  7. js怎么模拟点击网页元素

    在测试页面中,引入jquery源文件,并添加一个div标签,一个a标签,为了演示效果a标签暂时不添加地址 通过jquery为div标签绑定一个点击事件,这个事件是被动执行的.意思是要点击才会触发的 在 ...

  8. Amazing!!CSS 也能实现烟雾效果?

    最近利用 CSS 实现了一些看似超出 CSS 能力的效果: 巧用渐变实现高级感拉满的背景光动画 Amazing!!CSS 也能实现极光? 本文继续此系列,本文主要想探讨一下,使用 CSS 能否比较好的 ...

  9. 【前端】js代码模拟用户键盘鼠标输入

    js代码模拟用户键盘鼠标输入 原生js var event = new Event('mousewheel'); event.wheelDelta = 360 document.dispatchEve ...

  10. Rainyday.js – 使用 JavaScript 实现雨滴效果

    Rainyday.js 背后的想法是创建一个 JavaScript 库,利用 HTML5 Canvas 渲染一个雨滴落在玻璃表面的动画.Rainyday.js 有功能可扩展的 API,例如碰撞检测和易 ...

随机推荐

  1. python列表之索引及len()函数

    我们在刚开始使用列表的时候,经常会遇到这种错误 list_1 = ['one', 'two', 'three', 'four', 'five'] print(list_1[5]) 这段代码看上去是没有 ...

  2. 自学day7 数组

    typora-copy-images-to: media 数组 一.概念 对象中可以通过键值对存储多个数据,且数据的类型是没有限制的,所以通常会存储一个商品的信息或一个人的信息: var obj = ...

  3. 配置postcss-pxtorem报:options has an unknown property 'plugins'

    闲聊: 小颖最近在坐大屏相关的项目,要写适配,之前用的:postcss-px2rem.px2rem-loader,和朋友闲聊呢他说他们也在写大屏,不过他们用的 postcss-pxtorem,在写另外 ...

  4. 畅捷通T+与道一云对接集成报销信息列表连通凭证创建

    畅捷通T+与道一云对接集成获取报销信息列表连通凭证创建 数据源系统:道一云 在道一云坚实的技术基础上,道一云推出全新升级的2.0产品矩阵,分别是低码平台.智能门户.场景应用.基于云原生底座,为企业提供 ...

  5. iNeuOS工业互联网操作系统,高效采集数据配置与应用

    1. 概述 2. 通讯原理 3. 参数配置  1.   概述 某生产企业世界500强的集团能源管控平台项目建设,通过专线网络实现异地厂区数据集成,每个终端能源仪表都有IP地址,总共有1000多台能源表 ...

  6. 谷歌浏览器和火狐浏览器如何查看HTTP协议?

    按F12

  7. AR9271无线网卡Win10配置热点

    AR9271无线网卡Win10配置热点 需要的无线网卡如下图 1 准备工作 网卡参数 Atheros AR9271是一款高性能的无网络模块,采用802.11b/g/n标准,支持2.4GH频段.它被广泛 ...

  8. 一个适用于定制个性化界面的WPF UI组件库

    前言 今天给大家推荐一个能让你用最少的代码来实现期望的UI效果,适用于定制个性化界面的WPF UI组件库:Panuon.WPF.UI. 组件库官方介绍 Panuon.WPF.UI 是一个适用于定制个性 ...

  9. Microsoft Edge 分屏 推荐

    前言: 很早之前就在 Edge Dev 频道的更新公告中看到过 Edge 的新分屏功能,当时没怎么注意,昨天看文档的时候发现 Edge 的侧边栏可以拖动当作一个"虚假的"分屏页面来 ...

  10. Tensorflow2.0实战之Auto-Encoder

    autoencoder可以用于数据压缩.降维,预训练神经网络,生成数据等等 Auto-Encoder架构 需要完成的工作 需要完成Encoder和Decoder的训练 例如,Mnist的一张图片大小为 ...