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

如何从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 00:22:50