1. Three.js辅助类

在Three.js中,辅助类(Helpers)是用来可视化场景中各种元素(如坐标轴、灯光、相机等)的工具类。它们对于调试和开发非常有帮助。以下是一些常用的辅助类:

常用辅助类概览

1.1. 基础辅助类

// 坐标轴辅助器
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);

1.1.1. 灯光辅助类

// 点光源辅助器
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);

1.1.2. 相机辅助类

// 相机辅助器(显示视锥体)
const cameraHelper = new THREE.CameraHelper(camera);
scene.add(cameraHelper);

// 透视相机辅助器
const perspectiveCameraHelper = new THREE.CameraHelper(perspectiveCamera);
scene.add(perspectiveCameraHelper);

1.1.3. 几何体辅助类

// 包围盒辅助器
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);

1.1.4. 其他辅助类

// 平面辅助器
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);

实用封装类

1.1.5. HelperManager - 辅助器管理器

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);

1.1.6. DebugGUI - 调试界面集成

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();
    }
}

自定义辅助类

1.1.7. 自定义相机视锥体辅助器

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;
    }
}

1.1.8. 轨迹辅助器

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();
    }
}

使用建议

  1. 开发阶段:大量使用辅助类进行调试
  2. 生产环境:移除所有辅助类以减少性能开销
  3. 性能考虑:辅助类也会消耗渲染资源,不宜过多使用
  4. 动态更新:需要每帧更新的辅助器(如SpotLightHelper)要在动画循环中调用update()

性能优化建议

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开发的效率和调试能力。建议根据项目需求选择合适的辅助类,并在开发完成后移除不必要的辅助器。