如何从THREE.js的IcosahedronGeometry对象复制CANNON.js刚体形状?
解决Three.js与Cannon.js碰撞体和视觉模型匹配问题
针对你用正二十面体几何体(IcosahedronGeometry)搭配Cannon.js ConvexPolyhedron碰撞体的需求,直接提取Three.js几何体的顶点和面数据即可完成匹配,步骤如下:
1. 提取Three.js几何体的顶点数据
Three.js的BufferGeometry(包括IcosahedronGeometry)顶点存储在attributes.position中,需要将其转换为Cannon.js所需的CANNON.Vec3数组:
// 从Three.js几何体提取顶点 const vertices = []; const positionArray = geometry.attributes.position.array; for (let i = 0; i < positionArray.length; i += 3) { vertices.push(new CANNON.Vec3( positionArray[i], positionArray[i + 1], positionArray[i + 2] )); }
2. 提取面的索引数据
几何体的面索引存储在index.array中,每三个连续索引对应一个三角形面:
// 从Three.js几何体提取面索引 const faces = []; const indexArray = geometry.index.array; for (let i = 0; i < indexArray.length; i += 3) { faces.push([ indexArray[i], indexArray[i + 1], indexArray[i + 2] ]); }
3. 创建Cannon.js凸多面体碰撞体
用提取的顶点和面数组创建CANNON.ConvexPolyhedron,并关联到物理体:
// 创建Cannon碰撞形状 const convexShape = new CANNON.ConvexPolyhedron(vertices, faces); // 创建物理体 const body = new CANNON.Body({ mass: 1, // 根据需求设置质量,0表示静态物体 shape: convexShape, position: new CANNON.Vec3(0, 5, 0) // 初始位置和Three.js模型对应 }); world.addBody(body); // world是你创建的Cannon物理世界
4. 同步物理体与视觉模型的位置和旋转
在动画循环中更新Three.js模型的位置和旋转,保持与Cannon物理体一致:
function animate() { requestAnimationFrame(animate); // 同步位置 finalCube.position.copy(body.position); // 同步旋转 finalCube.quaternion.copy(body.quaternion); renderer.render(scene, camera); } animate();
完整示例代码
// 初始化Three.js场景 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 创建视觉模型 const geometry = new THREE.IcosahedronGeometry(1, 0); const material = new THREE.MeshLambertMaterial({ color: 0x009900 }); const finalCube = new THREE.Mesh(geometry, material); scene.add(finalCube); camera.position.z = 5; // 添加光源 const light = new THREE.PointLight(0xffffff, 1); light.position.set(5, 5, 5); scene.add(light); // 初始化Cannon物理世界 const world = new CANNON.World(); world.gravity.set(0, -9.82, 0); world.broadphase = new CANNON.NaiveBroadphase(); // 提取顶点和面数据 const vertices = []; const positionArray = geometry.attributes.position.array; for (let i = 0; i < positionArray.length; i += 3) { vertices.push(new CANNON.Vec3(positionArray[i], positionArray[i + 1], positionArray[i + 2])); } const faces = []; const indexArray = geometry.index.array; for (let i = 0; i < indexArray.length; i += 3) { faces.push([indexArray[i], indexArray[i + 1], indexArray[i + 2]]); } // 创建碰撞体并添加到物理世界 const convexShape = new CANNON.ConvexPolyhedron(vertices, faces); const body = new CANNON.Body({ mass: 1, shape: convexShape, position: new CANNON.Vec3(0, 5, 0) }); world.addBody(body); // 添加地面 const groundShape = new CANNON.Plane(); const groundBody = new CANNON.Body({ mass: 0 }); groundBody.addShape(groundShape); groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0); world.addBody(groundBody); // 动画循环同步位置旋转 function animate() { requestAnimationFrame(animate); world.step(1/60); finalCube.position.copy(body.position); finalCube.quaternion.copy(body.quaternion); renderer.render(scene, camera); } animate();
注意事项
- 如果你的几何体有缩放,需要将缩放应用到Cannon的顶点数据上,或者设置Cannon物理体的
scale属性。 - 对于非凸几何体,
ConvexPolyhedron不适用,需要用其他碰撞形状(比如Trimesh),但Trimesh性能较低,适合静态物体。
内容的提问来源于stack exchange,提问作者yoav trachtman cohen
相关产品推荐
相关产品推荐

