|
| 1 | +import * as THREE from 'three'; |
| 2 | + |
| 3 | +import * as Stats from 'three/examples/jsm/libs/stats.module.js'; |
| 4 | + |
| 5 | +let container: HTMLElement; |
| 6 | +let stats: Stats; |
| 7 | + |
| 8 | +let camera: THREE.PerspectiveCamera; |
| 9 | +let scene: THREE.Scene; |
| 10 | +let renderer: THREE.WebGLRenderer; |
| 11 | + |
| 12 | +let points: THREE.Points<THREE.BufferGeometry<THREE.NormalOrGLBufferAttributes>>; |
| 13 | + |
| 14 | +const particles = 300000; |
| 15 | +let drawCount = 10000; |
| 16 | + |
| 17 | +init(); |
| 18 | +animate(); |
| 19 | + |
| 20 | +function init() { |
| 21 | + container = document.getElementById('container')!; |
| 22 | + |
| 23 | + // |
| 24 | + |
| 25 | + renderer = new THREE.WebGLRenderer({ antialias: false }); |
| 26 | + renderer.setPixelRatio(window.devicePixelRatio); |
| 27 | + renderer.setSize(window.innerWidth, window.innerHeight); |
| 28 | + |
| 29 | + container.appendChild(renderer.domElement); |
| 30 | + |
| 31 | + // |
| 32 | + |
| 33 | + camera = new THREE.PerspectiveCamera(27, window.innerWidth / window.innerHeight, 5, 3500); |
| 34 | + camera.position.z = 2750; |
| 35 | + |
| 36 | + scene = new THREE.Scene(); |
| 37 | + scene.background = new THREE.Color(0x050505); |
| 38 | + scene.fog = new THREE.Fog(0x050505, 2000, 3500); |
| 39 | + |
| 40 | + // |
| 41 | + |
| 42 | + const geometry = new THREE.BufferGeometry<THREE.NormalOrGLBufferAttributes>(); |
| 43 | + |
| 44 | + const positions = []; |
| 45 | + const positions2 = []; |
| 46 | + const colors = []; |
| 47 | + |
| 48 | + const color = new THREE.Color(); |
| 49 | + |
| 50 | + const n = 1000; |
| 51 | + const n2 = n / 2; // particles spread in the cube |
| 52 | + |
| 53 | + for (let i = 0; i < particles; i++) { |
| 54 | + // positions |
| 55 | + |
| 56 | + const x = Math.random() * n - n2; |
| 57 | + const y = Math.random() * n - n2; |
| 58 | + const z = Math.random() * n - n2; |
| 59 | + |
| 60 | + positions.push(x, y, z); |
| 61 | + positions2.push(z * 0.3, x * 0.3, y * 0.3); |
| 62 | + |
| 63 | + // colors |
| 64 | + |
| 65 | + const vx = x / n + 0.5; |
| 66 | + const vy = y / n + 0.5; |
| 67 | + const vz = z / n + 0.5; |
| 68 | + |
| 69 | + color.setRGB(vx, vy, vz); |
| 70 | + |
| 71 | + colors.push(color.r, color.g, color.b); |
| 72 | + } |
| 73 | + |
| 74 | + const gl = renderer.getContext(); |
| 75 | + |
| 76 | + const pos = gl.createBuffer()!; |
| 77 | + gl.bindBuffer(gl.ARRAY_BUFFER, pos); |
| 78 | + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW); |
| 79 | + |
| 80 | + const pos2 = gl.createBuffer()!; |
| 81 | + gl.bindBuffer(gl.ARRAY_BUFFER, pos2); |
| 82 | + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions2), gl.STATIC_DRAW); |
| 83 | + |
| 84 | + const rgb = gl.createBuffer()!; |
| 85 | + gl.bindBuffer(gl.ARRAY_BUFFER, rgb); |
| 86 | + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); |
| 87 | + |
| 88 | + const posAttr1 = new THREE.GLBufferAttribute(pos, gl.FLOAT, 3, 4, particles); |
| 89 | + const posAttr2 = new THREE.GLBufferAttribute(pos2, gl.FLOAT, 3, 4, particles); |
| 90 | + geometry.setAttribute('position', posAttr1); |
| 91 | + |
| 92 | + setInterval(() => { |
| 93 | + const attr = geometry.getAttribute('position'); |
| 94 | + |
| 95 | + geometry.setAttribute('position', attr === posAttr1 ? posAttr2 : posAttr1); |
| 96 | + }, 2000); |
| 97 | + |
| 98 | + geometry.setAttribute('color', new THREE.GLBufferAttribute(rgb, gl.FLOAT, 3, 4, particles)); |
| 99 | + |
| 100 | + // |
| 101 | + |
| 102 | + const material = new THREE.PointsMaterial({ size: 15, vertexColors: true }); |
| 103 | + |
| 104 | + points = new THREE.Points(geometry, material); |
| 105 | + |
| 106 | + // Choose one: |
| 107 | + // geometry.boundingSphere = ( new THREE.Sphere() ).set( new THREE.Vector3(), Infinity ); |
| 108 | + points.frustumCulled = false; |
| 109 | + |
| 110 | + scene.add(points); |
| 111 | + |
| 112 | + // |
| 113 | + |
| 114 | + stats = new Stats(); |
| 115 | + container.appendChild(stats.dom); |
| 116 | + |
| 117 | + // |
| 118 | + |
| 119 | + window.addEventListener('resize', onWindowResize); |
| 120 | +} |
| 121 | + |
| 122 | +function onWindowResize() { |
| 123 | + camera.aspect = window.innerWidth / window.innerHeight; |
| 124 | + camera.updateProjectionMatrix(); |
| 125 | + |
| 126 | + renderer.setSize(window.innerWidth, window.innerHeight); |
| 127 | +} |
| 128 | + |
| 129 | +// |
| 130 | + |
| 131 | +function animate() { |
| 132 | + requestAnimationFrame(animate); |
| 133 | + |
| 134 | + render(); |
| 135 | + stats.update(); |
| 136 | +} |
| 137 | + |
| 138 | +function render() { |
| 139 | + drawCount = (Math.max(5000, drawCount) + Math.floor(500 * Math.random())) % particles; |
| 140 | + points.geometry.setDrawRange(0, drawCount); |
| 141 | + |
| 142 | + const time = Date.now() * 0.001; |
| 143 | + |
| 144 | + points.rotation.x = time * 0.1; |
| 145 | + points.rotation.y = time * 0.2; |
| 146 | + |
| 147 | + renderer.render(scene, camera); |
| 148 | +} |
0 commit comments