在Three.js中,辅助类(Helpers)是用来可视化场景中各种元素(如坐标轴、灯光、相机等)的工具类。它们对于调试和开发非常有帮助。以下是一些常用的辅助类:
// 坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 网格辅助器
const gridHelper = new THREE.GridHelper(10, 10);
scene.add(gridHelper);
// 极坐标网格辅助器
const polarGridHelper = new THREE.PolarGridHelper(5, 16, 8, 64, 0x0000ff, 0x808080);
scene.add(polarGridHelper);
// 点光源辅助器
const pointLightHelper = new THREE.PointLightHelper(pointLight, 0.5);
scene.add(pointLightHelper);
// 聚光灯辅助器
const spotLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(spotLightHelper);
// 需要每帧更新
function animate() {
spotLightHelper.update();
requestAnimationFrame(animate);
}
// 方向光辅助器
const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5);
scene.add(directionalLightHelper);
// 半球光辅助器
const hemisphereLightHelper = new THREE.HemisphereLightHelper(hemisphereLight, 5);
scene.add(hemisphereLightHelper);
// 相机辅助器(显示视锥体)
const cameraHelper = new THREE.CameraHelper(camera);
scene.add(cameraHelper);
// 透视相机辅助器
const perspectiveCameraHelper = new THREE.CameraHelper(perspectiveCamera);
scene.add(perspectiveCameraHelper);
// 包围盒辅助器
const boxHelper = new THREE.BoxHelper(mesh);
scene.add(boxHelper);
// 包围球辅助器
const sphereHelper = new THREE.SphereHelper(mesh, 0xff0000);
scene.add(sphereHelper);
// 顶点法向量辅助器
const vertexNormalsHelper = new THREE.VertexNormalsHelper(mesh, 0.5, 0xff0000);
scene.add(vertexNormalsHelper);
// 面法向量辅助器
const faceNormalsHelper = new THREE.FaceNormalsHelper(mesh, 0.5, 0x00ff00);
scene.add(faceNormalsHelper);
// 平面辅助器
const planeHelper = new THREE.PlaneHelper(plane, 5, 0xffff00);
scene.add(planeHelper);
// 箭头辅助器
const arrowHelper = new THREE.ArrowHelper(
new THREE.Vector3(1, 0, 0), // 方向
new THREE.Vector3(0, 0, 0), // 起点
3, // 长度
0xff0000 // 颜色
);
scene.add(arrowHelper);
// 骨骼辅助器
const skeletonHelper = new THREE.SkeletonHelper(skinnedMesh);
scene.add(skeletonHelper);
// 点辅助器
const pointsHelper = new THREE.PointsHelper(points, 0.2, 0xff0000);
scene.add(pointsHelper);
class HelperManager {
constructor(scene) {
this.scene = scene;
this.helpers = new Map();
}
// 添加辅助器
addHelper(name, type, target, ...args) {
let helper;
switch(type) {
case 'axes':
helper = new THREE.AxesHelper(...args);
break;
case 'grid':
helper = new THREE.GridHelper(...args);
break;
case 'light':
helper = this.createLightHelper(target, ...args);
break;
case 'camera':
helper = new THREE.CameraHelper(target);
break;
case 'box':
helper = new THREE.BoxHelper(target);
break;
default:
console.warn(`Unknown helper type: ${type}`);
return;
}
this.scene.add(helper);
this.helpers.set(name, { helper, type, target });
return helper;
}
// 创建灯光辅助器
createLightHelper(light, ...args) {
if (light.isPointLight) {
return new THREE.PointLightHelper(light, ...args);
} else if (light.isSpotLight) {
const helper = new THREE.SpotLightHelper(light, ...args);
// 设置更新回调
helper.update = helper.update.bind(helper);
return helper;
} else if (light.isDirectionalLight) {
return new THREE.DirectionalLightHelper(light, ...args);
} else if (light.isHemisphereLight) {
return new THREE.HemisphereLightHelper(light, ...args);
}
}
// 移除辅助器
removeHelper(name) {
const helperData = this.helpers.get(name);
if (helperData) {
this.scene.remove(helperData.helper);
this.helpers.delete(name);
}
}
// 切换辅助器显示/隐藏
toggleHelper(name, visible) {
const helperData = this.helpers.get(name);
if (helperData) {
helperData.helper.visible = visible !== undefined ? visible : !helperData.helper.visible;
}
}
// 更新辅助器(用于需要每帧更新的辅助器)
update() {
this.helpers.forEach(({ helper, type }) => {
if (type === 'light' && helper.update) {
helper.update();
}
});
}
// 清除所有辅助器
clear() {
this.helpers.forEach(({ helper }) => {
this.scene.remove(helper);
});
this.helpers.clear();
}
}
// 使用示例
const helperManager = new HelperManager(scene);
helperManager.addHelper('mainAxes', 'axes', null, 5);
helperManager.addHelper('mainGrid', 'grid', null, 10, 10);
class DebugGUI {
constructor(scene) {
this.scene = scene;
this.helpers = {};
// 如果有dat.GUI
if (typeof dat !== 'undefined') {
this.gui = new dat.GUI();
this.setupHelpersFolder();
}
}
setupHelpersFolder() {
const helpersFolder = this.gui.addFolder('Helpers');
// 坐标轴辅助器
this.helpers.axes = {
visible: false,
size: 5,
create: () => {
if (this.axesHelper) this.scene.remove(this.axesHelper);
this.axesHelper = new THREE.AxesHelper(this.helpers.axes.size);
this.scene.add(this.axesHelper);
this.axesHelper.visible = this.helpers.axes.visible;
}
};
helpersFolder.add(this.helpers.axes, 'visible')
.name('Show Axes')
.onChange((value) => {
if (this.axesHelper) {
this.axesHelper.visible = value;
} else if (value) {
this.helpers.axes.create();
}
});
helpersFolder.add(this.helpers.axes, 'size', 1, 20)
.name('Axes Size')
.onChange(() => {
if (this.axesHelper && this.helpers.axes.visible) {
this.scene.remove(this.axesHelper);
this.helpers.axes.create();
}
});
// 网格辅助器
this.helpers.grid = {
visible: false,
size: 10,
divisions: 10,
create: () => {
if (this.gridHelper) this.scene.remove(this.gridHelper);
this.gridHelper = new THREE.GridHelper(
this.helpers.grid.size,
this.helpers.grid.divisions
);
this.scene.add(this.gridHelper);
this.gridHelper.visible = this.helpers.grid.visible;
}
};
helpersFolder.add(this.helpers.grid, 'visible')
.name('Show Grid')
.onChange((value) => {
if (this.gridHelper) {
this.gridHelper.visible = value;
} else if (value) {
this.helpers.grid.create();
}
});
helpersFolder.open();
}
}
class CustomCameraHelper extends THREE.Object3D {
constructor(camera, color = 0xffffff) {
super();
this.camera = camera;
this.color = new THREE.Color(color);
this.createFrustum();
this.update();
}
createFrustum() {
const geometry = new THREE.BufferGeometry();
const material = new THREE.LineBasicMaterial({ color: this.color });
// 创建视锥体线条
const positions = new Float32Array(24 * 3);
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
// 连接点的索引
const indices = [
0,1, 1,2, 2,3, 3,0, // 近平面
4,5, 5,6, 6,7, 7,4, // 远平面
0,4, 1,5, 2,6, 3,7 // 连接线
];
geometry.setIndex(indices);
this.frustumLines = new THREE.LineSegments(geometry, material);
this.add(this.frustumLines);
}
update() {
if (!this.camera) return;
const positions = this.frustumLines.geometry.attributes.position.array;
// 获取相机视锥体8个角点
const frustum = new THREE.Frustum();
const projectionMatrix = new THREE.Matrix4()
.multiplyMatrices(this.camera.projectionMatrix, this.camera.matrixWorldInverse);
frustum.setFromProjectionMatrix(projectionMatrix);
const corners = [];
for (let x = -1; x <= 1; x += 2) {
for (let y = -1; y <= 1; y += 2) {
for (let z = -1; z <= 1; z += 2) {
const vector = new THREE.Vector3(x, y, z);
vector.applyMatrix4(this.camera.projectionMatrixInverse);
corners.push(vector);
}
}
}
// 更新顶点位置
for (let i = 0; i < 8; i++) {
positions[i * 3] = corners[i].x;
positions[i * 3 + 1] = corners[i].y;
positions[i * 3 + 2] = corners[i].z;
}
this.frustumLines.geometry.attributes.position.needsUpdate = true;
}
}
class TrajectoryHelper extends THREE.Object3D {
constructor(maxPoints = 100, color = 0xff0000) {
super();
this.maxPoints = maxPoints;
this.points = [];
this.color = color;
this.createLine();
}
createLine() {
const geometry = new THREE.BufferGeometry();
const material = new THREE.LineBasicMaterial({
color: this.color,
linewidth: 2
});
this.line = new THREE.Line(geometry, material);
this.add(this.line);
}
addPoint(position) {
this.points.push(position.clone());
// 限制点数
if (this.points.length > this.maxPoints) {
this.points.shift();
}
this.updateLine();
}
updateLine() {
const positions = new Float32Array(this.points.length * 3);
this.points.forEach((point, i) => {
positions[i * 3] = point.x;
positions[i * 3 + 1] = point.y;
positions[i * 3 + 2] = point.z;
});
this.line.geometry.setAttribute(
'position',
new THREE.BufferAttribute(positions, 3)
);
this.line.geometry.setDrawRange(0, this.points.length);
}
clear() {
this.points = [];
this.updateLine();
}
}
class OptimizedHelperManager {
constructor() {
this.helpers = new Set();
this.updateList = new Set(); // 需要每帧更新的辅助器
}
add(helper) {
this.helpers.add(helper);
// 标记需要更新的辅助器
if (helper.update && typeof helper.update === 'function') {
this.updateList.add(helper);
}
}
remove(helper) {
this.helpers.delete(helper);
this.updateList.delete(helper);
}
update() {
// 批量更新需要每帧更新的辅助器
this.updateList.forEach(helper => helper.update());
}
setVisible(visible) {
// 一键显示/隐藏所有辅助器
this.helpers.forEach(helper => {
helper.visible = visible;
});
}
}
这些辅助类可以大大提高Three.js开发的效率和调试能力。建议根据项目需求选择合适的辅助类,并在开发完成后移除不必要的辅助器。