three.js - 如何从 ThreeJS 中的 BufferGeometry 中获取单击的几何图形的名称属性?
问题描述
我正在开发一个 VueJs 应用程序并在场景中添加了 1000 个盒子模型。虽然我在单击盒子模型时设置了每个几何图形的名称属性,但我无法获得盒子几何图形的名称。我认为这是由应用合并缓冲区几何引起的。是否可以通过某种方式单击来获取名称属性?
这是我的代码;
addCubeToScene() {
this.oDracoLoader = new DRACOLoader();
this.oDracoLoader.setDecoderPath("./draco/");
this.oGltfLoader = new GLTFLoader();
this.oGltfLoader.setDRACOLoader(this.oDracoLoader);
this.oDracoLoader.preload();
this.oGltfLoader.load(BoxModel, function(oObject) {
oObject.scene.traverse(function(oObject) {
if (oObject.isMesh) {
var oObjectGeometry = oObject.geometry;
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
)
);
oGeometry.name = "Box-" + i;
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,
})
// new THREE.MeshNormalMaterial()
);
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) {
var oObject = oIntersects[0].object;
console.log(oObject); // I would like to get clicked geometry's name from this object
}
}
这是代表盒子模型视图的图像;
这是盒子几何数据的结果
当我合并所有几何图形时,我看不到任何名称属性
解决方案
我假设您正在尝试合并几何体以提高渲染性能。虽然您确实设法减少了 GPU 必须进行的绘制调用次数,但您也删除了不同BufferGeometry
对象的所有唯一性。您的几何图形现在由一个大缓冲区表示(调试oGeometriesCubes.attributes.position.array
以查看此内容)。
为了提高性能,我们不仅可以为您节省 GPU 绘制调用,还可以节省内存,而且我们还可以让您的盒子选择工作。让我们从头开始。看起来我们已经倒退了一步,但随后一切都会一起倒下……
您的几何定义
首先,您不需要唯一标识每个BufferGeometry
. Mesh
对象可以共享几何!和材料!这相当于节省内存。
拥有独特的Mesh
对象还意味着您可以将name
翻译信息放在Mesh
级别上。在几何级别翻译 1000 次是昂贵的,即使你像你一样预先做。相信 GPU 可以从 Mesh 级别非常快速地完成这些转换。这实际上是 GPU 的工作。
您的网格定义
现在,我们仍在查看 1000 个网格,这意味着 1000 个绘制调用,对吗?这就是InstancedMesh
发挥作用的地方。
实例化——用非常简单的术语来说——是一种让 GPU 不仅重新使用几何缓冲区信息的方式,而且还可以在一次滑动中执行所有 1000 个网格的绘制,而不是一次绘制一个。
把它们加起来:
- 仅使用一个
BufferGeometry
对象... - 还有一种材料...
- 我们将绘制 1000 个实例...
- 在一次抽奖中。
准备好?我们开始做吧。
编码
let geometry = yourGeometryFromGLTF;
// load your texture here...
let material = new new THREE.MeshLambertMaterial({
map: oTexture,
side: THREE.DoubleSide,
})
let iMesh = new THREE.InstancedMesh( geometry, material, 1000 );
let translateMatrix = new THREE.Matrix4();
let scaleMatrix = new THREE.Matrix4().makeScale( 0.5, 0.5, 0.5 );
let finalMatrix = new THREE.Matrix4();
for( let i = 0; i < 1000; ++i ){
translateMatrix.makeTranslation(
Math.random() * 1500,
Math.random() * 2500,
Math.random() * 500
);
finalMatrix.multiplyMatrices( translateMatrix, scaleMatrix );
iMesh.setMatrixAt( i, finalMatrix );
}
imesh.instanceMatrix.needsUpdate = true; // IMPORTANT
scene.add( iMesh );
现在您Raycaster
应该返回实例,但需要多一步才能获得您选择的框的“ID”。
let intersects = raycaster.intersectObjects(scene);
if(intersects.length > 0){
let boxName = `Box-${ intersects[0].instanceId }`;
}
将instanceId
是您的盒子实例的索引InstancedMesh
。您甚至可以使用它来获取有关该实例的更多信息,例如它的转换矩阵:
let iMatrix = new THREE.Matrix4();
iMesh.getMatrixAt( intersects.instanceId, iMatrix );
推荐阅读
- android - 从 iPhone 拍摄的照片在 Android 手机上旋转 90 度
- php - Opencart 2.x - 客户组 ID 返回为 0
- wordpress - 将此视频右对齐
- java - 如何将第一个图像从 ArrayList 设置为 imageView
- android-studio - Search-Everywhere in Android Studio (IntelliJ) 使搜索文件变得多余?
- apache-nifi - 如何远程访问 NiFi?
- python - 使用 ConnectionPatch 在 imshow 热图之间创建链接
- bash - 打印两个模式之间的所有行,独占,仅第一个实例(在 sed、AWK 或 Perl 中)
- javascript - 在 node.js 中需要 mocha 测试文件导致 TypeError: describe is not a function
- sql-server - 在过程中使用动态 sql 将数据插入模式表