☰
PhysX 一帧模拟的真实工作:流程、作用与职责
2026/10/8 1:19:03 网站建设 项目流程

从应用层看,PhysX 一步模拟通常只有:

scene->simulate(dt);scene->fetchResults(true);

但内部完成的是一个完整的数据处理过程:

读取当前物理状态和游戏输入,发现物体之间的交互,将接触和关节转化为约束,求解新的运动状态,最后交付模拟结果与事件。

下面以PhysX 4.x/5.x 的 CPU 刚体模拟为主说明。不同版本、PGS/TGS 求解器、CCD 和 GPU 配置会改变执行细节。

以下步骤按照职责和数据依赖划分,不是某个版本严格串行的函数调用顺序。


一、先明确:“物理一步”不等于“渲染一帧”

渲染帧的时间通常不固定,物理模拟一般使用固定时间步:

constexprfloatphysicsDt=1.0f/60.0f;

因此,一个渲染帧中可能执行零次、一次或多次物理模拟。

本文中的“一帧模拟”,指的是:

从时刻 t 的物理状态,推进到时刻 t + dt 的物理状态

其输入和输出可以概括为:

输入: 当前位姿、速度 力、力矩、运动学目标 碰撞形状、材质、过滤规则 关节及驱动参数 时间步 dt 输出: 新的位姿、速度 更新后的接触与交互状态 睡眠、唤醒、碰撞、触发器等事件

二、整体流程图

游戏逻辑提交物理输入 │ ▼ ① 模拟准备与任务调度 │ ├──→ 运动学状态与外力相关准备 │ ▼ ② 更新形状边界 │ ▼ ③ 宽相检测 Broad Phase │ ▼ ④ 碰撞过滤与交互关系维护 │ ▼ ⑤ 窄相检测 Narrow Phase │ ▼ ⑥ 岛屿与活动对象管理 │ ▼ ⑦ 约束准备 │ ▼ ⑧ 约束求解 │ ▼ ⑨ 状态积分与必要的 CCD 处理 │ ▼ ⑩ 睡眠判定与内部结果整理 │ ▼ ⑪ fetchResults:完成结果获取与事件交付 │ ▼ 游戏引擎同步 Transform、消费事件、渲染插值

实际内部存在并行、分阶段执行和任务交织。例如岛屿管理不是只运行一次,TGS 的求解与状态推进也不是完全分离的。


三、逐步解析:每一步做什么、为什么需要它

第 0 步:游戏逻辑提交输入

作用

告诉物理系统:下一步有哪些外部变化需要考虑。

主要职责

  • 添加力、力矩或冲量;
  • 设置运动学刚体目标;
  • 创建、移除物理对象;
  • 修改碰撞规则;
  • 更新关节驱动目标;
  • 必要时唤醒刚体。
box->addForce(PxVec3(10.0f,0.0f,0.0f));platform->setKinematicTarget(PxTransform(PxVec3(0.0f,2.0f,0.0f)));

需要区分

动态刚体: 游戏提交力等输入,物理决定运动。 运动学刚体: 游戏指定运动目标,物理处理它对其他对象的影响。

直接修改动态刚体位姿,更接近重新放置或瞬移,不等同于施力运动。

这一阶段主要由游戏引擎的物理适配层负责,而不是 PhysX 自动生成玩法输入。


第 1 步:模拟准备与任务调度

作用

把外部 API 操作转化为本次模拟可以执行的内部工作。

主要职责

  • 检查时间步与场景调用状态;
  • 处理需要反映到模拟中的对象变化;
  • 准备本步使用的临时数据和内存;
  • 建立任务及依赖关系;
  • 向 CPU Dispatcher 提交任务。

调用入口:

scene->simulate(dt);

常见源码阅读方向:

NpScene ↓ Sc::Scene ↓ 底层碰撞与动力学任务

输出

可执行的模拟任务图 本步需要更新的对象与内部数据

