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

ThreeJS中如何从合并BufferGeometry获取点击几何体的name属性

问题原因

BufferGeometryUtils.mergeBufferGeometries 合并几何体时,只会拼接顶点、索引等渲染必需的核心数据,不会保留每个原始几何体的自定义属性(如你设置的name),合并后生成的是全新的单一几何体,因此无法直接从点击的mesh对象中拿到原始单个盒子的name属性。

可行解决方案

方案1:基于面索引映射(适配你的场景,所有盒子几何体完全相同)

原理:每个原始盒子的三角面数量完全一致,射线检测返回的faceIndex可以直接对应到原始盒子的序号。

修改后代码

几何体创建部分

// 组件内提前定义变量存储单个盒子的面数
this.faceCountPerCube = 0

addCubeToScene() {
  this.oDracoLoader = new DRACOLoader();
  this.oDracoLoader.setDecoderPath("./draco/");
  this.oGltfLoader = new GLTFLoader();
  this.oGltfLoader.setDRACOLoader(this.oDracoLoader);
  this.oDracoLoader.preload();
  const oThis = this;
  this.oGltfLoader.load(BoxModel, function(oObject) {
    oObject.scene.traverse(function(oObject) {
      if (oObject.isMesh) {
        var oObjectGeometry = oObject.geometry;
        // 计算单个盒子的三角面数量
        if (oObjectGeometry.index) {
          oThis.faceCountPerCube = oObjectGeometry.index.count / 3;
        } else {
          oThis.faceCountPerCube = oObjectGeometry.attributes.position.count / 3;
        }
        const oCubes = []
        for (var i = 0; i < 1000; i++) {
          var oGeometry = oObjectGeometry.clone();
          oGeometry.applyMatrix4(
            new THREE.Matrix4().makeTranslation(
              Math.random() * 3000,
              Math.random() * 5000,
              Math.random() * 1000
            )
          );
          oCubes.push(oGeometry);
        }
        var oGeometriesCubes = BufferGeometryUtils.mergeBufferGeometries(
          oCubes
        );
        oGeometriesCubes.computeBoundingSphere();
        const oTexture = new THREE.TextureLoader().load(BoxTexture);
        oTexture.magFilter = THREE.NearestFilter;
        var oMesh = new THREE.Mesh(
          oGeometriesCubes,
          new THREE.MeshLambertMaterial({
            map: oTexture,
            side: THREE.DoubleSide,
          })
        );
        oMesh.scale.set(0.5, 0.5, 0.5);
        oThis.scene.add(oMesh);
        oThis.renderScene();
      }
    });
  });
}

点击事件部分

onMouseClick(oEvent) {
  oEvent.preventDefault();
  oEvent.stopPropagation();
  oMouse.x =
    (oEvent.offsetX / this.renderer.domElement.clientWidth) * 2 - 1;
  oMouse.y =
    -(oEvent.offsetY / this.renderer.domElement.clientHeight) * 2 + 1;
  oRaycaster.setFromCamera(oMouse, this.camera);
  var oIntersects = oRaycaster.intersectObjects(this.scene.children, true);
  if (oIntersects[0] && oIntersects[0].object) {
    // 拿到点击的面索引
    const faceIndex = oIntersects[0].faceIndex;
    // 计算对应的盒子序号
    const boxIndex = Math.floor(faceIndex / this.faceCountPerCube);
    // 拼接得到对应name
    const boxName = `Box-${boxIndex}`;
    console.log(boxName);
  }
}

方案2:自定义顶点属性映射(适配几何体大小、面数不同的通用场景)

原理:给每个原始几何体的所有顶点添加自定义的boxId属性,合并时该属性会被一起合并,点击时通过顶点索引取到对应boxId,再映射到name。
简要实现步骤:

  • 循环创建每个几何体时,新增Float32Array存储每个顶点的boxId,值为当前循环序号i
  • 将该数组挂载为几何体的自定义属性:oGeometry.setAttribute('boxId', new THREE.BufferAttribute(boxIdArray, 1))
  • 合并几何体后,点击时通过oIntersects[0].face拿到三个顶点的索引,取任意一个索引去oIntersects[0].object.geometry.attributes.boxId中取值,即为对应boxId,再映射到name即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 05:45:04