Public
Edited
Mar 18, 2023
Insert cell
Insert cell
Insert cell
renderer.domElement
Insert cell
viewof color = Inputs.color({ value: "#c17dff" })
Insert cell
count = 2 ** 12
Insert cell
fps(timeSec)
Insert cell
Insert cell
particleSystemChunk = `
${myRandChunk}
${easingChunk}
${zoneChunk}
${transformChunk}

mat4 emitterTransform(float timeSec) {
return translate(vec3(-250.0 + 500.0 * fract(timeSec * 0.2), 10.0, 0.0));
}

// Calculates a transform of a single particle at a given time of its lifespan
// given the myRand is procedurally seeded per particle
// @ particleTimeSec - time in [0, lifeSec)
// @ t - phase in [0, 1),
// @ iteration - an integer iteration of particle instance (each iteration seeds another particle)
mat4 particleTransform(in float particleTimeSec, in float t, in float iteration) {


return identity
// * translate(vec3(t * 30.0, 0.0, 0.0))
* scale(mix(vec3(1.0), vec3(randInRange(0.0, 3.0)), abs(sin(t * PI * 5.0))))
// * translate(randInBoxZone(vec3(0.0, -2.0, -2.0), vec3(0.0, 2.0, 2.0))) // initial position in a (flat) box
* translate(vec3(0.0, randInBallZone().yz) * 2.0) // initial position in a ball
* scale(mix(vec3(0.0), vec3(randInRange(0.05, 0.1)), abs(sin(t * PI * 5.0))))
* identity;
}

// 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); }
if (gl_InstanceID < 10) {
return identity
* translate(vec3(-250.0 + 500.0 * fract((timeSec + float(gl_InstanceID) + 0.5) * 0.2), 10.0, 0.0))
* scale(vec3(1.0))
* identity;
}
myRandSeed = seed;
float lifeSec = randInRange(5.0, 5.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
material = {
const material = new THREE.MeshBasicMaterial({
color: color
});
const x = particleSystemChunk;
material.onBeforeCompile = (shader) => {
material.userData.shader = shader;
shader.uniforms.timeSec = { value: 0 };
shader.vertexShader = resolveIncludes(shader.vertexShader)
.replaceAll("instanceMatrix", "instanceMatrix2")
.replace(
"void main() {",
`
uniform float timeSec;
${x}
void main() {
mat4 instanceMatrix2 = particleMain(
timeSec,
(1.0 + float(gl_InstanceID)) / ${toGlslFloatLiteral(count)},
gl_InstanceID == 0);
`
);
};
invalidation.then(() => material.dispose());
return material;
}
Insert cell
Insert cell
instancedMesh1 = {
const geometry = new THREE.SphereGeometry();
const selectedParticleSystemChunk = particleSystemChunk;
const obj = new THREE.InstancedMesh(geometry, material, count);
obj.position.x = -250;
const obj1 = new THREE.Group();
obj1.add(obj);
obj1.rotation.y = Math.PI * 0.66;
const startTimeSec = performance.now() * 0.001;
scene.add(obj1);
invalidation.then(() => {
scene.remove(obj1);
obj.dispose();
});
return obj;
}
Insert cell
instancedMesh2 = {
const geometry = new THREE.SphereGeometry(1, 4, 4);
const selectedParticleSystemChunk = particleSystemChunk;
const obj = new THREE.InstancedMesh(geometry, material, count);
obj.position.x = -250;
const obj1 = new THREE.Group();
obj1.add(obj);
obj1.rotation.y = -Math.PI * 0.66;
const startTimeSec = performance.now() * 0.001;
scene.add(obj1);
invalidation.then(() => {
scene.remove(obj1);
obj.dispose();
});
return obj;
}
Insert cell
instancedMesh3 = {
const geometry = new THREE.SphereGeometry();
const selectedParticleSystemChunk = particleSystemChunk;
const obj = new THREE.InstancedMesh(geometry, material, count);
obj.position.x = 250;
obj.position.z = 1;
const obj1 = new THREE.Group();
obj1.add(obj);
obj1.rotation.y = -Math.PI;
const startTimeSec = performance.now() * 0.001;
scene.add(obj1);
invalidation.then(() => {
scene.remove(obj1);
obj.dispose();
});
return obj;
}
Insert cell
Insert cell
{
if (material.userData.shader) {
material.userData.shader.uniforms.timeSec.value = timeSec;
}
scene.update(timeSec);
renderer.render(scene, scene.camera);
return "update & render";
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
resolveIncludes = {
// based on https://github.com/mrdoob/three.js/blob/b9bc47ab1978022ab0947a9bce1b1209769b8d91/src/renderers/webgl/WebGLProgram.js#L204
function resolveIncludes(string) {
return string.replace(
/^[ \t]*#include +<([\w\d./]+)>/gm,
(match, include) => resolveIncludes(THREE.ShaderChunk[include])
);
}
return resolveIncludes;
}
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