パーティクルの数を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>