如何在Forge模型上正确渲染动态更新位置的Three.js几何体
问题
在Forge模型上渲染位置持续更新的圆柱体几何体时,同一位置再次渲染该几何体显示异常。尝试使用window.viewer.impl.scene.remove(cylinder);和window.viewer.impl.sceneUpdated(true);可以解决显示问题,但会导致整个模型反复更新。
原始代码
import React, { useState, useEffect } from "react"; import axios from "axios"; import { fetchEventSource } from "@microsoft/fetch-event-source"; import { element } from "prop-types"; import { json } from "react-router-dom"; const serverBaseURL = "http://127.0.0.1:2204/video_feed"; const overlayScene = "Twin_Overlay"; function addCylinder(positionsList) { if (window.viewer) { if (window.viewer.overlays.hasScene(overlayScene)) { window.viewer.overlays.removeScene(overlayScene); } window.viewer.overlays.addScene(overlayScene); } if (window.THREE) { for (let index = 0; index < positionsList.length; index++) { let position = positionsList[index]; let geometry = new window.THREE.CylinderGeometry(0.5, 0.5, 4, 8); let material = new window.THREE.MeshBasicMaterial({ color: 0xff0000 }); let cylinder = new window.THREE.Mesh(geometry, material); cylinder.rotateX(Math.PI / 2); cylinder.position.set(position[0] / 1000, position[1] / 1000, 0); window.viewer?.overlays.addMesh(cylinder, overlayScene); window.viewer.impl.scene.remove(cylinder); window.viewer.impl.sceneUpdated(true); } } } async function getData() { let runTest = true; if (!runTest) { const response = await fetch(""); // const reader = response.body.getReader(); const positionsList = await response.json(); // let done, value; // while (!done) { // ({ value, done } = await reader.read()); // if (done) { // return; // } // let positionsList = new TextDecoder().decode(value); // positionsList = JSON.parse(positionsList); addCylinder(positionsList); console.log(positionsList); // } } else { setInterval(() => { let positionsList = test_getRandomPositions(); addCylinder(positionsList); }, 1000); } } function test_getRandomPositions() { let range = [ [-35000, 30000], [-7000, 16000], ]; let positionsList = []; let people_count = Math.round(Math.random() * 10); for (let i = 0; i < people_count; i++) { positionsList.push([ range[0][0] + Math.random() * (range[0][1] - range[0][0]), range[1][0] + Math.random() * (range[1][1] - range[1][0]), ]); } // return [[-4967, -2272]] return positionsList; } export default getData;
解决方案
问题分析
- 每次调用
addCylinder都删除并重建整个overlay场景,完全没必要,还会造成性能浪费 - 循环内调用
window.viewer.impl.scene.remove(cylinder)会把刚添加到overlay的圆柱体从主场景移除,等于白添加 - 循环内频繁调用
sceneUpdated(true)会强制整个模型场景刷新,导致反复更新的问题
修正后的代码
import React, { useState, useEffect } from "react"; import axios from "axios"; import { fetchEventSource } from "@microsoft/fetch-event-source"; import { element } from "prop-types"; import { json } from "react-router-dom"; const serverBaseURL = "http://127.0.0.1:2204/video_feed"; const overlayScene = "Twin_Overlay"; // 复用几何体和材质,避免重复创建提升性能 let cylinderGeometry = null; let cylinderMaterial = null; function initOverlay() { if (window.viewer && !window.viewer.overlays.hasScene(overlayScene)) { window.viewer.overlays.addScene(overlayScene); } if (window.THREE) { cylinderGeometry = new window.THREE.CylinderGeometry(0.5, 0.5, 4, 8); cylinderMaterial = new window.THREE.MeshBasicMaterial({ color: 0xff0000 }); } } function updateCylinders(positionsList) { if (!window.viewer || !cylinderGeometry || !cylinderMaterial) return; // 清除overlay场景中所有已有的圆柱体,而不是删除整个场景 const existingMeshes = window.viewer.overlays.getMeshes(overlayScene); existingMeshes.forEach(mesh => { window.viewer.overlays.removeMesh(mesh, overlayScene); // 销毁mesh释放资源 mesh.geometry.dispose(); mesh.material.dispose(); }); // 添加新的圆柱体 positionsList.forEach(position => { const cylinder = new window.THREE.Mesh(cylinderGeometry, cylinderMaterial); cylinder.rotateX(Math.PI / 2); cylinder.position.set(position[0] / 1000, position[1] / 1000, 0); window.viewer.overlays.addMesh(cylinder, overlayScene); }); // 只刷新overlay相关的部分,而不是整个模型场景 window.viewer.impl.invalidate(true); } async function getData() { // 初始化overlay和复用资源 initOverlay(); let runTest = true; if (!runTest) { const response = await fetch(""); const positionsList = await response.json(); updateCylinders(positionsList); console.log(positionsList); } else { setInterval(() => { const positionsList = test_getRandomPositions(); updateCylinders(positionsList); }, 1000); } } function test_getRandomPositions() { let range = [ [-35000, 30000], [-7000, 16000], ]; let positionsList = []; let people_count = Math.round(Math.random() * 10); for (let i = 0; i < people_count; i++) { positionsList.push([ range[0][0] + Math.random() * (range[0][1] - range[0][0]), range[1][0] + Math.random() * (range[1][1] - range[1][0]), ]); } return positionsList; } export default getData;
关键改进点
- 复用资源:提前创建并复用几何体和材质,避免每次循环都重复创建,减少内存占用和性能消耗
- 局部清除:只清除overlay场景中的已有mesh,而非删除整个场景
- 局部刷新:使用
viewer.impl.invalidate(true)替代sceneUpdated(true),只刷新必要的部分,避免整个模型反复更新 - 资源清理:移除旧mesh时销毁其几何体和材质,防止内存泄漏
内容的提问来源于stack exchange,提问作者Mohd. Amaan
相关产品推荐
相关产品推荐

