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
相关产品推荐
相关产品推荐

