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

