You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js切换多层模型时不显示,断点调试时可见问题

问题分析与修复方案

核心问题根源

你的代码存在几个关键错误,导致主线程阻塞、渲染中断:

  • 同步sleep阻塞主线程:自定义的sleep函数通过同步循环占用主线程,浏览器无法执行渲染循环和事件处理,直接导致GPU空闲(占用率0%),所有视觉更新都被延迟到sleep结束后一次性执行。
  • 异步加载顺序混乱:objLoader.loadAsync是异步操作,循环发起的10个加载请求完成顺序不确定,仅在第10个模型加载完成后才执行图层切换,此时其他模型可能还未加载完毕。
  • 渲染逻辑错误:手动调用animate()无效,且主线程阻塞时requestAnimationFrame无法正常触发渲染。
  • 多处语法与变量错误:GUI绑定属性不匹配、变量名不一致、材质颜色缺少引号、错误获取WebGPU上下文干扰Three.js渲染。

修复后的完整代码

<!DOCTYPE html>
<html lang="en">
<body>
    <div id="container"></div>

    <script type="module">
        import Stats from 'three/addons/libs/stats.module.js';
        import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
        import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
        
        let scene, renderer, camera; 
        let settings; 
        let rootFolder, filesArray;
        const objLoader = new OBJLoader();
        const loadedModels = []; // 存储已加载的模型,确保顺序

        rootFolder = '/obj_files/';
        filesArray = ['name0.obj', 'name1.obj', 'name2.obj', 'name3.obj', 'name4.obj', 
                      'name5.obj', 'name6.obj', 'name7.obj', 'name8.obj', 'name9.obj'];
                      
        init();

        function init() {
            const container = document.getElementById('container');

            camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 1, 1000);
            camera.position.set(1, 4, -10);
            camera.lookAt(0, 0, 0);
            // 默认只启用图层0(初始模型)
            camera.layers.enable(0); 

            scene = new THREE.Scene();
            const ambientLight = new THREE.AmbientLight(0xcccccc, 0.4);
            scene.add(ambientLight);

            const pointLight = new THREE.PointLight(0xffffff, 0.8);
            camera.add(pointLight);
            scene.add(camera);
            // 灯光启用所有图层
            for (let i = 0; i < 10; i++) {
                pointLight.layers.enable(i);
            }
        
            // 初始化渲染器
            renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
            renderer.setPixelRatio(window.devicePixelRatio);
            renderer.setSize(window.innerWidth, window.innerHeight);
            container.appendChild(renderer.domElement);

            // 加载初始模型并创建GUI
            loadAllModels().then(() => {
                createPanel();
                animate();
            });
        }

        // 按顺序加载所有模型,确保加载完成后再执行动画
        async function loadAllModels() {
            for (let i = 0; i < filesArray.length; i++) {
                const file = rootFolder + filesArray[i];
                const modelData = await objLoader.loadAsync(file);
                const mesh = modelData.children[0];
                mesh.material = new THREE.MeshPhongMaterial({ color: '#000000' });
                mesh.layers.set(i); // 模型对应图层0-9
                // 除了初始模型,其他默认隐藏(相机不启用对应图层)
                if (i !== 0) {
                    camera.layers.disable(i);
                }
                scene.add(mesh);
                loadedModels.push(mesh);
            }
        }

        function createPanel() {
            const panel = new GUI({ width: 310 }); 
            const folder = panel.addFolder('Movement');               
            settings = { startMovement: false }; 
            folder.add(settings, 'startMovement').name('开始运动').onChange(toggleMovement);
        }

        let animationId = null;
        let currentLayer = 0;
        const intervalTime = 10000; // 切换间隔10秒

        function toggleMovement(start) {
            if (start) {
                // 启动动画循环
                animationId = setInterval(() => {
                    switchLayer();
                }, intervalTime);
            } else {
                // 停止动画
                clearInterval(animationId);
                // 恢复到初始图层
                camera.layers.disableAll();
                camera.layers.enable(0);
                currentLayer = 0;
            }
        }

        function switchLayer() {
            // 隐藏当前图层
            camera.layers.disable(currentLayer);
            // 切换到下一个图层
            currentLayer = (currentLayer + 1) % loadedModels.length;
            camera.layers.enable(currentLayer);
            // 最后切换回初始图层
            if (currentLayer === loadedModels.length - 1) {
                setTimeout(() => {
                    camera.layers.disable(currentLayer);
                    camera.layers.enable(0);
                    currentLayer = 0;
                }, intervalTime);
            }
        }

        function animate() {
            requestAnimationFrame(animate);
            renderer.render(scene, camera);
        }

        // 窗口大小调整处理
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
    </script>
</body>
</html>

关键修复说明

  1. 替换同步sleep为异步定时器:用setInterval和setTimeout实现非阻塞的图层切换,避免主线程被卡死,确保渲染循环持续运行。
  2. 按顺序加载模型:使用async/await确保所有模型加载完成后再启动动画,避免切换时模型未加载的问题。
  3. 修正图层逻辑:模型对应图层0-9,初始仅启用图层0,切换时通过disable/enable控制显示,而非toggle(避免状态混乱)。
  4. 修复语法与变量错误:
    • 修正GUI控件绑定的属性名,确保按钮能触发动画
    • 材质颜色添加引号,修复语法错误
    • 移除错误的WebGPU上下文获取代码,让Three.js正常管理渲染上下文
  5. 优化渲染逻辑:animate函数通过requestAnimationFrame持续渲染,无需手动调用;添加窗口大小调整监听,保证渲染自适应。

内容的提问来源于stack exchange,提问作者wdvhuqscgz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 12:37:02