本文不会详述刚体运动学的公式推导。主要对旋转相关的几个关键物理量,以及相互之间的联系做一个简单的记录。
(1)使用转动惯量、力矩,计算角加速度
在系列文章1中已经简单介绍了转动惯量。这里贴上 games104 的图方便对照记忆。
games104.png
unity 的 rigidbody. inertiaTensor 对应 physx actor 的 getMassSpaceInertiaTensor 返回值。 rigidbody. inertiaTensorRotation 对应 actor.getCMassLocalPose().q 。返回质心的旋转四元数。
参考physx api - rigidbody
对于施加力矩,unity 中有两个函数:AddTorque( Vector3 torque ),AddRelativeTorque( Vector3 torque )。前者输入的是 world space 的力矩,后者输入的是 local space 的力矩。
Vector3 torque_local = transform.InverseTransformDirection ( torque_world )
简单插入一下关于 InverseTransformPoint, InverseTransformVector,
InverseTransformDirection 的区别。
InverseTransformPoint 的实现:
Vector3 pos_world;
var mat_ltw = Matrix4x4.TRS(transform.localPosition, transform.localRotation, transform.localScale);
Vector4 pos_local = mat_ltw * new Vector4(pos_world.x, pos_world.y, pos_world.z, 1)
//注意这里一定要把 pos_world 转成 Vector4 齐次坐标,否则就会丢失 translation
InverseTransformVector 不包含 translation,只包含 rotation, scale。
InverseTransformDirection 只包含 rotation。
那么,如何使用转动惯量,直接计算出角加速度,来实现 AddTorque 同样的效果呢。从公式我们知道,角加速度 。实际实现时,需要注意 unity 的 inertiaTensor 是 local space 的,所以在套用公式前后都需要进行坐标系转换。
Vector3 I = rgd.inertiaTensor;
Quaternion Ir = rgd.inertiaTensorRotation;
Vector3 cent_w = rgd.worldCenterOfMass;
Vector3 torque = Vector3.Cross(pos - cent_w, force);
Vector3 torque_local = transform.InverseTransformDirection(torque);
float[][] mat = new float[][] {
new float[]{I.x, 0, 0},
new float[]{ 0, I.y, 0},
new float[]{0, 0, I.z}};
Matrix Im = new Matrix(mat); // Matrix 是通用矩阵类
Matrix Irm = new Matrix(Ir);
Matrix It = Irm * Im * Irm.Transpose();
Vector3 wa_local = (It.Inverse() * torque_local).ToVec3();
Vector3 wa = transform.TransformDirection(wa_local);
rgd.angularVelocity = rgd.angularVelocity + wa * Time.deltaTime;
// * deltaTime 后等价于 AddTorque 使用 ForceMode.Force