simulate()返回时,模拟可能还未结束,也可能部分工作已经完成。它不是“返回即获得完整结果”的接口。


第 2 步:运动状态准备

这一职责可能分散在不同任务中,并非必然是一个独立阶段。

作用

计算本步运动所需的基础状态。

主要职责

对于动态刚体:

  • 考虑重力;
  • 考虑累计的外力和力矩;
  • 处理阻尼;
  • 应用相应的速度限制。

简化的线速度更新:

[
\mathbf{v}^{*}

\mathbf{v}_{t}
+
\left(\mathbf{g}+\frac{\mathbf{F}}{m}\right)\Delta t
]

这里的 (\mathbf{v}^{*}) 是尚未完成接触和关节约束修正的速度。

对于运动学刚体:

  • 根据当前位姿和运动目标准备运动状态;
  • 形成接触处理需要的相对运动信息。

输出

供碰撞响应和求解使用的运动数据

外力更新只描述“物体自由运动时会怎样”,不负责阻止穿墙或维持关节。


第 3 步:更新形状边界

作用

用便宜的空间表示包住复杂碰撞形状,为后续筛选提供依据。

主要职责

根据:

Actor 世界位姿 Shape 局部位姿 Geometry 几何描述

更新形状的世界空间边界,通常是 AABB:

Axis-Aligned Bounding Box 轴对齐包围盒

概念上:

旋转后的箱子 ↓ 包住它的世界空间 AABB

边界还可能根据接触裕量或特定运动处理进行扩展。

输出

新增、变化或需要移除的包围盒数据

引擎会利用状态变化信息,避免无差别地重算所有静止对象。

职责边界

这一阶段只回答:

“形状大致占据什么空间?”

不回答是否真正接触。


第 4 步:宽相检测 Broad Phase

作用

从大量形状中,快速筛选出可能发生碰撞的对象对。

如果有 (N) 个形状,直接两两检查需要:

[
\frac{N(N-1)}{2}
]

次配对。宽相检测的目的就是避免这种全量精确检测。

主要职责

  • 维护空间结构;
  • 处理包围盒变化;
  • 找到新增的候选重叠对;
  • 找到已经分离的候选对。

例如:

箱子 A 与地面:候选重叠仍然存在 箱子 A 与箱子 B:新增候选重叠 箱子 B 与墙壁:候选重叠消失

输出

可能发生交互的形状对及其变化

职责边界

宽相不计算:

  • 精确接触点;
  • 接触法线;
  • 碰撞冲量。

源码重点:

source/lowlevelaabb/

第 5 步:碰撞过滤与交互关系维护

作用

判断空间上接近的对象,是否应该交互,以及以什么方式交互。

主要职责

结合过滤数据、过滤 Shader/Callback 和对象配置,决定:

  • 忽略某对对象;
  • 抑制其碰撞处理;
  • 建立普通接触交互;
  • 建立触发器交互;
  • 是否请求接触通知;
  • 是否启用某些特殊接触处理。

例如:

子弹与发射者: 忽略。 箱子与地面: 生成接触并求解。 玩家与任务区域: 检测重叠,产生触发事件,不产生阻挡。

持续存在的交互还需要维护缓存和生命周期,而不是每一步全部重建。

输出

有效的接触交互 有效的触发器交互 后续处理所需的配对配置

检测到碰撞、产生物理响应、收到回调,是三个不同维度的行为。


第 6 步:窄相检测 Narrow Phase

作用

对有效候选对执行精确几何计算。

输入

两个形状的几何与位姿 接触生成参数 上一物理步保留的接触缓存

主要职责

根据几何组合选择算法:

球体 vs 球体 胶囊 vs 盒体 凸体 vs 凸体 凸体 vs 三角网格

生成或更新:

  • 接触位置;
  • 接触法线;
  • 分离距离;
  • 接触流形;
  • 后续材质处理等所需的附加信息。

输出

供约束准备使用的接触数据

两个重要细节

