3
4

トカマク型核融合炉のトーラス内を二方向に高速でパーティクルが流れるアニメーション。

Last updated at Posted at 2024-09-05

スクリーンショット 2024-09-06 043606.png

スクリーンショット 2024-09-06 043550.png

トカマク型核融合炉のトーラス内を二方向に高速でパーティクルが流れるアニメーションを行うHTMLとJavaScriptのコードです。これを実現するには、WebGLからGPUを利用して3Dオブジェクトを描画し、パーティクルシステムを作成します。Three.jsという3Dライブラリを使ってトーラスをワイヤーフレームで描画し、その内部でパーティクルが流れるアニメーションを実装してます。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Tokamak Particle Flow</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // シーン、カメラ、レンダラーの初期化
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // ワイヤーフレームトーラスを作成
        const geometry = new THREE.TorusGeometry(5, 1, 16, 100);  // 内径5、チューブの半径1
        const torusMaterial = new THREE.MeshBasicMaterial({
            color: 0xff66cc,  // 紫とピンクが混じった色
            wireframe: true  // ワイヤーフレームモード
        });
        const torus = new THREE.Mesh(geometry, torusMaterial);
        torus.rotation.x = Math.PI / 2;  // トーラスを90度傾ける
        scene.add(torus);  // トーラスをシーンに追加

        // パーティクルの設定
        const particleCount = 1000;  // パーティクルの数
        const particleGeometry1 = new THREE.BufferGeometry();
        const particleGeometry2 = new THREE.BufferGeometry();
        const positions1 = new Float32Array(particleCount * 3);  // パーティクル1の位置データ
        const positions2 = new Float32Array(particleCount * 3);  // パーティクル2の位置データ
        const speeds1 = [];  // パーティクル1の速度
        const speeds2 = [];  // パーティクル2の速度

        // パーティクルをトーラス内部に配置
        for (let i = 0; i < particleCount; i++) {
            // 円筒座標でトーラス内の位置を計算
            const angle = Math.random() * Math.PI * 2;  // 角度は0~2πのランダム値
            const radius = 5 + (Math.random() - 0.5) * 0.8;  // 内半径5、±0.4のばらつき
            const tubeRadius = (Math.random() - 0.5) * 0.8;  // チューブ半径内でランダムな位置

            // パーティクル1の位置設定
            const x1 = (radius + tubeRadius * Math.cos(angle)) * Math.cos(angle);
            const y1 = tubeRadius * Math.sin(angle);
            const z1 = (radius + tubeRadius * Math.cos(angle)) * Math.sin(angle);
            positions1[i * 3] = x1;
            positions1[i * 3 + 1] = y1;
            positions1[i * 3 + 2] = z1;

            // パーティクル2の位置設定
            const x2 = (radius + tubeRadius * Math.cos(angle)) * Math.cos(angle);
            const y2 = tubeRadius * Math.sin(angle);
            const z2 = (radius + tubeRadius * Math.cos(angle)) * Math.sin(angle);
            positions2[i * 3] = x2;
            positions2[i * 3 + 1] = y2;
            positions2[i * 3 + 2] = z2;

            // パーティクルの速度を設定(方向が逆の二つの流れ)
            speeds1.push(Math.random() * 0.02 + 0.01);  // パーティクル1は正方向
            speeds2.push(-(Math.random() * 0.02 + 0.01)); // パーティクル2は逆方向
        }

        // パーティクル位置をジオメトリに設定
        particleGeometry1.setAttribute('position', new THREE.BufferAttribute(positions1, 3));
        particleGeometry2.setAttribute('position', new THREE.BufferAttribute(positions2, 3));

        // パーティクルの描画マテリアルを作成
        const particleMaterial1 = new THREE.PointsMaterial({
            color: 0x00ffff,  // パーティクル1は青色
            size: 0.05  // パーティクルサイズ
        });
        const particleMaterial2 = new THREE.PointsMaterial({
            color: 0xff00ff,  // パーティクル2は紫色
            size: 0.05  // パーティクルサイズ
        });

        // パーティクルシステムを作成し、シーンに追加
        const particles1 = new THREE.Points(particleGeometry1, particleMaterial1);
        const particles2 = new THREE.Points(particleGeometry2, particleMaterial2);
        scene.add(particles1);
        scene.add(particles2);

        // カメラ位置を設定(斜め上からトーラスを見るように)
        camera.position.set(5, 5, 10);  // カメラの位置を調整
        camera.lookAt(0, 0, 0);  // シーンの中心を見る

        // アニメーション処理
        function animate() {
            requestAnimationFrame(animate);

            // パーティクル1とパーティクル2の位置を更新
            const positions1 = particleGeometry1.attributes.position.array;
            const positions2 = particleGeometry2.attributes.position.array;

            for (let i = 0; i < particleCount; i++) {
                // パーティクル1の円運動を更新
                let angle1 = Math.atan2(positions1[i * 3 + 2], positions1[i * 3]);
                angle1 += speeds1[i];  // 正方向に移動

                const radius1 = Math.sqrt(positions1[i * 3] * positions1[i * 3] + positions1[i * 3 + 2] * positions1[i * 3 + 2]);
                positions1[i * 3] = radius1 * Math.cos(angle1);
                positions1[i * 3 + 2] = radius1 * Math.sin(angle1);

                // パーティクル2の円運動を更新
                let angle2 = Math.atan2(positions2[i * 3 + 2], positions2[i * 3]);
                angle2 += speeds2[i];  // 逆方向に移動

                const radius2 = Math.sqrt(positions2[i * 3] * positions2[i * 3] + positions2[i * 3 + 2] * positions2[i * 3 + 2]);
                positions2[i * 3] = radius2 * Math.cos(angle2);
                positions2[i * 3 + 2] = radius2 * Math.sin(angle2);
            }

            // パーティクルの位置データを更新
            particleGeometry1.attributes.position.needsUpdate = true;
            particleGeometry2.attributes.position.needsUpdate = true;

            // シーンをレンダリング
            renderer.render(scene, camera);
        }

        // アニメーション開始
        animate();

        // ウィンドウリサイズ時にレンダラーとカメラを更新
        window.addEventListener('resize', () => {
            renderer.setSize(window.innerWidth, window.innerHeight);
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
        });
    </script>
