<canvas id="canvas"></canvas>
<script id="vertex" type="x-shader/x-vertex">
#version 300 es
in vec3 position;
out float noise;
out vec3 vMVPos;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform vec3 cameraPosition;
uniform float uTime;
uniform vec2 uMouse;
uniform vec2 uResolution;
uniform float uMouseDistortDist;
// Simplex 2D noise
// https://gist.github.com/patriciogonzalezvivo/670c22f3966e662d2f83
vec3 permute(vec3 x) {
return mod(((x * 34.0) + 1.0) * x, 289.0);
}
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
vec2 i = floor(v + dot(v, C.yy));
vec2 x0 = v - i + dot(i, C.xx);
vec2 i1;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
i = mod(i, 289.0);
vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
vec3 m = max(0.5 - vec3(dot(x0, x0), dot(x12.xy, x12.xy), dot(x12.zw, x12.zw)), 0.0);
m = m * m;
m = m * m;
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
m *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h * h);
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
vec2 getScreenPosition(vec3 pos) {
vec4 p = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
vec2 sp = p.xy / p.w * vec2(1.0, -1.0);
return (sp * 0.5 + 0.5) * uResolution;
}
void main() {
noise = snoise(position.xy + position.zx + uTime * 0.1) * 0.5 + 0.5;
float n = mix(0.9, 1.3, noise * 0.5);
vec2 pointScreenPosition = getScreenPosition(position * n);
float pointMouseFact = 1.0 - min(1.0, distance(pointScreenPosition, uMouse) / uMouseDistortDist);
vec4 newPosition = modelViewMatrix * vec4(position * (n + pointMouseFact * -0.25), 1.0);
vMVPos = newPosition.xyz;
newPosition = projectionMatrix * newPosition;
gl_Position = newPosition;
gl_PointSize = 3.0;
}
</script>
<script id="fragment" type="x-shader/x-fragment">
#version 300 es
precision highp float;
uniform float uTime;
uniform vec3 cameraPosition;
in float noise;
in vec3 vMVPos;
out vec4 pc_FragColor;
float circle(vec2 uv, float r) {
vec2 dist = uv - vec2(0.5);
return 1. - step(r, dot(dist, dist) * 4.0);
}
void main() {
vec2 uv = gl_PointCoord;
vec3 fogColor = vec3(0.04, 0.05, 0.05);
vec3 color1 = vec3(0.96, 0.26, 0.21);
vec3 color2 = vec3(1, 0.92, 0.23);
vec3 color = mix(color1, color2, noise);
float circ = circle(uv, 1.0);
if (circ < 0.001) discard;
float fogDist = length(vMVPos) - length(cameraPosition);
float fogFact = smoothstep(-0.5, 1.0, fogDist);
color = mix(color, fogColor, fogFact);
pc_FragColor.rgb = color;
pc_FragColor.a = circ;
}
</script>
body {
margin: 0;
height: 100vh;
background-color: #111;
}
#canvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
import {
Renderer,
Camera,
Transform,
Geometry,
Box,
Sphere,
Orbit,
Program,
Mesh,
Vec2,
Vec3
} from "https://cdn.skypack.dev/ogl@0.0.97";
const vertex = document.getElementById("vertex").textContent.trim();
const fragment = document.getElementById("fragment").textContent.trim();
// from: https://stackoverflow.com/a/60579136
function fibonacciSphere(total, index) {
const offset = 2 / total;
const increment = 2.399963229728653; // Math.PI * (3 - Math.sqrt(5));
const y = index * offset - 1 + offset / 2;
const r = Math.sqrt(1 - Math.pow(y, 2));
const phi = (index % total) * increment;
const x = Math.cos(phi) * r;
const z = Math.sin(phi) * r;
return [x, y, z];
}
const createSpring2D = (from, springCoef = 0.01, frictionCoef = 0.7) => {
from = from.clone();
const velocity = new Vec2();
const tmp = new Vec2();
return (to) => {
tmp.sub(to, from).scale(springCoef);
velocity.add(tmp).scale(frictionCoef);
from.add(velocity);
return from;
};
};
const mouse = new Vec2();
const mouseEased = new Vec2();
const resolution = new Vec2();
const pixelRatio = window.devicePixelRatio;
const canvas = document.querySelector("#canvas");
const renderer = new Renderer({ canvas, dpr: pixelRatio, antialias: true });
const gl = renderer.gl;
gl.clearColor(0.04, 0.05, 0.05, 1.0);
const camera = new Camera(gl, { fov: 35 });
camera.position.set(0, 0, 5);
camera.lookAt([0, 0, 0]);
const scene = new Transform(gl);
const controls = new Orbit(camera, { enableRotate: false });
const totalPoints = 4096;
const position = new Float32Array(totalPoints * 3);
for (let i = 0; i < totalPoints; i++) {
const p = fibonacciSphere(totalPoints, i);
position.set(p, i * 3);
}
const mesh = new Mesh(gl, {
mode: gl.POINTS,
geometry: new Geometry(gl, {
position: { size: 3, data: position }
}),
program: new Program(gl, {
vertex: vertex,
fragment: fragment,
uniforms: {
uTime: { value: 0 },
uResolution: { value: resolution },
uMouse: { value: mouseEased },
uMouseDistortDist: { value: 250 * pixelRatio }
}
})
});
// mesh.geometry.isInstanced = true;
// mesh.geometry.instancedCount = 1024;
mesh.setParent(scene);
function onResize() {
const width = window.innerWidth;
const height = window.innerHeight;
resolution.set(width * pixelRatio, height * pixelRatio);
renderer.setSize(width, height);
const aspect = width / height;
camera.perspective({ aspect });
}
window.addEventListener("resize", onResize);
onResize();
window.addEventListener("pointermove", (event) => {
const x = event.clientX * pixelRatio;
const y = event.clientY * pixelRatio;
mouse.set(x, y);
});
const mouseSpring = createSpring2D(mouse, 0.02, 0.85);
const mouseWorld = new Vec3();
const meshMouseVec = new Vec3();
function screenToWorld(position, targetZ = 0, out = new Vec3()) {
out.copy(position).divide(resolution).scale(2);
out.set(out.x - 1, 1 - out.y, 0);
camera.unproject(out);
out.sub(camera.position).normalize();
out.scale((targetZ - camera.position.z) / out.z);
out.add(camera.position);
return out;
}
requestAnimationFrame(function update(now) {
controls.update();
const newMouse = mouseSpring(mouse);
mouseEased.copy(newMouse);
screenToWorld(newMouse, mesh.position.z, mouseWorld);
meshMouseVec.sub(mesh.position, mouseWorld);
const meshMouseLen = meshMouseVec.len();
const meshMouseFactor = 1 - Math.min(2, meshMouseLen) / 2;
meshMouseVec.scale((1 / meshMouseLen) * meshMouseFactor * 0.02);
mesh.position.add(meshMouseVec);
mesh.program.uniforms.uTime.value = now / 1000;
renderer.render({ scene, camera });
requestAnimationFrame(update);
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.