本章涉及了了不少模拟刚体物体必须了解的点。
Applying Forces and Torques
物理学上和物体交互的方式常是对其施以外力。在经典力学中,很多物体之间的交互都是采用力来求解。因为力遵守如下法则:
f = m*a (force = mass * acceleration) 力= 质量*加速度 0-0
力直接控制物体的加速度,间接影响其位置和速度。因此,如果你需要立刻获得反应的话,使用力Force可能不太方便。力的好处是无需考虑场景中受力物体Body的情况,模拟程序会计算定义所有的约束条件。重力就是这么干的。
不幸的是,大量力会导致共振,使速度越来越大,最后系统solver因为无法maintain the joint constraints而崩溃。不像现实世界,joint break就是了。(joint的breakable打开也不行么?)
同一模拟帧内的力可以累加的,下一帧会全部置零。你可以设置力和力矩torque。
PxRigidBody和
PxRigidBodyExt中的相关函数如下.
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, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addLocalForceAtLocalPos(PxRigidBody & body, const PxVec3 & force, const PxVec3 & pos, PxForceMode::Enum mode, bool wakeup);
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, PxForceMode::Enum mode, bool wakeup);
void PxRigidBodyExt::addLocalForceAtLocalPos(PxRigidBody & body, const PxVec3 & force, const PxVec3 & pos, PxForceMode::Enum mode, bool wakeup);
PxForceMode 默认为
PxForceMode::eFORCE 。除此以外的可能值有:
PxForceMode::eIMPULSEs, impulsive force.
PxForceMode::eVELOCITY_CHANGE 会无视物体的质量...更多信息查看API手册中的PxForceMode章节.
Gravity
重力是最常用的。如果需要在整个场景内都使用重力效果,使用
PxScene类的
setGravity()进行设置。
使用米和秒为单位的话,重力值为9.8,方向为场景的竖直向下。
某些效果可能要求一些dynamic actor暂时不受重力影响(真想不出是啥时候啥情况),你可以这么干:
PxActor::setActorFlag(PxActorFlag::eDISABLE_GRAVITY,
true
);
Note: 改变重力效果的时候要严重当心。记得要手动恢复失重的物体,用
PxRigidDynamic::wakeUp()。因为系统如果要自动唤醒失重物体的话,势必遍历场景中的所有物体,性能不佳。.
Setting the Velocity
如果是想立刻让一个物体运动,给它初速度是最直接的。还有个更直接的办法是设动量momentum,如果你不知道物体的质量的话,这种方法可能更方便, 只是要知道速度的话还得要质量。
场景中设了初速度的物体, 你可能会遇到在第一帧结束以后速度就没了的情况。这是因为如果这个初速度和其他什么constraint冲突的话,就会被simulation以其他的设置重写。例如,如果一个球静止在桌子上,同时给了它一个向下的初速度,那么这个初速度就会,变成0. 如果给链条上某链节一个初速度,这个链节上的初速度会减小,其他链节的初速度会增大,以保证所有链节仍然连接在一起.
初速度的设置方法简单的说,如下:
void
PxRigidBody::setLinearVelocity(
const
PxVec3
&
linVel,
bool
autowake);
void PxRigidBody::setAngularVelocity( const PxVec3 & angVel, bool autowake);
void PxRigidBody::setAngularVelocity( const PxVec3 & angVel, bool autowake);
Kinematic Actors
dynamic Actor受力force影响,也受冲量impulse影响。通常俺们用
PxRigidBody::addForce()或
PxRigidBody::addTorque()函数的时候,用这俩枚举量
PxForceMode::eIMPULSE ,
PxForceMode::eFORCE 对其设置. 但是这些都是在以二手或者三手方式在控制actor,不够灵活。例如当想要移动舞台和角色的时候,直接改变它们的Actor的位置Position是最直接的。kinematic Acotor就提供了这样一种一手方式.
使用
PxRigidDynamic::moveKinematic()可以尽情操作kinematic Actor. 除了这个函数Kinematic Actor对不管是力,重力,碰撞啥的,都不起反应。这条命令先被暂存起来,当内部的simulate完成后,它会计算出一个速度,然后在本帧内以该速度向目标位置移动,本帧内的这一步移动完成后,速度会被归零。因此需要在反复调用该函数才能完成沿着某路径运动的效果。也就是在移动过程中,kinematic actor表现得像是质量为无穷大,因此总是马上停下来.
创建kinematic actor的方法只是在创建一个普通dynamic actor之后打开它的kinematic flag:
PxRigidDynamic
*
rigidDynamic;
rigidDynamic -> setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC, true );
rigidDynamic -> setRigidDynamicFlag(PxRigidDynamicFlag::eKINEMATIC, true );
只要把这个kinematic flag关闭,actor就又变成一个普通的dynamic actor. 如果你向一堆dynamic中的一个kinematic增加mass时,其实mass并没有增加,因为这个mass被用在了kinematic上,相当于沧海一粟,这一堆dynamic的总mass也不会变化。
附加说明:
- 注意 PxRigidDynamic::moveKinematic() 和 PxRigidBody::setGlobalPose() 的区别. setGlobalPose()也会将Actor移动到指定位置,但不会引起其他对象的变化。尤其是,它根本不会阻挡别的物体运动,而是会穿墙而过。但这个setGlobalPose()还是会被用到, 如果仅仅是想把某个东西移动到某个位置时。.
- kinematic actor是会推开别人的,如果有人挡道的话,但是其他物体不会反作用于kinematic物体。也就是说kinematic actor的出现,让其他dynamic actor表现为static actor或者kinematic actor. 譬如,一个dynamic豆腐会把冲过来的kinematic脑袋撞个坑?。
- kinematic 和 static actor之间木有相互作用和碰撞。
Sleeping
actor若持续一段时间木有动作, 可以看做将来它也不会再动作,除非再受到了外力。因此在下一次受到外力之前,它都不必参与simulate.这种状态就是Sleeping. 查询一个物体是不是睡了可以酱:
bool
PxRigidDynamic::isSleeping();
actor的动能kinematic energy低于某个值的话就会进入睡眠状态。这个门槛值可这样设置:
void
PxRigidDynamic::setSleepEnergyThreshold(PxReal threshold);
PxReal PxRigidDynamic::getSleepEnergyThreshold() const ;
PxReal PxRigidDynamic::getSleepEnergyThreshold() const ;
一个沉睡物体的醒来是因为有其他醒着的物体touch了它,或者它的什么属性发生了变化,睡还是没睡通常不应该是应用程序关心的问题,也不必。但如果非要人肉改变睡还是不睡,也不是没有办法:
void
PxRigidDynamic::wakeUp(PxReal wakeCounterValue
=
PX_SLEEP_INTERVAL);
void PxRigidDynamic::putToSleep();
void PxRigidDynamic::putToSleep();
但是,也有一些特殊情况要求用户必须手动唤醒Sleeping actor。哪些函数有这样的要求,API参考文档有详情。
Solver Accuracy
求解器精确度。当刚体rigid body的运动受到关节jount或和其他物体的接触contract的约束时,约束求解器constraint solver就会自动运行。求解器为了满足所有的约束条件,会有限次遍历运动物体上的所有约束条件。这次数越大,求解的结果越精确越接近现实。默认是4个位置和1个速度的遍历(?!?!)。The solver iteration count defaults to 4 position iterations and 1 velocity iteration. 可以单独设置每个刚体物体的遍历次数:
void
PxRigidDynamic::setSolverIterationCounts(PxU32 minPositionIters, PxU32 minVelocityIters);
眼目前俺们只发现了在一个关联了太多Joint并且这些joint的tolerance都很小的物体上才有必要把这个值设的比较大。如果该值需要设置到为30以上才能满足需求,基本上是整个模拟从根子上就有问题。
Fast Rotation
像铅笔这样的长条形状的物体模拟起来比较有难度,因为它们绕短轴旋转时会存储大量能量,而后若绕长轴旋转会变成一个很大的角速度。这样会出问题,因为旋转运动中的某些线性逼近米有办法完成。因此PhysX SDK中限制了角速度的最大值。当然这个值也是可以修改的。
void
PxRigidDynamic::setMaxAngularVelocity(PxReal maxAngVel);