</body>
</html>

テキストを3Dオブジェクトとして3D空間上に浮かせて表示するコードです。

以下は、英語と日本語のシンプルなPythonコードをサンプルテキストとして3D空間上に浮かせて表示するコードです。

スクリーンショット 2024-09-06 044630.png

スクリーンショット 2024-09-06 044645.png

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Floating Text</title>
    <style>
        body { margin: 0; overflow: hidden; }
        canvas { display: block; }
    </style>
</head>
<body>
    <!-- Three.jsのライブラリを読み込み -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // シーンを設定
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 照明を設定
        const light = new THREE.PointLight(0xffffff, 1, 100);
        light.position.set(50, 50, 50);
        scene.add(light);

        // フォントローダーを使用してフォントを読み込み
        const loader = new THREE.FontLoader();
        loader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', function(font) {
            // 英語のPythonコードをサンプルテキストとして作成
            const textGeometry1 = new THREE.TextGeometry("print('Hello, World!')", {
                font: font,
                size: 1,
                height: 0.2,
                curveSegments: 12,
            });
            const material1 = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
            const textMesh1 = new THREE.Mesh(textGeometry1, material1);
            textMesh1.position.set(-5, 2, -5); // テキストの位置を設定
            scene.add(textMesh1);

            // 日本語のPythonコードをサンプルテキストとして作成
            const textGeometry2 = new THREE.TextGeometry("print('こんにちは、世界!')", {
                font: font,
                size: 1,
                height: 0.2,
                curveSegments: 12,
            });
            const material2 = new THREE.MeshBasicMaterial({ color: 0x0000ff });
            const textMesh2 = new THREE.Mesh(textGeometry2, material2);
            textMesh2.position.set(-5, 0, -5); // テキストの位置を設定
            scene.add(textMesh2);

            // アニメーションを開始
            animate();
        });

        // カメラの位置を設定
        camera.position.z = 10;

        // アニメーションループ
        function animate() {
            requestAnimationFrame(animate);
            scene.rotation.y += 0.01; // シーン全体を回転
            renderer.render(scene, camera); // シーンをレンダリング
        }

        // ウィンドウのリサイズに対応
        window.addEventListener('resize', () => {
            renderer.setSize(window.innerWidth, window.innerHeight);
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
        });

    </script>
</body>
</html>


3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4