概述
PhysX目前是由 NVIDIA 开源的一个3D物理引擎,由于其功能强大,目前已经继承在了各大商业游戏引擎中,例如 Unreal, Unity等。
基本概念
PxScene
场景对象,同一个场景中的物体才有可能进行物理碰撞等,因此场景是一个场景物体的管理者。
PxRigidActor
刚体对象(Rigidbody)的封装
PxShape
几何体的封装, 每个 PxRigidActor 可以绑定多个 PxShape 对象,一般是通过 PxRigidActor 来创建 PxShape.
PxControllerManager
CharacterControllerManager,用于创建 CharacterController。
CharactorController
上层封装,其实就是一个 PxRigidStatic 和 一个 PxShape 的组合体。
碰撞过滤器 ReportFilterShader
ReportFilterShader用于指定哪些 intersection pairs 需要被处理,因为这个在物理引擎底层需要被频繁调用,因此性能至关重要,因此提出了 FilterShader 这样的概念,这 filter 的工作在 GPU 上也能运行,即使在 CPU 上运行也能并行。例子如下:
PxFilterFlags contactReportFilterShader(PxFilterObjectAttributes attributes0,
PxFilterData filterData0,
PxFilterObjectAttributes attributes1,
PxFilterData filterData1,
PxPairFlags& pairFlags,
const void* constantBlock,
PxU32 constantBlockSize)
{
if(PxFilterObjectIsTrigger(attributes0) || PxFilterObjectIsTrigger(attributes1))
{
pairFlags = PxPairFlag::eTRIGGER_DEFAULT;
return PxFilterFlag::eDEFAULT;
}
pairFlags = PxPairFlag::eCONTACT_DEFAULT;
// trigger the contact callback for pairs (A,B) where
// the filtermask of A contains the ID of B and vice versa.
// PX_INFO("contactReportFilterShader|%x|%x|%x|%x", filterData0.word0, filterData1.word1, filterData1.word0, filterData0.word1);
if((filterData0.word0 & filterData1.word1) && (filterData1.word0 & filterData0.word1))
{
pairFlags |= PxPairFlag::eNOTIFY_TOUCH_FOUND;
return PxFilterFlag::eDEFAULT;
}
return PxFilterFlag::eDEFAULT;
}
我们需要了解 ReportFilterShader 函数的参数含义以及返回值的含义:
先看看文档是怎么描述的:
The arguments of SampleSubmarineFilterShader() include PxFilterObjectAttributes and PxFilterData for the two objects, and a constant block of memory. Note that the pointers to the two objects are NOT passed, because those pointers refer to the computer's main memory, and that may, as we said, not be available to the shader, so the pointers would not be very useful, as dereferencing them would likely cause a crash. PxFilterObjectAttributes and PxFilterData are intended to contain all the useful information that one could quickly glean from the pointers. PxFilterObjectAttributes are 32 bits of data, that encode the type of object: For examplePxFilterObjectType::eRIGID_STATIC, ::eRIGID_DYNAMIC, or even ::ePARTICLE_SYSTEM. Additionally, it lets you find out if the object is kinematic, or a trigger.
Each PxShape and PxParticleBase object in PhysX has a member variable of type PxFilterData. This is 128 bits of user defined data that can be used to store application specific information related to collision filtering. This is the other variable that is passed to SampleSubmarineFilterShader() for each object.
There is also the constant block. This is a chunk of per-scene global information that the application can give to the shader to operate on. You will want to use this to encode rules about what to filter and what not.
Finally, SampleSubmarineFilterShader() also has a PxPairFlags parameter. This is an output, like the return value PxFilterFlags, though used slightly differently. PxFilterFlags tells the SDK if it should ignore the pair for good (eKILL), ignore the pair while it is overlapping, but ask again, when filtering related data changes for one of the objects (eSUPPRESS), or call the low performance but more flexible CPU callback if the shader cannot decide (eCALLBACK).
PxPairFlags specifies additional flags that stand for actions that the simulation should take in the future for this pair. For example,eNOTIFY_TOUCH_FOUND means notify the user when the pair really starts to touch, not just potentially.
简单解释一下:
- attributes0 和 filterData0 是 intersection pair 中 第一个 PxShape 的数据,前者表示 PxShape 的 Type(PxFilterObjectType::eRIGID_STATIC, ::eRIGID_DYNAMIC, or ::ePARTICLE_SYSTEM, 是否是 kinematic 或者是一个 trigger),后者是 PxShape 的 UserData,在创建 PxShape 的时候可以通过 PxShape::setSimulationFilterData 函数指定;
- attribute1 和 filterData1 是 intersection pair 中另一个 PxShape 的数据,含义类似;
- PxFilterFlags 返回值是告诉SDK,如何处理这一对物体的碰撞,是放弃这一次碰撞的处理(eKILL),还是暂不处理除非这一次碰撞中filterData被改变(eSUPRESS),还是报告需要回调函数(eCALLBACK,eNOTIFY)等。而pairFlags则是定义,如果需要回调函数的话,在什么情况下,报告回调函数,如碰撞被发现(eNOTIFY_TOUCH_FOUND), 碰撞结束两个物体已分离(eNOTIFY_TOUCH_LOST)等。
- constantBlock 和 constantBlockSize (This is a chunk of per-scene global information that the application can give to the shader to operate on. You will want to use this to encode rules about what to filter and what not.)
在创建场景的时候需要指定一个 FilterShader 用于解决后面的碰撞检测,这是在 PxSceneDesc 中指定的;然后在创建 PxShape 的时候需要指定 filterData,通过 PxShape::setSimulationFilterData 函数指定,其中的 word0 字段一般指定 layer,word1 字段指定和哪些 layer 之间会发生碰撞。
碰撞回调 PxSimulationEventCallback
场景中所有的碰撞都需要 developer 自己来进行处理,用于实现一个类继承 PxSimulationEventCallback,基类如下:
class PxSimulationEventCallback {
public:
void onConstraintBreak(PxConstraintInfo* constraints, PxU32 count) ;
void onWake(PxActor** actors, PxU32 count);
void onSleep(PxActor** actors, PxU32 count) ;
virtual void onTrigger(PxTriggerPair* pairs, PxU32 count);
void onContact(const PxContactPairHeader& pairHeader, const PxContactPair* pairs, PxU32 nbPairs) ;
};
一般而言只需要处理 OnTrigger 和 OnContact 方法。
CharacterController 碰撞回调
需要设置 PxCapsuleControllerDesc.reportCallback,其中需要实现:
void onShapeHit(const PxControllerShapeHit& hit);
void onControllerHit(const PxControllersHit& hit);
前者表示和其他 PxShape 的碰撞,后者表示和其他 CharacterController 的碰撞。
一般使用流程
- PxCreateFoundation 创建 PxFoundaPxPhysicsion 对象,全局唯一;
- PxCreatePhysics 创建 Physics对象,全局唯一;
- PxCreateCooking 创建 PxCooking 对象,全局唯一;
- PxSerialization::createSerializationRegistry 创建 PxSerializationRegistry 对象,全局唯一;
- 需要打开一个场景的时候,使用 Physics::CreateScene 创建一个场景,打开多个场景则需要创建多个 Scene 对象,设置好 FilterShader 和 SimulationCallback;
- 创建 Static 或者 Dynamic Actor 或者 CharacterController;
- Tick 中获取 Dynamin Actor 和 CharacterController的位置同步给逻辑层使用,同时在 SimulationCallback 中将碰撞信息通知逻辑层。