Toggle navigation
集客麦麦@谢坤
首页
随笔
首页
>>
创作中心
>>
cannonjs 的...
cannonjs 的 ConvexPolyhedron 和 Trimesh 使用和区别
在Cannon.js中,ConvexPolyhedron和Trimesh是两种常用的碰撞体类型,它们在形状表示、性能和适用场景上有显著区别,而face参数的组织方式也因类型而异。以下是对三者的综合分析: ### **1. 核心区别对比** | **特性** | **ConvexPolyhedron** | **Trimesh** | |------------------------|---------------------------------------|----------------------------------------| | **形状限制** | 只能表示凸形状(任意两点连线完全在内部) | 可表示任意形状(包括凹形状) | | **定义方式** | 通过顶点(`vertices`)和面(`faces`)定义 | 通过顶点数组(`vertices`)和三角形索引(`faces`)定义 | | **face参数结构** | 二维数组:`[[顶点索引1, 索引2, 索引3], ...]` | 一维数组:`[三角形1顶点1, 顶点2, 顶点3, 三角形2顶点1, ...]` | | **面的组织方式** | 每个子数组表示一个完整的凸多边形面 | 每3个索引强制组成一个三角形 | | **性能** | 高(凸形状碰撞检测优化) | 低(需遍历大量三角形) | | **物理行为** | 支持滚动、滑动、稳定堆叠等复杂物理交互 | 仅适合静态碰撞体(动态物体会不稳定) | | **典型应用** | 箱子、石头、不规则凸物体 | 地形、建筑、复杂模型的静态碰撞体 | ### ConvexPolyhedron 使用 在 Cannon.js 物理引擎里,ConvexPolyhedron 和 Trimesh 是两种不同的碰撞形状,它们主要存在以下差异: #### 1. 基本概念 - **ConvexPolyhedron(凸多面体)**:它是由一组顶点和平面构成的凸形状。这里的“凸”意味着在该形状内部任意两点之间连线,这条线都处于形状内部。像立方体、金字塔这类都属于常见的凸多面体。 - **Trimesh(三角网格)**:它是由众多三角形面组合而成的网格形状,能够表示任意复杂的几何体,既可以是凸的,也可以是凹的。 #### 2. 主要区别 | **特性** | **ConvexPolyhedron** | **Trimesh** | |-------------------|------------------------------------------|------------------------------------------| | **形状限制** | 只能表示凸形状 | 可以表示任意形状,包括凹形状 | | **性能表现** | 性能较好,因为凸形状的碰撞检测算法更为高效 | 性能较差,尤其是面对复杂网格时 | | **创建方式** | 通过顶点和平面来定义 | 基于三角形网格数据创建 | | **物理模拟效果** | 支持滚动、滑动等复杂物理交互 | 主要用于静态场景,动态物体会有不稳定的情况 | | **应用场景** | 适用于像箱子、石头这类凸物体 | 适用于地形、建筑等复杂静态环境 | #### 3. 示例代码 下面是使用这两种形状的简单代码示例: ```javascript // ConvexPolyhedron 示例 - 创建一个四面体 const vertices = [ new CANNON.Vec3(0, 0, 0), new CANNON.Vec3(1, 0, 0), new CANNON.Vec3(0, 1, 0), new CANNON.Vec3(0, 0, 1) ]; const faces = [ [0, 1, 2], // 底面 [0, 1, 3], // 右侧面 [0, 2, 3], // 前侧面 [1, 2, 3] // 顶面 ]; const convexShape = new CANNON.ConvexPolyhedron(vertices, faces); const convexBody = new CANNON.Body({ mass: 1 }); convexBody.addShape(convexShape); // Trimesh 示例 - 基于立方体顶点和面创建 const cubeVerts = [ -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1 ]; const cubeFaces = [ 0, 1, 2, 0, 2, 3, // 前 1, 5, 6, 1, 6, 2, // 右 5, 4, 7, 5, 7, 6, // 后 4, 0, 3, 4, 3, 7, // 左 3, 2, 6, 3, 6, 7, // 上 4, 5, 1, 4, 1, 0 // 下 ]; const trimeshShape = new CANNON.Trimesh(cubeVerts, cubeFaces); const trimeshBody = new CANNON.Body({ mass: 0 }); // 静态物体 trimeshBody.addShape(trimeshShape); ``` ### **2. face参数的理解** #### **ConvexPolyhedron的face参数** 是一个二维数组,每个子数组代表一个**凸多边形面**,通过顶点索引按**逆时针顺序**排列定义面的朝向(法线方向由右手定则确定)。例如: ```javascript const vertices = [ /* 8个顶点 */ ]; const faces = [ [0, 1, 2, 3], // 前表面(四边形) [4, 5, 6, 7], // 后表面 // ...其他面 ]; ``` - **特点**: - 面可以是任意凸多边形(三角形、四边形等)。 - 引擎会自动计算面的法线和边界平面。 - 所有面共同围成一个封闭的凸空间。 #### **Trimesh的cubeFaces参数** 是一个一维数组,每**3个索引**强制组成一个**三角形**,无论原始模型的面是几边形都需拆分为三角形。例如: ```javascript const cubeFaces = [ 0, 1, 2, 0, 2, 3, // 前表面(拆分为2个三角形) 1, 5, 6, 1, 6, 2, // 右表面 // ...其他面(每个面2个三角形,共12个三角形) ]; ``` - **特点**: - 必须手动将所有面拆分为三角形。 - 顶点索引顺序决定三角形法线方向(逆时针为正面)。 - 不要求整体形状是凸的,但用于动态物体时物理模拟可能不稳定。 ### **3. 选择建议** 1. **优先使用ConvexPolyhedron**: - 当物体是凸形状时(如箱子、石头、金字塔)。 - 需要精确的物理模拟(如滚动、滑动)。 - 性能敏感的场景(凸形状检测更快)。 2. **使用Trimesh的场景**: - 表示复杂或凹形状(如地形、建筑)。 - 静态碰撞体(如地面、墙壁)。 - 模型已预先三角化(如从3D软件导出的网格)。 3. **避免的陷阱**: - 不要用Trimesh创建动态物体(会导致物理不稳定)。 - 对复杂凸形状使用ConvexPolyhedron时,手动计算顶点和面可能很繁琐,可借助工具生成。 ### **4. 示例对比** #### **ConvexPolyhedron创建四面体** 例: ```javascript const vertices = [ new CANNON.Vec3(0, 0, 0), // 顶点0 new CANNON.Vec3(1, 0, 0), // 顶点1 new CANNON.Vec3(0, 1, 0), // 顶点2 new CANNON.Vec3(0, 0, 1) // 顶点3 ]; const faces = [ [0, 1, 2], // 底面(三角形) [0, 1, 3], // 右侧面 [0, 2, 3], // 前侧面 [1, 2, 3] // 顶面 ]; const convexShape = new CANNON.ConvexPolyhedron(vertices, faces); ``` #### **Trimesh创建立方体** 下面从多个角度详细解释这个参数: #### 1. **基本概念** - **Trimesh(三角网格)**:由多个三角形面组成的网格,可表示任意 3D 形状。 - **cubeFaces**:是一个一维数组,将立方体的每个面拆分为多个三角形,每个三角形由 **3 个顶点索引**表示。 #### 2. **立方体的结构** 一个立方体有: - **8 个顶点**(每个顶点有 x、y、z 坐标)。 - **6 个面**(每个面是一个四边形,需拆分为 2 个三角形)。 - **12 个三角形**(每个面由 2 个三角形组成)。 #### 3. **cubeFaces 参数详解** 以 Cannon.js 文档中的示例为例: ```javascript const cubeFaces = [ 0, 1, 2, 0, 2, 3, // 前面(2个三角形) 1, 5, 6, 1, 6, 2, // 右面 5, 4, 7, 5, 7, 6, // 后面 4, 0, 3, 4, 3, 7, // 左面 3, 2, 6, 3, 6, 7, // 上面 4, 5, 1, 4, 1, 0 // 下面 ]; ``` ##### **解析**: - **每 3 个索引表示一个三角形**:例如 `[0, 1, 2]` 表示由顶点 0、1、2 组成的三角形。 - **每个面由 2 个三角形组成**:例如前面由 `[0, 1, 2]` 和 `[0, 2, 3]` 组成。 - **顶点索引顺序**:按 **逆时针方向排列**,确保法线方向正确(右手定则)。 #### 4. **顶点索引与空间位置的对应关系** 假设立方体的 8 个顶点(`cubeVerts`)按以下方式定义: ```javascript const cubeVerts = [ -1, -1, -1, // 0: 左下后 1, -1, -1, // 1: 右下后 1, 1, -1, // 2: 右上后 -1, 1, -1, // 3: 左上后 -1, -1, 1, // 4: 左下前 1, -1, 1, // 5: 右下前 1, 1, 1, // 6: 右上前 -1, 1, 1 // 7: 左上前 ]; ``` ##### **可视化立方体的面**: ``` 7-------6 /| /| 4-------5 | | | | | | 3-----|-2 |/ |/ 0-------1 ``` #### 5. **为什么需要拆分为三角形?** - **简化碰撞检测**:三角形是最基本的多边形,物理引擎可以高效处理。 - **灵活性**:任何复杂的 3D 模型都可以分解为三角形网格。 #### 6. **常见问题** 1. **顶点索引越界**:确保所有索引值在 `cubeVerts` 的有效范围内(0~7)。 2. **法线方向错误**:若三角形的顶点顺序为顺时针,法线会朝向相反方向,导致碰撞检测异常。 3. **重复顶点**:Trimesh 允许顶点共享(如示例中的顶点 0 被多个面使用),减少内存占用。 #### 7. **示例:手动构建一个面** 以立方体的前面(z=1)为例: - 顶点:左下(4)、右下(5)、右上(6)、左上(7)。 - 拆分为两个三角形: 1. 三角形 1:顶点 4 → 5 → 6(逆时针)。 2. 三角形 2:顶点 4 → 6 → 7(逆时针)。 - 对应 `cubeFaces` 中的索引:`[4, 5, 6, 4, 6, 7]`。 #### 注意 `cubeFaces` 参数通过顶点索引定义了立方体的三角形面结构,每个面被拆分为两个三角形,确保物理引擎能够正确计算碰撞。理解顶点顺序和三角形拆分方式是使用 Trimesh 的关键。 ### **总结** - **ConvexPolyhedron**适合凸形状的动态物体,通过顶点和面定义,支持复杂物理行为。 - **Trimesh**适合任意形状的静态碰撞体,通过顶点和三角形索引定义,灵活性高但性能较低。 - **face参数**的组织方式直接反映了两种类型的差异:ConvexPolyhedron按面组织,Trimesh按三角形组织。