第一,接触不一定意味着已经穿透。

contactOffset、restOffset等参数会影响接触生成与期望间距。

第二,接触数据通常会被复用。

箱子稳定放在桌面时,相邻两步的接触变化很小。持久接触流形利用这种时间相干性,减少计算并改善稳定性。

职责边界

窄相只负责:

“接触发生在哪里、沿哪个方向、距离如何?”

它不负责最终把箱子弹开。

相关源码:

source/geomutils/ source/lowlevel/

第 7 步:岛屿管理与活动对象组织

作用

找出哪些动态对象相互影响,从而组织调度、唤醒与求解。

主要职责

将刚体视为节点,将接触或关节视为连接:

箱子 A —— 箱子 B —— 箱子 C 球体 D —— 球体 E

这两组对象可以形成不同的模拟岛屿。

岛屿管理用于:

  • 识别相互耦合的系统;
  • 组织独立工作;
  • 传播唤醒;
  • 协调睡眠;
  • 为求解准备活动对象集合。

静态地面不会仅因为同时支撑多堆箱子,就像动态连接那样把所有箱子耦合到一起。

输出

需要处理的活动刚体和约束集合 相关连通关系及睡眠、唤醒状态

注意:

一个岛屿 ≠ 固定占用一个线程

实际任务粒度由批处理与调度策略决定。


第 8 步:约束准备

作用

把几何意义上的接触和关节,转换成求解器能计算的数学约束。

主要职责

结合:

  • 质量与逆质量;
  • 惯性与逆惯性;
  • 接触点相对质心的位置;
  • 相对速度;
  • 摩擦和恢复参数;
  • 误差修正参数;
  • 关节限制及驱动配置;

构建约束数据。

接触转化成什么?

例如箱子接触地面:

法向约束: 限制继续向地面内部运动。 摩擦约束: 限制沿地面切向滑动。

关节转化成什么?

例如门轴:

限制不允许的平移和旋转 允许绕指定轴转动 根据配置加入限位和驱动

输出

求解器刚体数据 约束数据 批处理信息 必要的历史冲量信息

接触点是几何事实,约束是对运动的数学限制。两者不能混为一谈。


第 9 步:约束求解

作用

求出接触、摩擦和关节应该施加多大的冲量,使运动尽可能满足约束。

概念上:

[
\Delta \mathbf{v}

M{-1}JT\lambda
]

其中:

  • (M^{-1}):广义逆质量;
  • (J):约束雅可比;
  • (\lambda):约束冲量。

主要职责

  • 修正接触法向上的相对运动;
  • 计算摩擦响应;
  • 在满足条件时处理反弹;
  • 维持关节限制;
  • 求解驱动与限位;
  • 在相互耦合的约束间迭代传播修正。

例如:

修正底层箱子 ↓ 影响中层箱子 ↓ 影响顶层箱子 ↓ 继续迭代调整

PhysX 常见求解器包括 PGS 和 TGS。

输出

满足约束程度更高的速度和相关状态 求得的约束冲量

为什么只能“尽可能满足”?

实时模拟使用有限时间预算和有限迭代次数。因此:

  • 穿透误差不一定完全消失;
  • 关节可能存在偏差;
  • 不合理的质量比可能导致收敛困难。

源码重点:

source/lowleveldynamics/

第 10 步:状态积分与 CCD

作用

根据运动状态推进物体位置和旋转,并处理配置启用的连续碰撞问题。

状态积分

最简单的线性表达:

[
\mathbf{x}_{t+\Delta t}

\mathbf{x}_t+\mathbf{v}\Delta t
]

旋转则根据角速度更新。

实际实现中,求解、误差修正与积分可能交织,尤其不能把 TGS 简化为“求解完后只积分一次”。

CCD 的职责

高速物体可能出现:

本步开始:子弹在墙左侧 本步结束:子弹在墙右侧 两个离散位置都没与墙重叠

CCD 用于降低这种穿透风险。

