1. 多场景处理

在 Three.js 中处理多个场景可以通过以下几种方式实现:

1.1. 创建和管理多个场景

1.1.1. 基本设置

// 创建渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建多个场景
const scene1 = new THREE.Scene();
const scene2 = new THREE.Scene();
const scene3 = new THREE.Scene();

// 创建多个相机(可根据需要)
const camera1 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const camera2 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

// 给不同场景设置不同背景
scene1.background = new THREE.Color(0x000000);
scene2.background = new THREE.Color(0x111122);
scene3.background = new THREE.Color(0x221111);

1.2. 渲染多个场景的方式

1.2.1. 方式一:顺序渲染(覆盖式)

function animate() {
    requestAnimationFrame(animate);

    // 清屏渲染第一个场景
    renderer.autoClear = true;
    renderer.render(scene1, camera1);

    // 不清屏渲染第二个场景(叠加)
    renderer.autoClear = false;
    renderer.render(scene2, camera2);

    // 继续渲染第三个场景
    renderer.render(scene3, camera3);

    // 重置为默认
    renderer.autoClear = true;
}

1.2.2. 方式二:视口分割(多重视口)

function renderMultiViewport() {
    const width = window.innerWidth;
    const height = window.innerHeight;

    // 场景1 - 左半部分
    renderer.setViewport(0, 0, width/2, height);
    renderer.setScissor(0, 0, width/2, height);
    renderer.setScissorTest(true);
    renderer.render(scene1, camera1);

    // 场景2 - 右上半部分
    renderer.setViewport(width/2, height/2, width/2, height/2);
    renderer.setScissor(width/2, height/2, width/2, height/2);
    renderer.render(scene2, camera2);

    // 场景3 - 右下半部分
    renderer.setViewport(width/2, 0, width/2, height/2);
    renderer.setScissor(width/2, 0, width/2, height/2);
    renderer.render(scene3, camera3);

    // 重置视口
    renderer.setViewport(0, 0, width, height);
    renderer.setScissorTest(false);
}

1.2.3. 方式三:渲染到纹理(Render Targets)

// 创建渲染目标
const renderTarget1 = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);
const renderTarget2 = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight);

function renderToTexture() {
    // 将场景渲染到纹理
    renderer.setRenderTarget(renderTarget1);
    renderer.render(scene1, camera1);

    renderer.setRenderTarget(renderTarget2);
    renderer.render(scene2, camera2);

    // 重置为默认渲染目标(屏幕)
    renderer.setRenderTarget(null);

    // 使用纹理创建平面显示
    const planeMaterial = new THREE.MeshBasicMaterial({
        map: renderTarget1.texture
    });
    const plane = new THREE.Mesh(new THREE.PlaneGeometry(5, 5), planeMaterial);
    scene3.add(plane); // 在第三个场景中显示
}

1.3. 完整示例:多场景切换

class MultiSceneManager {
    constructor() {
        this.scenes = new Map();
        this.currentScene = null;
        this.renderer = null;
        this.cameras = new Map();
    }

    init() {
        this.renderer = new THREE.WebGLRenderer({ antialias: true });
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(this.renderer.domElement);

        // 初始化场景
        this.createScene('main', 0x000000);
        this.createScene('ui', 0x000000);
        this.createScene('background', 0x111122);

        // 设置场景透明,支持叠加
        this.scenes.get('ui').background = null;
        this.renderer.autoClear = false;

        // 设置初始场景
        this.switchToScene('main');
    }

    createScene(name, bgColor) {
        const scene = new THREE.Scene();
        scene.name = name;

        if (bgColor !== null) {
            scene.background = new THREE.Color(bgColor);
        }

        // 为每个场景创建相机
        const camera = new THREE.PerspectiveCamera(
            75, 
            window.innerWidth / window.innerHeight, 
            0.1, 
            1000
        );
        camera.position.z = 5;

        this.scenes.set(name, scene);
        this.cameras.set(name, camera);

        return { scene, camera };
    }

    switchToScene(sceneName) {
        this.currentScene = sceneName;
    }

    render() {
        requestAnimationFrame(() => this.render());

        // 清屏
        this.renderer.clear();

        // 渲染背景场景
        this.renderer.render(
            this.scenes.get('background'),
            this.cameras.get('background')
        );

        // 不清屏渲染主场景(叠加)
        this.renderer.clearDepth(); // 清除深度缓冲区
        this.renderer.render(
            this.scenes.get(this.currentScene),
            this.cameras.get(this.currentScene)
        );

        // 渲染UI场景(在最上层)
        this.renderer.clearDepth();
        this.renderer.render(
            this.scenes.get('ui'),
            this.cameras.get('ui')
        );
    }

    getScene(name) {
        return this.scenes.get(name);
    }

    getCamera(name) {
        return this.cameras.get(name);
    }
}

// 使用示例
const sceneManager = new MultiSceneManager();
sceneManager.init();

// 向不同场景添加对象
const mainScene = sceneManager.getScene('main');
const cube = new THREE.Mesh(
    new THREE.BoxGeometry(),
    new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
mainScene.add(cube);

const uiScene = sceneManager.getScene('ui');
const uiText = new THREE.TextTexture('UI Element', { fontSize: 24 });
const uiPlane = new THREE.Mesh(
    new THREE.PlaneGeometry(),
    new THREE.MeshBasicMaterial({ map: uiText })
);
uiScene.add(uiPlane);

1.4. 性能优化建议

// 1. 按需渲染
class SelectiveRenderer {
    constructor() {
        this.activeScenes = new Set();
    }

    setSceneActive(name, active) {
        if (active) {
            this.activeScenes.add(name);
        } else {
            this.activeScenes.delete(name);
        }
    }

    render(scenes, cameras) {
        this.activeScenes.forEach(sceneName => {
            renderer.render(scenes.get(sceneName), cameras.get(sceneName));
        });
    }
}

// 2. 共享资源
const sharedGeometry = new THREE.BoxGeometry();
const sharedMaterial = new THREE.MeshBasicMaterial({ color: 0xffffff });

const cube1 = new THREE.Mesh(sharedGeometry, sharedMaterial);
scene1.add(cube1);

const cube2 = new THREE.Mesh(sharedGeometry, sharedMaterial);
scene2.add(cube2);

// 3. 使用层次结构(替代多个场景)
const parentScene = new THREE.Scene();
const layer1 = new THREE.Group();
const layer2 = new THREE.Group();

layer1.name = 'background';
layer2.name = 'foreground';

parentScene.add(layer1);
parentScene.add(layer2);

// 单独控制不同层级的可见性
layer1.visible = true;
layer2.visible = true;

1.5. 最佳实践

  1. 明确需求:根据具体需求选择合适的多场景方案
  2. 性能考虑:避免不必要的场景渲染
  3. 内存管理:及时清理不用的场景资源
  4. 事件处理:为每个场景实现独立的事件系统
  5. 调试工具:使用 Three.js 的辅助工具查看场景结构

选择哪种方案取决于你的具体应用场景,通常组合使用多种技术可以获得最佳效果。