PhysX3 User Guide 04 - Rigid Body Dynamics

时间:2021-10-11 00:16:23
本章涉及了了不少模拟刚体物体必须了解的点。

  Applying Forces and Torques      
 
物理学上和物体交互的方式常是对其施以外力。在经典力学中,很多物体之间的交互都是采用力来求解。因为力遵守如下法则:

f = m*a (force = mass * acceleration) 力= 质量*加速度 0-0

 

力直接控制物体的加速度,间接影响其位置和速度。因此,如果你需要立刻获得反应的话,使用力Force可能不太方便。力的好处是无需考虑场景中受力物体Body的情况,模拟程序会计算定义所有的约束条件。重力就是这么干的。

不幸的是,大量力会导致共振,使速度越来越大,最后系统solver因为无法maintain the joint constraints而崩溃。不像现实世界,joint break就是了。(joint的breakable打开也不行么?)

同一模拟帧内的力可以累加的,下一帧会全部置零。你可以设置力和力矩torque。 PxRigidBodyPxRigidBodyExt中的相关函数如下. 

 

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);

 

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);

   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 );


只要把这个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 ;

一个沉睡物体的醒来是因为有其他醒着的物体touch了它,或者它的什么属性发生了变化,睡还是没睡通常不应该是应用程序关心的问题,也不必。但如果非要人肉改变睡还是不睡,也不是没有办法:

 

void  PxRigidDynamic::wakeUp(PxReal wakeCounterValue = PX_SLEEP_INTERVAL);
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);