THREE.js循环内渲染Canvas无法实时更新问题求助
解决方案
核心问题分析
同步for循环会阻塞浏览器事件循环,导致UI渲染线程被挂起——哪怕循环内调用了render,所有渲染操作都会被排队,直到整个循环执行完毕才会一次性显示结果。另外renderScene里的无限递归requestAnimationFrame会和循环内的渲染请求冲突,进一步加剧了渲染延迟。
具体修复步骤
- 异步分帧处理:将同步循环改为基于
requestAnimationFrame的异步递归处理,每帧处理一个扇区,给浏览器留出渲染时间。 - 优化资源创建:把重复创建的
PointsMaterial、颜色比例尺移到循环外部,避免不必要的内存开销和性能损耗。 - 调整渲染逻辑:移除
renderScene的无限递归,改为按需触发渲染,确保每次添加扇区后立即更新画布。
修改后的代码示例
// 把重复创建的资源移到外部,避免循环内重复实例化 const reflectivityColourScale = d3.scaleQuantize().domain([-32.0, 94.5]).range(reflectivityColours); const pointsMaterial = new THREE.PointsMaterial({ size: 2, vertexColors: true, sizeAttenuation: false }); const drawReflectivityMap = (scans, reflectivityData, azimuthData, scene, renderer, cameraPosition, camera) => { let currentScanIndex = 0; const currentCamera = camera; const currentRenderer = renderer; // 异步处理单个扇区的函数 const processScan = () => { if (currentScanIndex >= scans) return; // 终止条件 console.log('Drawing Reflectivity ' + currentScanIndex); const reflectivity = reflectivityData[currentScanIndex]; const azimuth = utils.radians(azimuthData[currentScanIndex]); const sinMultiplier = Math.sin(azimuth); const cosMultiplier = Math.cos(azimuth); const initialRange = mikeUtilities.addRange( mikeUtilities.multiplyRange(mikeUtilities.createRange(0, reflectivity.GateCount, 1), reflectivity.GateSize), reflectivity.FirstGate ); const x = utils.multiplyRange(initialRange, sinMultiplier); const y = utils.multiplyRange(initialRange, cosMultiplier); const pointsGraph = []; const coloursGraph = []; x.forEach((_, i) => { if (reflectivity.MomentDataValues[i] > -33) { const dataPointColour = new THREE.Color(reflectivityColourScale(reflectivity.MomentDataValues[i])); pointsGraph.push(x[i], y[i], 0); coloursGraph.push(dataPointColour.r, dataPointColour.g, dataPointColour.b); } }); const geometry = new THREE.BufferGeometry(); geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(pointsGraph), 3)); geometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(coloursGraph), 3)); const pointsMap = new THREE.Points(geometry, pointsMaterial); scene.add(pointsMap); // 触发一次渲染 currentCamera.position.z = cameraPosition; currentRenderer.render(scene, currentCamera); currentScanIndex++; // 调度下一帧处理下一个扇区 requestAnimationFrame(processScan); }; // 启动异步处理 requestAnimationFrame(processScan); };
额外优化建议
- 如果2880个扇区逐个处理速度太慢,可以每次处理多个扇区(比如一次处理10个),平衡渲染流畅度和总耗时。
- 可以添加进度提示,比如更新DOM显示当前渲染的扇区数量,提升用户体验。
内容的提问来源于stack exchange,提问作者Mike Upjohn
相关产品推荐
相关产品推荐

