刚体状态
在物理引擎中,一个刚体是模拟的基本单元。为了完整地描述一个刚体在任何时刻的物理状态,并为其运动演化提供必要的数据,我们需要一个精心设计的数据结构。这个数据结构不仅要包含物体的当前位置和朝向,还要存储其速度、质量属性、受力情况以及与其他物体交互所需的参数。
静态属性
这些属性在物体被创建后通常不会改变。它们定义了物体的内在物理特性。
- 质量 (Mass)
m: 一个标量,描述物体抵抗线性加速度的程度。 - 逆质量 (Inverse Mass)
1/m: 在计算中,我们更常用到质量的倒数。这样做可以将除法运算转换成乘法运算,效率更高。对于质量无穷大的静态物体(如地面),其逆质量为0。这是一种优雅的处理方式,使得求解器无需为静态物体编写特殊代码——施加在它们身上的冲量乘以0后,速度变化自然也为0。 - 惯性张量 (Inertia Tensor)
I_body: 一个 3x3 矩阵,在物体的局部坐标系(body space)中定义,描述物体抵抗角加速度的程度。 - 逆惯性张量 (Inverse Inertia Tensor)
I_body⁻¹: 同样,我们预先计算并存储惯性张量的逆,以提高计算效率。对于不希望旋转的物体,可以将其逆惯性张量设为零矩阵。 - 碰撞形状 (Collision Shape): 指向物体碰撞几何体(如球体、盒子、凸包)的指针或引用。这是碰撞检测系统需要的核心数据。
- 物理材质 (Material): 包含摩擦系数(friction)和恢复系数(restitution, a.k.a. bounciness)等参数。
动态状态 (Dynamic State)
这些是描述物体当前运动状态的变量,它们在每个模拟时间步都会被积分器更新。
- 位置 (Position)
x: 一个 3D 向量,描述物体质心在世界空间中的位置。 - 朝向 (Orientation)
q: 一个单位四元数,描述物体从其局部坐标系到世界坐标系的旋转。 - 线速度 (Linear Velocity) : 一个 3D 向量,描述质心的速度。
- 角速度 (Angular Velocity) : 一个 3D 向量,描述物体围绕其质心的角速度。
派生数据 (Derived Data)
这些数据可以由上述核心状态计算得出,但为了性能考虑,可能会在每帧开始时计算并缓存起来。
-
世界空间逆惯性张量 (World Inverse Inertia Tensor)
I_world⁻¹: 其中 是由朝向四元数 导出的旋转矩阵。这个矩阵在求解器计算角加速度时频繁使用。 -
变换矩阵 (Transform Matrix): 一个 4x4 矩阵,结合了位置和朝向,用于将顶点从局部空间变换到世界空间,主要供渲染系统使用。
临时变量 (Temporary Variables)
这些变量用于在单个时间步内累积结果。
- 总受力 (Total Force)
F: 在施加力阶段累积的所有外力(如重力、玩家施加的力)的合力。 - 总力矩 (Total Torque)
τ: 累积的所有外力矩的合力。
在每个积分步骤结束时,这些累加器都需要被清零。
数据结构示例
一个典型的刚体数据结构可能如下所示:
struct RigidBody {
// --- 静态属性 ---
float mass; // 质量
float inverseMass; // 逆质量
Matrix3x3 inertiaTensorBody; // 局部坐标系下的惯性张量
Matrix3x3 inverseInertiaTensorBody; // 局部坐标系下的逆惯性张量
CollisionShape* shape; // 指向碰撞形状
Material material; // 物理材质 (摩擦, 恢复系数)
// --- 动态状态 ---
Vector3 position; // 位置
Quaternion orientation; // 朝向
Vector3 linearVelocity; // 线速度
Vector3 angularVelocity; // 角速度
// --- 派生数据 (可缓存) ---
Matrix3x3 inverseInertiaTensorWorld; // 世界坐标系下的逆惯性张量
Matrix4x4 transformMatrix; // 变换矩阵
// --- 临时变量 ---
Vector3 force; // 总受力
Vector3 torque; // 总力矩
// --- 标志位 ---
bool isStatic; // 是否为静态物体
bool isAwake; // 是否处于激活状态
};
在面向数据的设计中,我们不会创建这样一个大的结构体数组。相反,我们会为每个成员创建一个独立的数组:
struct RigidBodySystem {
std::vector<float> masses;
std::vector<float> inverseMasses;
// ...
std::vector<Vector3> positions;
std::vector<Quaternion> orientations;
// ...
};
虽然在概念上我们可以将所有数据封装在一个 RigidBody 类中,但在追求极致性能的现代引擎中,采用面向数据的设计,将状态分解为多个并行的数组,是最大化缓存利用率和释放SIMD潜力的关键。