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

如何在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;
解决方案

问题分析

  1. 每次调用addCylinder都删除并重建整个overlay场景,完全没必要,还会造成性能浪费
  2. 循环内调用window.viewer.impl.scene.remove(cylinder)会把刚添加到overlay的圆柱体从主场景移除,等于白添加
  3. 循环内频繁调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 21:40:18