
Particles Go Wild with Textures
Just a few more posts on particles, I promise.
This article is part of my ongoing series of medium difficulty ThreeJS tutorials. I’ve long wanted something in between the intro “How to draw a cube” and “Let’s fill the screen with shader madness” levels. So here it is.
So far we have used only one texture image for every effect. This one:
This one works fine for a lot of effects, but there’s so much more we could do using different textures. I downloaded this amazing set of images from master game art creator Kenney. Just look at all of the cool images in there!
Now we can let our imagination run wild. Here are five different examples using one of the images from Kenney, along with the settings I used for each.
Particles of Magic
const options = {
position: new THREE.Vector3(0,2,0),
positionRandomness: 0.0,
velocity: new THREE.Vector3(0.0, -0.5, 0.0),
velocityRandomness: 1.0,
acceleration: new THREE.Vector3(0.0,0.0,0.0),
color: new THREE.Color(1.0,0.0,1.0),
endColor: new THREE.Color(0.0,1.0,1.0),
colorRandomness: 0.0,
lifetime: 2.0,
fadeIn:0.001,
fadeOut:0.001,
size: 60,
sizeRandomness: 0.0,
}
parts = new GPUParticleSystem({
maxParticles: 10000,
particleSpriteTex: texture,
blending: THREE.AdditiveBlending,
onTick:(system,time) => {
for (let i = 0; i < 10; i++) {
options.velocity.set(rand(-1,1),rand(-1,1), 0)
system.spawnParticle(options);
}
}})
A Rainbow of Stars
const options = {
position: new THREE.Vector3(0,2,0),
positionRandomness: 0.0,
velocity: new THREE.Vector3(0.0, -0.5, 0.0),
velocityRandomness: 1.0,
acceleration: new THREE.Vector3(0.0,-1.0,0.0),
color: new THREE.Color(1.0,0.0,1.0),
endColor: new THREE.Color(0.0,1.0,1.0),
lifetime: 20.0,
fadeIn:0.001,
fadeOut:0.001,
size: 100,
sizeRandomness: 0.0,
}
parts = new GPUParticleSystem({
maxParticles: 10000,
particleSpriteTex: texture,
blending: THREE.AdditiveBlending,
onTick:(system,time) => {
for (let i = 0; i < 1; i++) {
options.color.setHSL(rand(0,1),1.0,0.5)
options.endColor = options.color
options.position.set(rand(-2.5,-2),2,0)
options.velocity.set(rand(1.2,1.4),1, 0)
system.spawnParticle(options);
}
}})
Periodic Fireballs
This one is slightly different in that it uses a conditional to decide when to fire. The code (floor(time)%6 === 0)
means every six seconds it will fire continuously for one second.
const options = {
position: new THREE.Vector3(0,2,0),
positionRandomness: 0.0,
velocity: new THREE.Vector3(0.0, 0, 0.0),
velocityRandomness: 1.0,
acceleration: new THREE.Vector3(0.0,0.0,0.0),
color: new THREE.Color(1.0,0.0,1.0),
endColor: new THREE.Color(0.0,1.0,1.0),
lifetime: 2.0,
fadeIn:0.001,
fadeOut:3.0,
size: 60,
sizeRandomness: 0.0,
}
parts = new GPUParticleSystem({
maxParticles: 10000,
particleSpriteTex: texture,
blending: THREE.AdditiveBlending,
onTick:(system,time) => {
if(Math.floor(time) %6 == 0) for (let i = 0; i < 6; i++) {
options.color.setHSL(0.1, 1.0, 0.5)
options.endColor.setHSL(0.0, 0.0, 0.8)
options.position.set(0,0,0)
const s = 0.05
options.velocity.set(rand(-s,s), rand(-s,s), rand(-s,s))
system.spawnParticle(options);
}
}})
Transporter Effect
const options = {
position: new THREE.Vector3(0,2,0),
positionRandomness: 0.0,
velocity: new THREE.Vector3(0.0, 0, 0.0),
velocityRandomness: 1.0,
acceleration: new THREE.Vector3(0.0,0.0,0.0),
color: new THREE.Color(1.0,0.0,1.0),
endColor: new THREE.Color(0.0,1.0,1.0),
lifetime: 2.0,
fadeIn:0.001,
fadeOut:3.0,
size: 200,
sizeRandomness: 0.0,
}
parts = new GPUParticleSystem({
maxParticles: 10000,
particleSpriteTex: texture,
blending: THREE.AdditiveBlending,
onTick: (system, time) => {
if(Math.floor(time/1) % 2 === 0) {
options.color.setHSL(0.1, 1.0, 0.5)
options.endColor.setHSL(0.0, 0.0, 0.8)
options.position.set(0, 0, 0)
const s = 0.05
options.velocity.set(rand(-s, s), rand(-s, s), rand(-s, s))
system.spawnParticle(options);
}
}
})
Sparkler Ring
Notice the code below calculates the starting position from a random angle using sin and cos. This is what creates the circle shape. A square or other shape would be just as easy.
const options = {
position: new THREE.Vector3(0,2,0),
positionRandomness: 0.0,
velocity: new THREE.Vector3(0.0, 0, 0.0),
velocityRandomness: 1.0,
acceleration: new THREE.Vector3(0.0,0.0,0.0),
color: new THREE.Color(1.0,0.0,1.0),
endColor: new THREE.Color(0.0,1.0,1.0),
lifetime: 2.0,
fadeIn:0.001,
fadeOut:3.0,
size: 30,
sizeRandomness: 0.0,
}
parts = new GPUParticleSystem({
maxParticles: 10000,
particleSpriteTex: texture,
blending: THREE.AdditiveBlending,
onTick: (system, time) => {
for(let i=0; i<10; i++) {
options.color.setHSL(0.1, 1.0, 0.5)
options.endColor.setHSL(0.0, 0.0, 0.8)
const theta = rand(0,Math.PI*2)
const size = 0.4
options.position.set(Math.sin(theta)*size,Math.cos(theta)*size, 0)
const s = 0.02
options.velocity.set(rand(-s, s), rand(-s, s), rand(-s, s))
system.spawnParticle(options);
}
}
})
Until Next Time
Remember, if you are working on a cool WebVR experience that you’d like to have showcased right in Firefox Reality, let me know.