Published
Edited
Jun 24, 2019
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
fragmentShader = `
precision highp float;
varying vec2 vUv;
varying vec3 vPos;
varying float vParticleIndex;
varying vec2 vLifetime;

uniform float time;

uniform vec3 colorBase;
uniform vec3 colorStart;
uniform vec3 colorPeak;
uniform vec3 colorEnd;

#define PI 3.1415926536
#define PI2 6.28318530718


float drawBase (in vec2 uv, in float min, in float max) {
float dist = sqrt(dot(uv, uv));
if (dist >= max || dist <= min) {
return 0.0;
}
float sm = smoothstep(max, max - 0.01, dist);
float sm2 = smoothstep(min, min - 0.01, dist);
float alpha = sm * sm2;
return (1.0-alpha);
}

void main() {

// figure out opacity early
float alpha = drawBase(vUv - vec2(0.5), 0.0, 0.5);
float progress = vLifetime.x / vLifetime.y;
alpha *= (1.0 - progress);
// lower the alpha since many particles will overlap
alpha /= 7.0;
// discard if not needed
if (alpha == 0.0) {
discard;
}

vec4 color = vec4(colorBase, alpha);

float alteration = sin(vLifetime.x + vLifetime.y + vParticleIndex) * 0.25;
float range1 = progress * 3.0 + alteration;
color.rgb = mix(color.rgb, colorStart.rgb, range1);
float range2 = progress * 3.0 - vLifetime.y + alteration;
color.rgb = mix(color.rgb, colorPeak.rgb, range2);
float range3 = progress * 3.0 - vLifetime.y * 2.0 + alteration;
color.rgb = mix(color.rgb, colorEnd.rgb, range3);

gl_FragColor = color;
}
`
Insert cell
vertexShader = `
precision highp float;

#define PI 3.1415926536
#define PI2 6.28318530718

attribute vec3 position;
attribute vec3 offset;
attribute float scale;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

uniform float time;

attribute vec2 uv;
varying vec2 vUv;
attribute float particleIndex;
varying float vParticleIndex;

attribute vec2 lifetime;
varying vec2 vLifetime;

varying vec3 vPos;

void main(){

float progress = lifetime.x / lifetime.y;

//vec3 pos = position * vec3(scale, scale, 1.0) * vec3(0.5+progress/2.0, 0.5+progress/2.0, 1.0);
vec3 pos = position * vec3(scale * progress, scale * progress, 1.0);
pos = pos + offset;
pos.y += -30.0 + progress * 50.0;

// make the particle move slightly down before going up
pos.y += sin(-2.5 + progress * PI) * 10.0;

pos.x += sin(time * 0.4 + particleIndex * 25.0) * 30.0 * progress;
vPos = pos;
vLifetime = lifetime;
vParticleIndex = particleIndex;
vUv = vec2(uv.x, 1.0-uv.y);

gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0 );
}
`
Insert cell
function initialize () {
const scene = new THREE.Scene()
// radius * window.devicePixelRatio
const aspectRatio = 100 / height
const camera = new THREE.OrthographicCamera(
width * aspectRatio / - 2,
width * aspectRatio / 2,
height * aspectRatio / 2,
height * aspectRatio / - 2,
1, 1000 );
// const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000)
const renderer = new THREE.WebGLRenderer()
// const controls = new THREE.OrbitControls(camera, renderer.domElement)
const clock = new THREE.Clock(true)

renderer.setSize(width, height)
renderer.setPixelRatio(devicePixelRatio)
camera.position.set(0, 0, 1)
return {scene, camera, renderer, clock}
}
Insert cell
function createParticles() {
// create an instanced buffer from a plane buffer geometry
const geometry = new THREE.InstancedBufferGeometry()
geometry.copy(new THREE.PlaneBufferGeometry(1, 1, 1, 1))
// Add/generate attributes buffers for the geometry
const offsets = []
const scales = []
const indexes = []
const lifetimes = []
for (let i = 0; i < numberOfParticles; i += 1) {
offsets.push(
// x
- radius + (radius * 2) * i / numberOfParticles,
// y
0,
// z
0
)
scales.push(0)
indexes.push(i + 1)
lifetimes.push(
// current lifetime
0,
// max lifetime
0
)
}
geometry.addAttribute('offset', new THREE.InstancedBufferAttribute(new Float32Array(offsets), 3))
geometry.addAttribute('scale', new THREE.InstancedBufferAttribute(new Float32Array(scales), 1))
geometry.addAttribute('particleIndex', new THREE.InstancedBufferAttribute(new Float32Array(indexes), 1))
geometry.addAttribute('lifetime', new THREE.InstancedBufferAttribute(new Float32Array(lifetimes), 2))
// Material
const material = new THREE.RawShaderMaterial( {
uniforms: {
time: { value: 1.0 },
colorBase: new THREE.Uniform(new THREE.Color(0x2f3333)),
colorStart: new THREE.Uniform(new THREE.Color(0xcac360)),
colorPeak: new THREE.Uniform(new THREE.Color(0xb37209)),
colorEnd: new THREE.Uniform(new THREE.Color(0x9a6c2e)),
},
vertexShader,
fragmentShader,
side: THREE.FrontSide,
transparent: true,
blending: THREE.AdditiveBlending
})

const mesh = new THREE.Mesh(geometry, material)
return {geometry, material, mesh}
}
Insert cell
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more