/script type="x-shader/x-vertex" id="vertexshader">
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
/script type="x-shader/x-fragment" id="fragmentshader">
uniform vec3 color;
uniform sampler2D pointTexture;
uniform float alphaTest;
varying vec3 vColor;
void main() {
gl_FragColor = vec4(color * vColor, 1.0);
gl_FragColor = gl_FragColor * texture2D(pointTexture, gl_PointCoord);
if (gl_FragColor.a < alphaTest) discard;
}
/script type="module">
import * as THREE from 'https://unpkg.com/three@0.127.0/build/three.module.js';
let container = document.getElementById('threejs-container');
let scene = new THREE.Scene();
scene.background = new THREE.Color('#fec75c');
let camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
camera.position.z = 250;
let renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
let raycaster = new THREE.Raycaster();
let pointer = new THREE.Vector2();
const PARTICLE_SIZE = 20;
let particles;
initParticles();
animate();
function initParticles() {
const geometry = new THREE.IcosahedronGeometry(140, 10); // Second number updates number of points, 6 is less, 8 is more
const vertices = geometry.getAttribute('position').array;
const colors = new Float32Array(vertices.length);
const sizes = new Float32Array(vertices.length / 3);
for (let i = 0, l = vertices.length / 3; i < l; i++) {
const hue = 180 / 360;
const saturation = 0.7;
const lightness = 0.2 + 0.1 * (i / l); // Variable lightness
const colorIndex = i * 3;
const color = new THREE.Color().setHSL(hue, saturation, lightness);
colors[colorIndex] = color.r;
colors[colorIndex + 1] = color.g;
colors[colorIndex + 2] = color.b;
sizes[i] = PARTICLE_SIZE * 0.35;
}
const particleGeometry = new THREE.BufferGeometry();
particleGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
particleGeometry.setAttribute('customColor', new THREE.BufferAttribute(colors, 3));
particleGeometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
const material = new THREE.ShaderMaterial({
uniforms: {
color: { value: new THREE.Color(0xffffff) },
pointTexture: { value: new THREE.TextureLoader().load('https://assets-global.website-files.com/65bdc8d1c1d14c7ba1112827/65bef2d8afe1cc2af3470926_disc.png') },
alphaTest: { value: 0.9 }
},
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
});
particles = new THREE.Points(particleGeometry, material);
scene.add(particles);
window.addEventListener('resize', onWindowResize);
document.addEventListener('pointermove', onPointerMove);
}
function onPointerMove(event) {
pointer.x = ((event.clientX - renderer.domElement.offsetLeft) / renderer.domElement.clientWidth) * 2 - 1;
pointer.y = -((event.clientY - renderer.domElement.offsetTop) / renderer.domElement.clientHeight) * 2 + 1;
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
render();
}
function render() {
particles.rotation.x += 0.0005;
particles.rotation.y += 0.001;
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObject(particles);
const sizes = particles.geometry.attributes.size.array;
for (let i = 0; i < intersects.length; i++) {
const particleIndex = intersects[i].index;
sizes[particleIndex] = PARTICLE_SIZE * .7;
}
if (intersects.length > 0) {
particles.geometry.attributes.size.needsUpdate = true;
}
renderer.render(scene, camera);
}