パーティクルが円を描きながら上昇する(スパイラル運動)

ThreeJS

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