Skip to content

Three.js - 调试与统计

在构建 3D 应用程序时,能够实时调整值和监控性能至关重要。这能大大加快开发速度并有助于识别性能瓶颈。在本章中,我们将探讨两个基本工具:一个用于实时代码操作的图形用户界面 (GUI) 和一个用于性能监控的统计面板。

通过不断更改代码和刷新浏览器来试验对象的position、color或scale等变量是一个缓慢而繁琐的过程。调试 UI 面板解决了这个问题。经典的 dat.GUI 已被更现代、更轻量的 lil-gui 所取代,我们将使用它。它与 Three.js 无缝集成,允许您创建简单的 UI 来实时更改代码中的变量。

在现代 JavaScript 项目中,我们使用像 npm 或 yarn 这样的包管理器来管理依赖项。这是推荐的方法。

# 使用 npm
npm install three lil-gui
# 使用 yarn
yarn add three lil-gui

安装后,您可以将其导入到您的 JavaScript 模块中。

import * as THREE from 'three';
import GUI from 'lil-gui';

首先,初始化 GUI。这将创建一个面板,通常位于屏幕的右上角。

const gui = new GUI();

接下来,您可以为您想要操作的属性添加一个控制器。.add() 方法接受您要控制的对象和属性名称(作为字符串)。

// 假设 'cube' 是一个 THREE.Mesh 对象
gui.add(cube.position, 'y'); // 为立方体的 y 轴位置添加控制器

您可以通过链式调用方法来增强控制器,以设置最小值/最大值、步长增量和显示名称。

gui.add(cube.position, 'x')
.min(-3)
.max(3)
.step(0.01)
.name('Cube X Position');

对于非数字属性,lil-gui 提供了特定的控制器。例如,对于颜色使用 .addColor(),对于布尔值(这将创建一个复选框)使用简单的 .add()。

// 对于布尔值(例如,线框)
gui.add(cube.material, 'wireframe');
// 对于颜色,最好控制一个单独的对象并使用 onChange 事件
const colorParams = {
color: 0xff0000
}
gui.addColor(colorParams, 'color').onChange(() => {
cube.material.color.set(colorParams.color);
});

当您有许多可控属性时,您的 UI 面板可能会变得拥挤。您可以使用文件夹来组织和分组相关的控件。

const cubeFolder = gui.addFolder('Cube Properties');
const positionFolder = cubeFolder.addFolder('Position');
positionFolder.add(cube.position, 'x', -5, 5);
positionFolder.add(cube.position, 'y', -5, 5);
positionFolder.add(cube.position, 'z', -5, 5);
const materialFolder = cubeFolder.addFolder('Material');
materialFolder.add(cube.material, 'wireframe');
// ... 在此处添加颜色控制器

另一个重要的调试工具是 Stats.js,它提供了一个简单的面板来监控应用程序的性能,显示每秒帧数 (FPS)、内存使用情况和帧时间。高且稳定的 FPS(通常为 60)表示流畅的体验。

首先,安装该包:

npm install stats.js

然后,导入它,创建一个实例,将其添加到页面,并在动画循环的每一帧中更新它。

import Stats from 'stats.js';
// 1. 初始化
const stats = new Stats();
stats.showPanel(0); // 0: FPS, 1: 帧时间 (毫秒), 2: 内存 (MB), 3+: 自定义
document.body.appendChild(stats.dom);
// 2. 在动画循环中更新
function animate() {
stats.begin(); // 开始监控
// ... 你的动画代码 ...
renderer.render(scene, camera);
stats.end(); // 结束监控
requestAnimationFrame(animate);
}
animate();

这是一个在现代项目设置中集成 Three.js、lil-gui 和 stats.js 的完整示例。此代码通常位于 main.js 文件中。

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import GUI from 'lil-gui';
import Stats from 'stats.js';
/**
* 基础设置
*/
// 调试
const gui = new GUI();
const stats = new Stats();
stats.showPanel(0);
document.body.appendChild(stats.dom);
// 画布
const canvas = document.querySelector('canvas.webgl');
// 场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x262626);
/**
* 对象
*/
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
/**
* 光源
*/
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 0.8);
pointLight.position.set(2, 3, 4);
scene.add(pointLight);
/**
* 尺寸
*/
const sizes = {
width: window.innerWidth,
height: window.innerHeight
};
window.addEventListener('resize', () => {
// 更新尺寸
sizes.width = window.innerWidth;
sizes.height = window.innerHeight;
// 更新相机
camera.aspect = sizes.width / sizes.height;
camera.updateProjectionMatrix();
// 更新渲染器
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
/**
* 相机
*/
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 100);
camera.position.z = 3;
scene.add(camera);
/**
* 控制器
*/
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
/**
* 渲染器
*/
const renderer = new THREE.WebGLRenderer({
canvas: canvas
});
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
/**
* 调试 UI
*/
const cubeFolder = gui.addFolder('Cube');
cubeFolder.add(cube.position, 'y').min(-3).max(3).step(0.01).name('elevation');
cubeFolder.add(cube, 'visible');
cubeFolder.add(material, 'wireframe');
const colorParams = { color: 0x00ff00 };
cubeFolder.addColor(colorParams, 'color').onChange(() => {
material.color.set(colorParams.color);
});
/**
* 动画
*/
const clock = new THREE.Clock();
const tick = () => {
stats.begin();
const elapsedTime = clock.getElapsedTime();
// 更新控制器
controls.update();
// 渲染
renderer.render(scene, camera);
stats.end();
// 在下一帧再次调用 tick
window.requestAnimationFrame(tick);
};
tick();
  • 什么都不可见: 检查您的浏览器开发者控制台 (F12) 中是否有错误。通常是属性名称拼写错误或导入不正确。
  • GUI 未出现: 确保您已经使用 new GUI() 实例化了它。检查页面上是否有任何 CSS 可能会隐藏它(例如,<body> 上的 overflow: hidden)。
  • 性能低下: 使用 Stats.js 面板。如果 FPS 持续低于 60,您可能有太多对象、几何体过于复杂或动画循环中存在开销大的操作。尝试简化您的场景以找出原因。
  • 可视化坐标轴: 为了更好地理解对象的方向和位置,向您的场景添加一个 AxesHelper:const axesHelper = new THREE.AxesHelper(2); scene.add(axesHelper);