3
4

小さなGPUでのエンジニアリング。自動的にFPSを計測し内蔵GPUのレンダリングパフォーマンスを探るゲーム。

Last updated at Posted at 2024-09-03

スクリーンショット 2024-09-04 053217.png

スクリーンショット 2024-09-04 053945.png

スクリーンショット 2024-09-04 054110.png

image.png

パーティクルの数を5段階に変化させ、それぞれのパーティクル数に対して自動的にFPSを計測し、結果をコンソールにプリントします。(F12でコンソール開きます。)

Three.js でパーティクルシミュレーションを実行し、GPUレンダリングのパフォーマンスを測定してます。

このコードでは、パーティクルの数を段階的に増やし、その度に一定期間のFPSを計測し、計測が完了したら次の段階に進むようにしています。

実行結果。

Particle Count: 100000, Average FPS: 55.11
index.html:82 Particle Count: 300000, Average FPS: 47.71
index.html:82 Particle Count: 500000, Average FPS: 33.90
index.html:82 Particle Count: 700000, Average FPS: 27.94
index.html:82 Particle Count: 900000, Average FPS: 21.49
index.html:87 FPS Measurement Complete

Three.js でパーティクルシミュレーションを実行し、GPUレンダリングのパフォーマンスを測定するコード。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Particle Simulation with 3JS</title>
    <style>
        body { margin: 0; overflow: hidden; background-color: black; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        let scene, camera, renderer, particleSystem, clock, fpsDisplay, lastTime, fps;
        let particles, velocities;
        let frameCount = 0, fpsSum = 0, stage = 0;
        const maxStages = 5; // 測定するステージの数
        const particleSizes = [100000, 300000, 500000, 700000, 900000]; // 各ステージでのパーティクル数

        // Three.js の初期化
        function initThreeJS(particleCount) {
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 10000);
            camera.position.z = 1000; // カメラの位置を設定

            // パーティクルの位置と速度を設定
            particles = new Float32Array(particleCount * 3);
            velocities = new Float32Array(particleCount * 3);

            for (let i = 0; i < particleCount; i++) {
                particles[i * 3] = (Math.random() * 2 - 1) * 500;
                particles[i * 3 + 1] = (Math.random() * 2 - 1) * 500;
                particles[i * 3 + 2] = (Math.random() * 2 - 1) * 500;

                velocities[i * 3] = (Math.random() - 0.5) * 2;
                velocities[i * 3 + 1] = (Math.random() - 0.5) * 2;
                velocities[i * 3 + 2] = (Math.random() - 0.5) * 2;
            }

            // パーティクルジオメトリとマテリアルを作成
            const geometry = new THREE.BufferGeometry();
            geometry.setAttribute('position', new THREE.BufferAttribute(particles, 3));
            geometry.setAttribute('velocity', new THREE.BufferAttribute(velocities, 3));

            const material = new THREE.PointsMaterial({ color: 0xffffff, size: 2 });
            particleSystem = new THREE.Points(geometry, material);

            scene.add(particleSystem); // シーンにパーティクルシステムを追加

            clock = new THREE.Clock(); // クロックを初期化
        }

        // アニメーション関数
        function animate() {
            const delta = clock.getDelta(); // 時間の差を計算
            fps = 1 / delta; // FPS を計算
            fpsSum += fps;
            frameCount++;

            const positions = particleSystem.geometry.attributes.position.array;
            const velocities = particleSystem.geometry.attributes.velocity.array;

            // パーティクルの位置を更新
            for (let i = 0; i < positions.length / 3; i++) {
                positions[i * 3] += velocities[i * 3];
                positions[i * 3 + 1] += velocities[i * 3 + 1];
                positions[i * 3 + 2] += velocities[i * 3 + 2];

                // 画面外に出た場合の反転処理
                if (positions[i * 3] < -500 || positions[i * 3] > 500) velocities[i * 3] *= -1;
                if (positions[i * 3 + 1] < -500 || positions[i * 3 + 1] > 500) velocities[i * 3 + 1] *= -1;
                if (positions[i * 3 + 2] < -500 || positions[i * 3 + 2] > 500) velocities[i * 3 + 2] *= -1;
            }

            particleSystem.geometry.attributes.position.needsUpdate = true; // 位置の更新をマーク

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

            // 100フレームごとにFPSを計測し、次のステージへ
            if (frameCount >= 100) {
                const avgFps = fpsSum / frameCount;
                console.log(`Particle Count: ${particleSizes[stage]}, Average FPS: ${avgFps.toFixed(2)}`);
                stage++;
                if (stage < maxStages) {
                    startSimulation(particleSizes[stage]);
                } else {
                    console.log('FPS Measurement Complete');
                }
            } else {
                requestAnimationFrame(animate); // 次のフレームをリクエスト
            }
        }

        // シミュレーションを開始する関数
        function startSimulation(particleCount) {
            if (renderer) {
                renderer.dispose(); // 前のレンダラーを解放
                document.body.removeChild(renderer.domElement); // 前のレンダラーの要素を削除
            }

            initThreeJS(particleCount); // 新しいシミュレーションの初期化

            renderer = new THREE.WebGLRenderer(); // GPU レンダリング
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement); // レンダラーの要素を追加

            frameCount = 0;
            fpsSum = 0;
            clock.start(); // クロックをスタート
            requestAnimationFrame(animate); // アニメーションの開始
        }

        // ウィンドウサイズが変更されたときの処理
        window.addEventListener('resize', () => {
            if (renderer) {
                renderer.setSize(window.innerWidth, window.innerHeight);
                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();
            }
        });

        // 最初のシミュレーションを開始
        startSimulation(particleSizes[stage]);
    </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