我来详细介绍 Three.js 渲染性能分析工具,帮助你诊断和优化性能问题。
最常用的性能监控工具:
import Stats from 'three/addons/libs/stats.module.js';
const stats = new Stats();
stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
document.body.appendChild(stats.dom);
function animate() {
stats.begin();
// 渲染场景
renderer.render(scene, camera);
stats.end();
requestAnimationFrame(animate);
}
内置的性能统计信息:
console.log(renderer.info);
// 输出:
// {
// memory: { geometries: 10, textures: 5 },
// render: { calls: 100, triangles: 50000, points: 0, lines: 0 },
// programs: 8
// }
监控内存使用情况,检测 Three.js 对象泄漏。
import { computeBoundsTree, disposeBoundsTree } from 'three-mesh-bvh';
// 启用BVH性能统计
const geometry = new THREE.BoxGeometry();
geometry.boundsTree = computeBoundsTree(geometry);
console.log(geometry.boundsTree.stats);
浏览器扩展,可视化检查 Three.js 场景:
// 启用WebGL调试
const renderer = new THREE.WebGLRenderer({
powerPreference: "high-performance",
antialias: false,
stencil: false,
depth: false
});
// 检查扩展
console.log(renderer.capabilities);
// 监控GPU帧时间
const query = renderer.getContext().createQuery();
renderer.getContext().beginQuery(renderer.getContext().TIME_ELAPSED_EXT, query);
// 渲染代码...
renderer.getContext().endQuery(renderer.getContext().TIME_ELAPSED_EXT);
class PerformanceMonitor {
constructor() {
this.frameTimes = [];
this.maxSamples = 60;
}
beginFrame() {
this.startTime = performance.now();
}
endFrame() {
const frameTime = performance.now() - this.startTime;
this.frameTimes.push(frameTime);
if (this.frameTimes.length > this.maxSamples) {
this.frameTimes.shift();
}
const avg = this.frameTimes.reduce((a, b) => a + b) / this.frameTimes.length;
console.log(`平均帧时间: ${avg.toFixed(2)}ms (${(1000/avg).toFixed(1)} FPS)`);
}
}
class ObjectCounter {
constructor() {
this.counts = {
meshes: 0,
vertices: 0,
triangles: 0,
textures: 0,
materials: 0
};
}
analyzeScene(scene) {
scene.traverse((object) => {
if (object.isMesh) {
this.counts.meshes++;
const geometry = object.geometry;
if (geometry) {
this.counts.vertices += geometry.attributes.position.count;
this.counts.triangles += geometry.index ? geometry.index.count / 3 : geometry.attributes.position.count / 3;
}
}
if (object.material) {
this.counts.materials++;
}
});
return this.counts;
}
}
class PerformanceChecker {
static checkIssues(renderer, scene) {
const issues = [];
// 检查draw calls
if (renderer.info.render.calls > 1000) {
issues.push(`Draw calls过高: ${renderer.info.render.calls}`);
}
// 检查三角形数量
if (renderer.info.render.triangles > 1000000) {
issues.push(`三角形数量过多: ${renderer.info.render.triangles}`);
}
// 检查纹理内存
if (renderer.info.memory.textures > 50) {
issues.push(`纹理数量过多: ${renderer.info.memory.textures}`);
}
return issues;
}
}
class PerformanceDashboard {
constructor(renderer, scene) {
this.renderer = renderer;
this.scene = scene;
this.stats = new Stats();
this.setupUI();
}
setupUI() {
// 创建控制面板
this.panel = document.createElement('div');
this.panel.style.cssText = `
position: fixed; top: 10px; right: 10px;
background: rgba(0,0,0,0.8); color: white;
padding: 10px; font-family: monospace;
z-index: 10000;
`;
document.body.appendChild(this.panel);
// 添加Stats.js
this.stats.dom.style.position = 'relative';
this.stats.dom.style.top = '0';
this.panel.appendChild(this.stats.dom);
}
update() {
const info = this.renderer.info;
const statsHTML = `
<div style="margin-top: 10px;">
<div>Draw Calls: ${info.render.calls}</div>
<div>Triangles: ${info.render.triangles.toLocaleString()}</div>
<div>Textures: ${info.memory.textures}</div>
<div>Geometries: ${info.memory.geometries}</div>
<div>Shaders: ${info.programs ? info.programs.length : 0}</div>
</div>
`;
// 更新显示
const existingStats = this.panel.querySelector('.custom-stats');
if (existingStats) {
existingStats.innerHTML = statsHTML;
} else {
const div = document.createElement('div');
div.className = 'custom-stats';
div.innerHTML = statsHTML;
this.panel.appendChild(div);
}
}
}
// 性能回归测试
function runPerformanceTest(scene, camera, renderer, duration = 5000) {
const startTime = Date.now();
const frameTimes = [];
function frame() {
const start = performance.now();
renderer.render(scene, camera);
const end = performance.now();
frameTimes.push(end - start);
if (Date.now() - startTime < duration) {
requestAnimationFrame(frame);
} else {
analyzeResults(frameTimes);
}
}
frame();
}
这些工具和技巧能帮助你全面分析 Three.js 应用的渲染性能,快速定位瓶颈并进行优化。