以下为个人学习笔记整理。参考 PhysX SDK 3.4.0 文档
# GeometryQuery
几何查询主要用来做物体间的碰撞检测,有以下四种形式:
- raycasts:射线检测。
- sweeps:扫描检测。沿一条线移动一个几何对象以找到与另一个几何对象的第一个交点
- overlaps:重叠检测。确定两个几何对象是否相交
- penetration depth:穿透检测,计算相撞物体的撞击点和距离。
# 射线检测 ——raycast
通过某坐标点 + 方向向量 + 查询距离
/** | |
\param [in] origin 射线起点 | |
\param [in] unitDir 射线方向 | |
\param [in] geom 被检测对象几何信息 | |
\param [in] pose 被检测对象位置(世界坐标) | |
\param [in] maxDist 射线长度 [0, inf), 0 表示检测是否重叠 | |
\param [in] hitFlags 标记命中需要带回的返回结果 | |
\param [in] maxHits 记录命中结果最大数量 | |
\param [out] rayHits 命中信息 | |
\return 命中数量 | |
*/ | |
PX_PHYSX_COMMON_API static PxU32 raycast(const PxVec3& origin, | |
const PxVec3& unitDir, | |
const PxGeometry& geom, | |
const PxTransform& pose, | |
PxReal maxDist, | |
PxHitFlags hitFlags, | |
PxU32 maxHits, | |
PxRaycastHit* PX_RESTRICT rayHits); | |
// example | |
PxRaycastHit hitInfo; | |
PxU32 maxHits = 1; | |
PxHitFlags hitFlags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eDISTANCE|PxHitFlag::eUV; | |
PxU32 hitCount = PxGeometryQuery::raycast(origin, unitDir, | |
geom, pose, | |
maxDist, | |
hitFlags, | |
maxHits, &hitInfo); |
# 特殊查询情况 —— 如果射线起点在几何内部
# Solid Object
内部视为实心的几何体(球体、胶囊、盒子、凸面),最多得到一个结果
- 命中点:射线起点
- 法线:射线反方向
- 距离:0
# 特殊查询情况 —— 特殊几何
# 网格类
支持多种命中模式:
- 多次命中:返回全部命中点
- 最近命中:返回距离射线最近的命中点
- 任意命中:随机返回任意的命中点
网格的命中判定会被面剔除规则影响。
- 如果设置了 PxMeshGeometryFlag::eDOUBLE_SIDED 或 PxHitFlag::eMESH_BOTH_SIDES,则禁用剔除。
- 如果设置了 PxMeshGeometryFlag::eDOUBLE_SIDED,则反面命中的法线会取反。
# 高度场
「thickness」表示距离地面以下多少的深度,因此 「thickness」 >0
表示 -y
方向深度值。
当「thickness」 <=0 时,法线方向为 +y
,「thickness」 >0 时,法线方向为 -y
# Solid Object
最多得到一个命中结果
# Plane
3D 空间下的 「2D 几何」,最多得到一个命中结果,如果起点或终点和平面很接近,有可能不会视为命中
# 特殊返回结果 —— 网格类
网格和高度厂这种由三角形组成的网格几何可以获取到交点坐标的 UV 重心表示法,参考重心坐标
其中 v0、v1 和 v2 是命中三角形的顶点,详细实现见 PxTriangle::pointFromUV
:
# 扫描检测 ——sweeps
一般用于检测一个物体运动过程中和其他物体的碰撞
/** | |
\param [in] unitDir 扫描 | 运动方向 | |
\param [in] distance 扫描距离 | |
\param [in] geom0 扫描的几何体 | |
\param [in] pose0 扫描几何体的坐标 | |
\param [in] geom1 被扫描几何体 | |
\param [in] pose1 被扫描几何体的坐标 | |
\param [out] sweepHit 命中信息 | |
\param [in] hitFlags 标记命中所需返回的信息 | |
\param [in] inflation 膨胀大小,扩大检测范围的参数,让几何看起来更圆 | |
\return 命中数量 | |
*/ | |
bool PxGeometryQuery::sweep(const PxVec3& unitDir, const PxReal distance, | |
const PxGeometry& geom0, const PxTransform& pose0, | |
const PxGeometry& geom1, const PxTransform& pose1, | |
PxSweepHit& sweepHit, PxHitFlags hitFlags, | |
const PxReal inflation) | |
// example | |
PxSweepHit hitInfo; | |
PxHitFlags hitFlags = PxHitFlag::ePOSITION|PxHitFlag::eNORMAL|PxHitFlag::eDISTANCE; | |
PxReal inflation = 0.0f; | |
PxU32 hitCount = PxGeometryQuery::sweep(unitDir, maxDist, | |
geomToSweep, poseToSweep, | |
geomSweptAgainst, poseSweptAgainst, | |
hitInfo, | |
hitFlags, | |
inflation); |
# 问题
- 如果两个几何的尺寸差异很大会导致结果的不正确。
- 由于扫描操作也能够判断初始状态的重叠情况,但是和重叠检测算法存在差异,两者结果可能完全不同。推荐先用重叠检查初始状态,再用扫描检查看深度。
# 重叠检测 ——overlaps
用于检查两个几何的重叠情况,其中一个必须是 Solid Object,另一个可以任意
特殊规则:
- Plane 被视作实心空间,其后方的区域也被视作体积的一部分。
- 网格的三角形被视为薄面片,不具备体积。
- 高度场被视为有「thickness」的三角形面片组成,因此带有体积
# 简单版本
只判断重叠,不返回额外信息
/** | |
\param [in] geom0 第一个几何结构 | |
\param [in] pose0 第一个几何位置 | |
\param [in] geom1 第二个几何结构 | |
\param [in] pose1 第二个几何位置 | |
\return True if the two geometry objects overlap | |
*/ | |
PX_PHYSX_COMMON_API static bool overlap(const PxGeometry& geom0, const PxTransform& pose0, | |
const PxGeometry& geom1, const PxTransform& pose1); | |
// example | |
bool isOverlapping = overlap(geom0, pose0, geom1, pose1); |
# 复杂版本
会获取到重叠的网格信息
/** | |
\param [in] geom 第一个几何结构 | |
\param [in] geomPose 第一个几何位置 | |
\param [in] meshGeom 第二个几何网格结构 | |
\param [in] meshPose 第二个几何位置 | |
\param [out] results 返回值,保留重叠三角形数据 | |
\param [in] maxResults 最大重叠查询个数 | |
\param [in] startIndex 超过阈值时正在查询的三角形编号,用于后续继续查询 | |
\param [out] overflow 是否超过阈值了 | |
\return Number of overlaps found, i.e. number of elements written to the results buffer | |
*/ | |
PX_PHYSX_COMMON_API static PxU32 findOverlapTriangleMesh( const PxGeometry& geom, const PxTransform& geomPose, | |
const PxTriangleMeshGeometry& meshGeom, const PxTransform& meshPose, | |
PxU32* results, PxU32 maxResults, PxU32 startIndex, bool& overflow); |
# GJK 算法精确物理碰撞检测
...
# 穿透检测 ——penetration depth
穿透检测的意义在于计算物体发生碰撞时的穿透深度(MTD),并得到分离穿透所需的方向和距离。
/** | |
\param [out] 对象 1 的平移方向 | |
\param [out] 平移距离 | |
\param [in] 第一个几何结构 | |
\param [in] 第一个几何位置 | |
\param [in] 第二个几何结构 | |
\param [in] 第二个几何位置 | |
\return 穿透返回 true | |
*/ | |
PX_PHYSX_COMMON_API static bool computePenetration(PxVec3& direction, PxF32& depth, | |
const PxGeometry& geom0, const PxTransform& pose0, | |
const PxGeometry& geom1, const PxTransform& pose1); |
对于网格和高度场,使用迭代算法计算深度,本质上就是多次调用 computePenetration
的结果并更新 geom0
的 pose0
直到返回值为 false。