但要区分:

  • Sweep-based CCD:包含沿运动路径的检测及相关响应;
  • Speculative CCD:通过预测性接触等方式处理。

CCD 不是所有对象都会执行的统一末尾步骤,也不是所有模式都有相同调用路径。

输出

本步更新后的刚体位姿与运动状态 必要的连续碰撞处理结果

第 11 步:睡眠判定与内部结果整理

作用

减少后续步骤中的无效计算,并整理本步结果。

主要职责

  • 更新睡眠相关计数与状态;
  • 判断稳定对象是否可休眠;
  • 整理刚体状态及约束结果;
  • 更新交互生命周期;
  • 准备需要交付的事件。

休眠对象通常仍保留在场景里,并不是被删除。它们可以继续被查询,并在满足条件时唤醒。

为什么重要?

一万个正在运动和碰撞的刚体

与:

一万个大部分已休眠的刚体

计算成本可能相差很大。

这也是为什么评估场景性能时,不能只看刚体总数,还要看活动刚体、接触和约束数量。


第 12 步:fetchResults()获取结果与事件

作用

完成模拟结果获取,让应用安全地进入后续处理。

scene->fetchResults(true);

其中true表示必要时等待模拟完成。

主要职责

  • 等待相关模拟任务完成;
  • 完成结果获取相关内部工作;
  • 在相应流程中交付常规模拟事件;
  • 完成本步调用周期。

随后应用可以读取:

PxTransform pose=body->getGlobalPose();PxVec3 velocity=body->getLinearVelocity();

常见事件

  • 接触开始或结束;
  • 配置请求的持续接触通知;
  • 触发器进入或离开;
  • 睡眠或唤醒;
  • 约束断裂。

不是所有回调都在fetchResults()内发生。接触修改等回调可能发生在模拟任务执行过程中,其线程和访问限制应单独遵守。

另外:

fetchResults(true)耗时长,不代表所有计算都在这个函数里发生;它可能主要是在等待未完成的模拟任务。


四、哪些工作不是 PhysX 这一步自动负责的?

模拟完成后,通常还需要游戏引擎执行:

读取物理结果 ↓ 更新 ECS / 游戏对象 Transform ↓ 消费碰撞与触发事件 ↓ 处理动画与物理控制权 ↓ 执行渲染插值

这些不是 PhysX 自动完成的业务工作。

同样:

scene->raycast(...);scene->sweep(...);scene->overlap(...);

是应用发起的场景查询,不是每次模拟必然执行的三个固定步骤。不过,查询加速结构的维护可能与场景更新流程相关。


五、职责速查表

阶段核心问题主要输出
输入提交游戏希望怎样影响物理世界?力、目标、对象变化
模拟准备本步执行哪些工作?任务图、内部准备数据
运动准备没有约束时会怎样运动?候选运动状态
边界更新形状大致占据哪里?世界空间边界
宽相检测哪些形状可能相交?候选对
过滤与交互哪些候选对应该处理?有效交互
窄相检测接触在哪里、方向是什么?接触数据
岛屿管理哪些对象相互影响?活动对象与连通关系
约束准备接触和关节如何限制运动?求解器约束
约束求解应施加多大冲量?修正后的运动状态
积分与 CCD下一时刻在哪里,是否越过障碍?新位姿及连续碰撞结果
睡眠与整理哪些对象可停止活跃计算?活动状态与结果数据
结果获取怎样将结果交付应用?可读取状态和事件

最核心的理解

PhysX 一步模拟不是简单的“碰撞检测后把物体推开”,而是:

先用空间算法找交互,再用几何算法生成接触,然后用约束求解统一处理接触、摩擦和关节,最后推进状态并交付结果。

源码阅读时,最重要的分界线就是:

碰撞检测:发生了什么接触? 约束准备:这个接触如何限制运动? 约束求解:需要多大的运动修正? 状态积分:下一时刻的物体在哪里?

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询