Rigid Body Dynamics
在本章中,我们将介绍一些主题,一旦您熟悉了设置基本的刚体模拟世界,这些主题也很重要。
Velocity
刚体的运动分为线性和角速度(linear and angular velocity)分量。在仿真过程中, PhysX
将根据重力(gravity)、其他施加的力(other applied forces)和扭矩(torques)以及各种约束(如碰撞或joint)来修改物体的速度。
可以使用以下方法读取物体的线性和角速度:
PxVec3 PxRigidBody::getLinearVelocity();
PxVec3 PxRigidBody::getAngularVelocity();
可以使用以下方法设置物体的线性和角速度:
void PxRigidBody::setLinearVelocity(const PxVec3& linVel, bool autowake);
void PxRigidBody::setAngularVelocity(const PxVec3& angVel, bool autowake);
Mass Properties
dynamic actor
需要质量属性,包括:质量(mass)、惯性矩(moment of inertia)和质量frame的中心(the center of mass frame),center of mass frame
指定Actor
的质心位置及其主惯性轴(principal inertia axes)。计算质量属性的最简单方法是使用 PxRigidBodyExt::updateMassAndInertia()
帮助程序函数,该函数将根据Actor
的Shape
和均匀的密度值设置所有三个属性。此功能的变体允许组合每个Shape
的密度和手动指定某些质量属性。有关更多详细信息,请参阅 PxRigidBodyExt
的参考。
North Pole Sample
中摇摇晃晃的雪人说明了不同质量属性的使用。雪人就像罗利聚乙烯玩具,通常只是一个空壳,底部装满了一些沉重的材料。低质心导致它们在倾斜后移回直立位置。它们有不同的风格,具体取决于质量属性的设置方式:
-
第一种基本上是无质量的。在
Actor
的底部只有一个质量相对较高的小球体。由于产生的惯性矩很小,这导致相当快速的运动。雪人感觉很轻。 -
第二个仅使用底部雪球的质量,导致更大的惯性。后来,质心被移动到
Actor
的底部。这种近似在物理上绝是不正确的,但由此产生的雪人感觉更饱满一些。 -
第三个和第四个雪人使用
Shape
来计算质量。不同之处在于,人们首先计算惯性矩(从实际质心),然后将质心移动到底部。另一个计算我们传递给计算例程的低质心的惯性矩。请注意第二种情况的摆动速度要慢得多,尽管两者的质量相同。这是因为头部在惯性矩(与质心的距离平方)中占了更多。 -
最后一个雪人的质量属性是手动设置的。该示例使用惯性矩的粗略值来创建特定的所需行为。对角线张量在 X 中具有低值,在 Y 和 Z 中具有高值,从而产生围绕 X 轴旋转的低阻力和围绕 Y 和 Z 的高阻力。因此,雪人只会围绕X轴来回摆动。
如果您有一个 3x3 惯性矩阵(例如,您的对象有现实中的惯性张量),请使用 PxDiagonalize()
函数获取主轴和对角惯性张量来初始化 PxRigidDynamic actor
。
当手动设置物体的质量/惯性张量时,PhysX
需要质量和每个主惯性轴的正值。但是,在这些值中提供 0 是合法的。当提供0质量或惯性值时, PhysX
将其解释为表示围绕该主轴的无限质量或惯性。这可用于创建抵抗所有线性运动或抵抗所有或某些角度运动的物体。使用此方法可以实现的效果示例如下:
- Bodies that behave as if they were kinematic.
- Bodies whose translation behaves kinematically but whose rotation is dynamic.
- Bodies whose translation is dynamic but whose rotation is kinematic.
- Bodies which can only rotate around a specific axis.
下面详细介绍了可以取得的成就的一些例子。首先,让我们假设我们正在创建一个共同的结构 - 一个风车。下面提供了构建将成为风车一部分的body的代码:
PxRigidDynamic* dyn = physics.createRigidDynamic(PxTransform(PxVec3(0.f, 2.5f, 0.f)));
PxRigidActorExt::createExclusiveShape(*dyn, PxBoxGeometry(2.f, 0.2f, 0.1f), material);
PxRigidActorExt::createExclusiveShape(*dyn, PxBoxGeometry(0.2f, 2.f, 0.1f), material);
dyn->setActorFlag(PxActorFlag::eDISABLE_GRAVITY, true);
dyn->setAngularVelocity(PxVec3(0.f, 0.f, 5.f));
dyn->setAngularDamping(0.f);
PxRigidStatic* st = mPhysics.createRigidStatic(PxTransform(PxVec3(0.f, 1.5f, -1.f)));
PxRigidActorExt::createExclusiveShape(*t, PxBoxGeometry(0.5f, 1.5f, 0.8f), material);
scene.addActor(dyn);
scene.addActor(st);
上面的代码为风车创建了一个静态箱形框架,并创建了一个十字形来表示风车的叶片。我们关闭风车叶片上的重力和角度阻尼,并赋予其初始角速度。因此,该风车叶片将无限期地以恒定的角速度旋转。但是,如果另一个物体与风车相撞,我们的风车将停止正常运行,因为风车叶片将被撞出原位。有几种选择可以使风车叶片在其他物体与之相互作用时保持在正确的位置。其中一种方法可能是使风车具有无限的质量和惯性。在这种情况下,与物体的任何相互作用都不会影响风车:
dyn->setMass(0.f);
dyn->setMassSpaceInertiaTensor(PxVec3(0.f));
此示例无限期地保留了风车以恒定角速度旋转的先前行为。然而,现在body
的速度不能受到任何约束的影响,因为body
有无限的质量和惯性。如果一个物体与风车叶片相撞,碰撞的行为就好像风车叶片是一个kinematic
物体。
另一种选择是使风车具有无限的质量,并将其旋转限制在body
的局部z轴周围。这将提供与在风车和静态风车框架之间应用旋转接头相同的效果:
dyn->setMass(0.f);
dyn->setMassSpaceInertiaTensor(PxVec3(0.f, 0.f, 10.f));
在这两个例子中,body
的质量都设置为0,表明body
具有无限的质量,因此其线速度不能被任何约束改变。但是,在此示例中,body 的惯性被配置为允许body 的角速度受围绕一个主轴或惯性的约束的影响。这提供了与引入旋转接头(a revolute joint)类似的效果。z轴周围的惯性值可以增加或减少,以使风车对运动的抵抗力更大/更小。
Applying Forces and Torques
与物体相互作用的最物理友好的方式是向它施加力。在经典力学中,物体之间的大多数相互作用通常通过使用力来解决。由于定理:
$ f = m * a(force = mass * acceleration) $
力直接控制物体的加速度,但其速度和位置只是间接的。因此,如果您需要立即响应,通过强制控制可能会不方便。力的优点是,无论您对场景中的实体施加何种力,模拟都能够保持所有定义的约束(joint和接触)得到满足。例如,重力的工作原理是对物体施加力。
不幸的是,以系统的共振频率对铰接体(articulated bodies)施加大的力可能会导致速度不断增加,并最终导致求解器无法维持joint约束。这与现实世界的系统没有什么不同,在现实世界中,joint最终会断裂。
作用在body上的力在每个模拟帧之前累积,施加到模拟中,然后重置为零,为下一帧做准备。下面列出了 PxRigidBody
和 PxRigidBodyExt
的相关方法。有关更多详细信息,请参阅 API 参考:
void PxRigidBody::addForce(const PxVec3& force, PxForceMode::Enum mode, bool autowake);
void PxRigidBody::addTorque(const PxVec3& torque, PxForceMode::Enum mode, bool autowake);
void PxRigidBodyExt::addForceAtPos(PxRigidBody& body, const PxVec3& force,
const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addForceAtLocalPos(PxRigidBody& body, const PxVec3& force,
const PxVec3& pos, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addLocalForceAtPos(PxRigidBody& body, const PxVec3& force,
const PxVec3& pos, PxForceM