Public
Edited
Feb 20, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
md`## Implementation`
Insert cell
particleSystemChunk = `
${randomBySpatialChunk}
${myRandChunk}
${easingChunk}
${zoneChunk}
${transformChunk}


vec3 projectOnVector(vec3 v, vec3 target) {
return (dot(target, target) != 0.0)
? (target * dot(v, target) / dot(target, target))
: vec3(0.0);
}

uniform vec3 prevPosRelative;
uniform float prevTimeSec;
uniform float zeroTimeSec;

mat4 particleTransform(in float particleTimeSec, in float t, in float iteration) {
vec3 initialVelocity = randDir3() * 1.0;
initialVelocity -= projectOnVector(initialVelocity, prevPosRelative);
initialVelocity += normalize(prevPosRelative) * randInRange(1.0, 3.0);
vec3 displacementInTime = initialVelocity * particleTimeSec;

return identity
* translate(displacementInTime)
* scale(mix(vec3(0.0), vec3(randInRange(0.3, 0.8)), min(1.0, easeOutQuad(t * 2.5)))) // scale in
* scale(mix(vec3(1.0), vec3(0.0), easeOutQuad(t))); // scale out
}


mat4 emitterTransform(float timeSec) {
if ((timeSec > zeroTimeSec || timeSec <= prevTimeSec)) { return scaleZero; }
return ${
generateDiscretely
? `identity`
: `translate(mix(prevPosRelative, vec3(0.0), (timeSec - zeroTimeSec) / (prevTimeSec - zeroTimeSec)))`
};
}

// Prepares particle iteration variables, seeds the random, returns particle transform
mat4 particleMain(in float timeSec, in float seed, bool debug) {
if (debug) { return emitterTransform(timeSec); }
myRandSeed = seed;
float lifeSec = randInRange(0.8, 1.0);
float initialLifeOffsetSec = randInRange(-lifeSec, 0.0);
float iterationWithPhase = (timeSec + initialLifeOffsetSec) / lifeSec;
float iteration = floor(iterationWithPhase);
float t = fract(iterationWithPhase);
float particleTimeSec = t * lifeSec;
float emissionTimeSec = timeSec - particleTimeSec;
float iterationSeed = fract(iteration * 1e-10);
myRandSeed = fract(myRandSeed + iterationSeed); // reseed rand for every iteration
return emitterTransform(emissionTimeSec) * particleTransform(particleTimeSec, t, iteration);
}
`
Insert cell
particleMaterial = {
const material = createStatelessMaterial(
{
...THREE.ShaderLib.phong,
lights: true,
fog: true,
uniforms: THREE.UniformsUtils.merge([
THREE.ShaderLib.phong.uniforms,
{
prevTimeSec: { value: 0.0 },
zeroTimeSec: { value: 0.0 },
prevPosRelative: { value: new THREE.Vector3() }
}
])
},
particleSystemChunk
);
return material;
}
Insert cell
fastMovingEmitter = {
const geometry = new THREE.DodecahedronGeometry();
const obj = new THREE.Group();
// obj.add(
// new THREE.Mesh(
// geometry,
// new THREE.MeshPhongMaterial()
// )
// );
const _startTimeSec = performance.now() * 0.001;
let prevTimeSec = 0;

const bursts = [];

obj.update = (_timeSec) => {
const timeSec = _timeSec - _startTimeSec;
const prev = obj.position.clone();
if (motionType === "brownian") {
obj.position.add(new THREE.Vector3().randomDirection().multiplyScalar(5));
if (obj.position.length() > 30) {
obj.position.multiplyScalar(0.95);
}
} else {
obj.position
.set(
Math.sin(timeSec * 8),
Math.cos(timeSec * 10),
Math.pow(Math.cos(timeSec * 2), 3)
)
.multiplyScalar(15);
}

const burst = new THREE.InstancedMesh(
geometry,
particleMaterial.clone(),
500
);
burst.material.uniforms.prevTimeSec.value = prevTimeSec;
burst.material.uniforms.zeroTimeSec.value = timeSec;
burst.material.uniforms.prevPosRelative.value.copy(prev).sub(obj.position);
burst.position.copy(obj.position);
// burst.add(new THREE.Mesh(geometry, new THREE.MeshPhongMaterial()));

scene.add(burst);
bursts.push(burst);
(async () => {
await Promises.delay(1000);
burst.removeFromParent();
if (bursts.indexOf(burst) >= 0) {
bursts.splice(bursts.indexOf(burst), 1);
}
})();
for (const b of bursts) {
b.material.update(timeSec);
}

prevTimeSec = timeSec;
};

scene.add(obj);
invalidation.then(() => {
scene.remove(obj);
for (const b of bursts) {
b.removeFromParent();
}
});
return obj;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more