パーティクルが円を描きながら上昇する(スパイラル運動)
HTML
<canvas id='canvas'></camvas>
CDN
<script type='importmap'>
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.175.0/build/three.module.js"
}
}
</script>
<script type='module'>
import * as THREE from 'three';
width = window.innerWidth;
height = window.innerHeight;
Scene
const scene = new THREE.Scene();
PerspectiveCamera
const camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000)'
camera.position.z = 50;
WebGLRenderer
const renderer = new THREE.WebGLRenderer({
canvas:document.querySelector("#canvas");
});
renderer.setSize(width, height);
particle
各パーティクルのX,Y,Z座標(つまり30000個の数値)を格納できる配列を用意します
const positions = new Float32Array(10000 * 3);
各パーティクルに個別の回転スピードを設定するための配列を用意します
const speeds = new Float32Array(10000);
初期位置を設定します
for (let i = 0; i < 10000; i++) {
5~25の範囲で中心からの距離(円の半径)をランダムに決めます
const radius = Math.random() * 20 + 5;
0~2πまでのランダムな角度(ラジアン)を設定します
const angle = Math.random() * Math.PI * 2;
-20~20の範囲でY座標(高さ)をランダムに設定します(上下にバラけた配置を作るため)
const height = (Math.random() - 0.5) * 40;
円周上に点を配置します(スパイラル)
positions[i * 3] = radius * Math.cos(angle); // X座標
positions[i * 3 + 1] = height; // Y座標
positions[i * 3 + 2] = radius * Math.sin(angle); // Z座
パーティクルごとに個別の回転スピードを0.005~0.015の範囲で設定します
speeds[i] = Math.random() * 0.01 + 0.005;
}
geometry
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
material
const material = new THREE.PointsMaterial({
color: 0x00ffff,
size: 0.1,
});
Meshにしてシーンに追加
const particles = new THREE.Points(geometry, material);
scene.add(particles);
Animation
function animate() {
requestAnimationFrame(animate);
パーティクルの位置情報(x,y,z)をまとめた配列を取得します
const pos = geometry.attributes.position.array;
各パーティクルに対して処理をします
for (let i = 0; i < 10000; i++) {
各パーティクルのXとZ
const x = pos[i * 3];
const z = pos[i * 3 + 2];
パーティクルがどの角度にいるか(angle)
const angle = Math.atan2(z, x);
パーティクルが中心からどれだけ離れているか(radius)
const radius = Math.sqrt(x * x + z * z);
設定したspeeds[i]分だけ回転させます
const newAngle = angle + speeds[i];
新しい角度に基づいてX、Z、Yを更新します
pos[i * 3] = radius * Math.cos(newAngle);
pos[i * 3 + 2] = radius * Math.sin(newAngle);
pos[i * 3 + 1] += 0.02; // 上昇させる
// 一定範囲を超えたらリセット
if (pos[i * 3 + 1] > 30) {
pos[i * 3 + 1] = -30;
}
}
変更をGPUに伝えます(これがないと画面に反映されません)
geometry.attributes.position.needsUpdate = true;
画面に描画します
renderer.render(scene, camera);
}
animate();
BACK