Three.js中如何移动InstancedMesh实例至指定位置?
在Three.js中高效管理InstancedMesh实例的位置更新
核心思路
InstancedMesh的实例数据存在instanceMatrix缓冲区里,没法直接像普通数组那样修改位置,但你可以通过索引遍历+缓存位置数据的方式实现类似数组的操作逻辑,具体步骤如下:
1. 初始化时缓存实例位置
创建InstancedMesh的同时,维护一个数组存储每个实例的世界位置,方便后续快速判断和修改:
// 假设已准备好geometry和material const instanceMesh = new THREE.InstancedMesh(geometry, material, 150); const instancePositions = []; // 存储每个实例的Vector3位置 const tempMatrix = new THREE.Matrix4(); // 初始化实例到9个60×60的虚拟单元格 for (let i = 0; i < 150; i++) { // 替换成你实际的单元格分配逻辑 const cellIndex = Math.floor(i / (150 / 9)); const cellX = (cellIndex % 3) * 60; const cellZ = Math.floor(cellIndex / 3) * 60; const position = new THREE.Vector3(cellX, 0, cellZ); // 缓存位置 instancePositions.push(position); // 设置实例矩阵 tempMatrix.makeTranslation(position.x, position.y, position.z); instanceMesh.setMatrixAt(i, tempMatrix); } // 通知Three.js更新缓冲区 instanceMesh.instanceMatrix.needsUpdate = true;
2. 遍历实例并更新位置
角色移动时,遍历每个实例索引,判断其是否在可见区域外,若需要则移动到新单元格:
const tempMatrix = new THREE.Matrix4(); // 替换成你实际的可见区域边界 const visibleBounds = new THREE.Box3( new THREE.Vector3(-120, -10, -120), new THREE.Vector3(120, 10, 120) ); // 遍历所有实例 for (let i = 0; i < instanceMesh.count; i++) { const currentPos = instancePositions[i]; // 判断是否在可见区域外 if (!visibleBounds.containsPoint(currentPos)) { // 替换成你获取空单元格位置的逻辑 const newCellX = getAvailableCellX(); const newCellZ = getAvailableCellZ(); const newPos = new THREE.Vector3(newCellX, 0, newCellZ); // 更新缓存的位置 instancePositions[i].copy(newPos); // 更新实例矩阵 tempMatrix.makeTranslation(newPos.x, newPos.y, newPos.z); instanceMesh.setMatrixAt(i, tempMatrix); } } // 必须通知缓冲区更新,否则修改不会生效 instanceMesh.instanceMatrix.needsUpdate = true;
3. 关键注意事项
- 每次修改实例矩阵后,必须设置
instanceMatrix.needsUpdate = true,否则Three.js不会重新上传缓冲区数据。 - 复用
tempMatrix这类临时对象,避免在循环里频繁创建新的Matrix4或Vector3,减少GC开销。 - 如果需要基于相机视锥体判断可见性,可以用
Frustum替代Box3:const frustum = new THREE.Frustum(); frustum.setFromProjectionMatrix( new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse) ); // 判断实例是否在视锥体内 if (!frustum.containsPoint(currentPos)) { // 执行移动逻辑 }
内容的提问来源于stack exchange,提问作者Timur Gafforov
相关产品推荐
相关产品推荐

