第 22 章
物理的虚构边界
游戏物理引擎从来就不是为了给出正确答案而设计的。这个判断听起来像是一种指责,但它不是。它只是一个被二十年的产业实践反复验证的事实陈述。当id Software在1996年为Quake编写第一个专用物理子系统时,John Carmack关心的不是碰撞响应的数学严格性,而是火箭跳的视觉反馈是否足够即时、足够爽快。当Havok在2000年将物理中间件作为独立产品推出时,它的卖点不是仿真精度,而是让爆炸看起来更壮观、让布娃娃动画看起来更自然。
当NVIDIA在2008年收购Ageia并将PhysX整合进GPU加速管线时,它的演示场景是碎布飞扬、流体倾泻、粒子炸裂——每一个都是为了征服眼球而设计。这套逻辑运行得如此成功,以至于产业几乎忘记了它原本只是一个选择,而不是物理模拟的唯一可能。直到非游戏领域开始入侵引擎生态,这个被遗忘的选择才重新浮现为必须回答的问题。把时间拨回2018年。
当构建服务器在深夜轰鸣,逐行翻译着从节点图衍生出的数十万种着色器排列时,这座巴别塔的重量不是抽象的隐喻——它是电费账单、是发布延期的风险、是某个技术美术在移动端预览中看到的那片品红色。引擎纪元的下一个压力点,正在这些数字的堆积中酝酿成形。但在图形渲染的危机之外,另一个更隐蔽的断裂正在引擎的物理模拟系统中悄然发生。
它的起点不是某次GDC的主题演讲,不是某篇引爆社区的技术博客,而是一个与游戏开发几乎无关的安全补丁:2018年4月30日,7-Zip修复了其RAR档案提取模块中的一个任意代码执行漏洞,编号CVE-2018-10115。这个漏洞本身并不涉及物理引擎。但它揭示了一种更深层的技术焦虑:当软件模块被嵌入到远超其原始设计意图的系统时,那些曾经被视为可接受的误差就会转化为不可接受的风险。
7-Zip的解压模块原本服务于桌面用户的文件管理需求,当它被集成到构建管线、自动化测试框架和持续集成系统时,一个缓冲区溢出就不再只是用户的不便,而是整个生产链的安全缺口。同样的逻辑,正在物理引擎的领域以更缓慢、更静默的方式重演。
最先显露迹象的是自动驾驶仿真。2018年前后,一批自动驾驶公司开始认真评估将Unreal Engine用作仿真环境的可行性。他们的需求表面上看与游戏开发高度重叠:需要一个能够实时渲染复杂城市场景、支持物理碰撞检测、允许程序化生成交通流量的三维环境。Unreal Engine在这些方面拥有游戏产业积累的全部优势——成熟的渲染管线、PhysX驱动的刚体动力学、蓝图可视化脚本。但自动驾驶工程师很快发现了一个在游戏开发中从未被视为问题的问题:非确定性。同一场景在两次运行中产生了不同的物理结果。
输入条件完全一致——车辆初始位置、速度向量、障碍物坐标、路面摩擦系数——但碰撞发生的精确时刻、撞击角度的微小偏移、碎片飞散的具体轨迹,在不同运行之间存在可测量的差异。在游戏里,这种差异被帧率波动、玩家输入和网络延迟完全淹没,没有人会注意到一块虚拟玻璃的碎裂模式在上一次运行中旋转了零点三度。但在安全认证的逻辑中,这意味着仿真不可审计。
工程仿真的铁律是可复现性。每一步计算都必须能够被追溯、验证,并在相同输入条件下产生完全相同的输出。这不是技术偏好问题,而是法律和监管要求。如果一辆自动驾驶系统在仿真中通过了碰撞测试,安全工程师需要确保这个通过不是浮点舍入误差的偶然产物。他们需要能够指着日志说:在这个时间戳,这个碰撞点,这个力的向量,这个材料响应——所有数值都是确定的,可以在另一台机器上完全重现。这种需求与游戏物理的技术基础存在根本性冲突。
现代物理引擎的核心算法——碰撞检测的GJK算法、约束求解的迭代方法、刚体运动的数值积分——几乎全部建立在浮点运算之上。浮点数在表示实数时存在固有的舍入误差,这种误差在不同硬件架构、不同并行执行顺序下会产生微小但确实存在的差异。IEEE 754标准定义了浮点运算的基本规则,但标准允许在不同舍入模式下产生不同结果,而GPU厂商在追求性能的过程中对标准的实现各有取舍。
更致命的是并行性:现代物理引擎大量使用多线程加速,而浮点加法的结合律在并行环境下不再成立——a加b再加c的结果可能因为计算顺序不同而产生不同的舍入结果,因为中间值的精度取决于运算次序,而运算次序取决于线程调度,线程调度取决于操作系统的负载状态。在游戏物理中,这个问题被一个简单的设计原则化解:永远不要依赖物理结果的精确数值。游戏逻辑通过碰撞事件和触发器来响应物理交互,这些事件本身是二值的——碰到了还是没碰到,进入了还是离开了。
至于碰撞发生的精确位置偏移了零点零一毫米,游戏设计者不会基于这个数值做判断。但自动驾驶仿真的需求恰好相反:碰撞的精确位置、时刻和力学参数正是需要被审计的核心数据。
NVIDIA最先公开回应了这一需求。作为PhysX的拥有者和自动驾驶仿真平台Drive Sim的构建者,NVIDIA处于一个独特的位置:它同时服务于游戏产业和汽车产业,而这两个产业对物理引擎的要求正在走向对立。PhysX在Unreal Engine中的整合深度意味着任何架构层面的修改都会波及整个游戏开发生态,NVIDIA不能简单地用工程仿真的需求去替换游戏物理的实现。
他们的解决方案是引入一个确定性模式开关。这个模式开关的原理看似简单:当启用确定性模式时,PhysX将浮点运算替换为定点数运算,或使用严格排序的并行策略来消除计算顺序的不确定性。
但实现这一开关的代价远超表面所见。定点数运算需要开发者在编译时确定小数点的位置——也就是数值的范围和精度之间的权衡。
自动驾驶仿真需要处理从毫米级碰撞检测到公里级场景漫游的多尺度问题,单一的定点数格式无法同时覆盖精度和范围。PhysX团队最终实现了一套可配置的定点数系统,允许开发者根据场景需求调整精度参数,但这也意味着确定性不再是一个简单的布尔值——它变成了一个需要专业判断的工程决策。
更深的裂痕出现在性能层面。确定性模式下的PhysX在某些场景中损失了百分之四十到六十的性能。这不是因为定点数运算本身比浮点慢——现代CPU的定点数指令同样高效——而是因为确定性要求强制了并行策略的保守化。为了确保每次运行的计算顺序完全一致,引擎必须放弃许多动态负载均衡的优化,强制线程按照预定顺序执行约束求解。在一个典型的多体碰撞场景中,这意味着大量计算资源在等待同步点时空转。游戏开发者对此的反应是明确而冷淡的。
在Unreal Engine社区论坛上,当有人询问是否应该在游戏项目中启用PhysX确定性模式时,Epic的工程师给出了直白的回答:除非你的项目需要满足工业仿真的审计要求,否则不要开启这个模式。你付出的是实打实的帧率损失,换来的是玩家永远不会注意到的数值可复现性。
这个回答精确地标记了两种需求之间的不可通约:游戏物理的价值衡量单位是视觉可信度和帧率预算,工程仿真的价值衡量单位是数学严格性和审计可追溯性。它们使用不同的计算资源、不同的优化策略、不同的正确性标准。Unity面对的挑战更为复杂。作为一款以民主化为使命的引擎,Unity的用户基础横跨游戏开发、建筑可视化、工业仿真和影视预演等多个领域。当这些领域的物理需求发生分裂时,Unity不能像Epic那样提供一个简单的开关——因为它的用户群体中,有大量的人同时需要这两种物理,甚至不知道它们之间存在冲突。Unity最初的物理方案是内部集成的PhysX。
随着Unity在移动游戏领域的统治地位确立,PhysX的浮点非确定性问题在移动GPU的瓦片渲染架构下变得更加突出——不同GPU厂商的浮点单元实现在细节上存在差异,同一个Unity项目在不同Android设备上可能产生微妙不同的物理结果。对于游戏开发者来说,这又是一个被更紧迫的问题掩盖的细节:屏幕分辨率、帧率稳定性、发热控制远比物理数值的可复现性更直接影响用户体验。
但当Unity开始向工业数字孪生市场推进时,这个问题变得不可回避。数字孪生的核心承诺是:虚拟世界中的物理行为可以替代部分物理试验。这意味着模拟精度必须达到工程可用的水平,而结果必须在数学上可复现。一个工厂的数字孪生在两次运行中因浮点误差产生不同的设备碰撞结果,这在工程决策中是不可接受的——它意味着仿真无法作为安全评估的依据。Unity的回应是DOTS Physics——一个从头设计的确定性物理架构。
DOTS是Data-Oriented Technology Stack的缩写,这是Unity在2018年前后开始推进的技术栈重构,其核心思想是将数据导向设计原则应用到引擎的所有子系统。DOTS Physics是这个思想在物理模拟领域的极致实践:它将物理世界表示为紧密排列的实体组件数组,使用Unity的Burst编译器将其编译为高度优化的本地代码,并从根本上重新设计了并行策略以确保确定性。
DOTS Physics选择的技术路线是定点数物理。这个选择本身就标志着一场深刻的范式转换。定点数运算不是新技术——它在1990年代的游戏中广泛使用,当时的CPU缺乏浮点单元,所有数学运算都通过整数模拟。随着浮点硬件成为标配,定点数被游戏产业逐渐遗弃,只在一些对精度要求极高的金融计算中保留。DOTS Physics重新拾起这个被遗忘的技术,不是因为怀旧,而是因为定点数有一个浮点数永远无法提供的特性:严格的可复现性。
整数运算在所有硬件上产生相同的结果,没有舍入模式差异,没有结合律问题,没有GPU厂商特有的优化路径。
但这个选择的代价同样巨大。定点数物理要求开发者在设计阶段就确定数值范围,这给内容创作增加了额外的约束。一个设计为处理十米尺度场景的定点数格式,在扩展到百米尺度时会遭遇精度损失或溢出风险。对于开放世界游戏来说,这意味着物理系统的数值范围必须在项目早期就锁定,而不能像浮点方案那样自然适应任何尺度。对于工业仿真来说,这意味着不同的仿真场景可能需要不同的定点数配置,而切换配置意味着整个物理资产的重新校准。
性能数据揭示了另一个维度的张力。在某些测试场景中,DOTS Physics的定点数实现在单线程性能上比PhysX的浮点实现慢百分之十五到二十五。这个差距通过Burst编译器的自动向量化优化和多线程并行被部分弥补,但在需要大量约束求解的复杂场景中,定点数运算的额外开销仍然可观。
Unity的策略是将选择权交给开发者:DOTS Physics同时提供浮点和定点两种模式,浮点模式服务于游戏的性能需求,定点模式服务于工程的确定性需求。但这两个模式之间的切换不是透明的——它们使用不同的数据结构、不同的求解器参数、甚至不同的碰撞检测算法变体。
Havok的情况则构成了第三条线索。作为3A游戏领域历史最悠久的物理中间件之一,Havok从2000年起就绑定在大量自研引擎和商业引擎中。它的客户名单包括从《光环》到《刺客信条》的几乎所有顶级3A系列。与PhysX和Unity不同,Havok面对需求分裂时的选择不是添加模式开关或重构架构,而是分裂自身。2018至2023年间,Havok的产品线出现了明显的分叉迹象。Havok Physics继续服务于游戏产业,保持其传统的浮点运算、启发式优化和足够好的哲学。
Havok开始向工业客户提供一套独立的物理验证工具链,这套工具链使用不同的算法选择、不同的精度策略,甚至不同的API设计。游戏版Havok关心的是如何在十六毫秒内给出视觉上可信的结果;工业版Havok关心的是如何在可接受的计算时间内给出数学上可审计的结果。
这种分裂在技术架构层面表现为一系列不可调和的选择。游戏版Havok使用迭代约束求解器,它在每次物理步进中执行固定次数的迭代,结果越迭代越精确但永远不保证收敛到数学上的真值。工业版使用直接求解器或高迭代次数的配置,追求约束方程的精确满足,代价是计算时间不可预测——一个包含数千个接触点的场景可能需要数百毫秒来求解,远超实时帧预算。
碰撞检测的选择同样分裂。游戏版Havok使用近似凸分解和层次包围盒的启发式优化,这些技术可以在百分之九十九的情况下给出正确的碰撞结果,但在边界情况下可能漏报或误报。
对于游戏来说,偶尔的漏报只是意味着一个物体穿过了另一个物体——玩家可能会注意到,也可能不会,即使注意到了也通常只是一笑置之。工业版Havok不能承受这种偶尔。在机械装配仿真中,一个漏报的碰撞意味着虚拟零件在现实中可能无法安装;在建筑结构分析中,一个误报的碰撞意味着设计可能被错误地否决。这两种需求的不可通约在Havok的架构中凝固为两个独立的产品。
这不是技术升级,而是需求分裂——同一个物理世界,需要两种不同的真相。这三条线索——PhysX的确定性模式开关、Unity DOTS Physics的定点数架构、Havok的产品线分裂——在2018至2023年间平行展开,共同标记着物理引擎的身份危机。危机的核心不在技术层面,而在定义层面:物理引擎服务的真相究竟是什么?是玩家眼球认可的视觉可信度,还是审计日志要求的数学可复现性?这个问题之所以尖锐,是因为它无法通过技术进步来解决。
更快的CPU不会让浮点非确定性消失——它根植于IEEE 754标准的灵活性和并行计算的数学性质。更智能的算法不会让两种需求统一——视觉可信度追求的是感知效率,数学可复现性追求的是逻辑严格性,它们优化的是不同的目标函数。物理引擎站在一条虚构的边界上:一边是游戏世界允许的合理错觉,另一边是工程世界要求的数学真相。这条边界不是临时的技术限制,而是两种认知模式的根本分界。
边界两侧的语言互不相通。游戏物理的话语体系围绕着感觉展开——碰撞响应是否扎实,爆炸效果是否震撼,角色移动是否顺滑。这些都是定性的、主观的判断,无法转化为数值指标。工程物理的话语体系围绕着误差展开——仿真结果与解析解的偏差是否在允许范围内,两次运行之间的差异是否小于阈值,计算过程是否可以被独立验证。这些都是定量的、客观的指标,无法用感觉来评判。当同一个引擎必须同时服务这两种语言时,架构层面的选择就变得不可避免。
Epic的选择是将边界封装为一个开关,让开发者自行决定站在哪一边。Unity的选择是将边界暴露为两种后端,让开发者理解这一选择的代价。Havok的选择是将边界固化为两个产品,承认这两种需求不可能被同一个代码库满足。
这三种策略的共同之处在于:它们都放弃了对统一物理的追求。在引擎纪元的早期,物理引擎的理想是提供一个通用的物理模拟框架,足够灵活以适应所有应用场景,足够精确以满足所有需求层次。这个理想在2018年之后被证明是虚幻的。物理模拟不是一项可以从不够精确升级到足够精确的技术——它从一开始就面临一个二元选择:你要服务的是感知的真实还是数学的真实?这两个目标要求的算法路径、数值策略和并行架构是根本不同的。
这个判断的证据可以在一份份技术报告中找到。NVIDIA在发布PhysX确定性模式时的文档中明确列出了该模式的限制:不支持某些优化路径,不兼容某些GPU架构,不保证跨平台的一致性。
Unity在DOTS Physics的技术博客中坦承定点数模式的性能代价,并建议开发者仅在必要时使用。Havok在产品路线图更新中将游戏物理和工程仿真列为两个独立的产品方向,使用不同的版本号和发布周期。这些技术文档的语言是冷静的、工程师式的,但它们的累积效应是沉重的:它们共同宣告了物理引擎的统一理想已经终结。引擎不再是那个可以同时服务所有主人的通用工具——它必须在两种真相之间做出选择,或者更准确地说,它必须让用户做出选择。
这个选择的重量在2020年前后开始被具体地感受到。一个标志性案例发生在工业数字孪生领域。某大型制造企业使用Unreal Engine构建了一条虚拟装配线的数字孪生,用于测试新设备的安装方案。项目团队由游戏引擎开发者和工业工程师混合组成,前者熟悉Unreal的物理系统但不懂公差分析,后者精通机械装配但对实时渲染一无所知。
项目进行到中期时,一个关键问题浮现:装配线中一个机械臂的运动轨迹在多次仿真运行中产生了不一致的结果。游戏开发者检查了代码,确认输入参数完全一致,认为这是正常的浮点误差,不影响视觉呈现。工业工程师则指出,这个误差对应的物理偏移量约为一点二毫米——在某些装配公差等级下,这已经超出了允许范围。
争论持续了数周。游戏开发者尝试通过调整物理子步长和求解器迭代次数来减少差异,但无法完全消除。工业工程师要求提供每次运行的完整日志以便审计,但Unreal的PhysX集成并不提供这种级别的数值追溯能力。最终,项目被搁置,企业转而评估专门的工业仿真软件——那些在性能上远不如Unreal,但在数学严格性上符合工程标准的工具。
这个案例没有公开记录,但它的逻辑在行业内被反复验证。当一个引擎被要求同时持有两种真相时,它会在某个临界点同时失去两个阵地:游戏开发者抱怨确定性模式的性能损失,工业工程师质疑浮点模式的数学可靠性。
引擎架构必须在这条虚构的边界上划出一条清晰的、可配置的界线,否则两边的用户都会离开。
这条界线的划定方式因引擎而异,但所有方案都指向同一个结构性问题:引擎代际更替在此处表现为服务对象的根本分裂。回顾引擎纪元的早期阶段,物理模拟被引入游戏时的契约是清晰的:它服务于消费级硬件的实时帧预算,结果只需要在视觉上成立。这个契约支撑了从Quake III的火箭跳到半条命2的重力枪再到战地系列的破坏效果——二十年的游戏物理创新都在这个框架内展开。
但当自动驾驶公司、工业制造商和影视制片厂开始将引擎视为仿真基础设施时,他们带入的不是更高的性能需求,而是一种完全不同的正确性标准。这个标准不是游戏物理的自然延伸,而是对游戏物理的根本否定。硬件契约的断裂在此处表现得最为赤裸。
消费级GPU的浮点单元被设计为追求吞吐量优先于精度一致性,NVIDIA和AMD在每一代架构中都会调整浮点单元的实现细节以提升性能,这些调整对游戏画面几乎没有影响但对物理确定性有致命后果。工业级仿真的需求指向的是另一种硬件契约:计算过程必须可追溯、结果必须可复现、精度必须可审计。这两种契约在同一块硅片上无法同时满足——它们要求的是不同的硬件设计哲学。
引擎架构师们发现自己站在一个不可能的位置上。他们不能要求GPU厂商为了少数工业客户的需求而牺牲游戏市场的性能竞争力,也不能要求工业客户接受看起来对作为安全认证的标准。他们唯一能做的,是在软件层面划出一条边界,让两种需求各自获得优化的路径,同时接受这条边界的存在本身就是对通用引擎理想的否定。到2023年,这条边界已经成为引擎架构中不可消除的结构性特征。PhysX的确定性模式从实验性功能变为正式产品特性,但其文档中关于性能损失和平台限制的警告从未减少。
Unity DOTS Physics的定点数后端从预览版进入稳定版,但Unity官方在推广材料中谨慎地将其定位为面向特定工业应用场景的方案,而非游戏开发的推荐选项。Havok的游戏版和工业版继续沿着各自的路线图演进,两个版本之间的代码共享比例逐年下降。
这些技术决策的累积效应是安静的,没有发布会,没有主题演讲,没有技术博客宣布统一理想的终结。但在每一个需要在两种真相之间做选择的项目团队中,在每一次因为非确定性问题被驳回的数字孪生验收中,在每一个发现定点数模式性能不足以支撑实时交互的自动驾驶仿真工程师的沉默中,这条虚构的边界都在变得比代码更真实。某个工业数字孪生项目在验收会议上被驳回的画面,或许最能捕捉这种清醒的重量。
会议室里的沉默不是因为技术失败——虚拟装配线的视觉效果令人惊叹,实时交互流畅自然,所有游戏开发的标准都被满足。驳回的理由写在审计报告的一行数字里:两次完全相同的输入条件下,机械臂末端执行器的位置偏差为零点八毫米。
这个数值在游戏物理的世界里小到可以忽略不计,在工程公差的世界里大到不可接受。引擎团队的沉默不是无言以对,而是清楚地知道:要消除这零点八毫米的偏差,他们需要重写物理引擎的底层数值策略,放弃GPU加速的某些优化路径,接受至少百分之三十的性能损失——而即使做到这一切,他们也只能保证在这个特定硬件配置上的确定性。换一块GPU,问题可能重新出现。
沉默持续了足够长的时间。长到会议室里每个人都意识到:这不是一个等待解决的技术问题,而是一个已经做出的架构选择。引擎在诞生之初选择了浮点、并行和启发式优化的路径,这些选择构成了它的骨骼。可以在骨骼上附加一个确定性模式,可以移植一套定点数后端,可以分裂出一个工业版本——但不能让同一副骨骼同时支撑两种站姿。
那条虚构的边界就在那里。它不需要被宣布存在,因为它已经存在于每一次模式切换时的性能警告中,存在于每一份注明平台限制的技术文档中,存在于每一个因非确定性而被驳回的验收报告中。
引擎纪元曾许诺一个统一的物理世界——一个同时服务于眼球和数学、同时满足帧预算和审计要求、同时容纳合理错觉和严格真相的通用框架。到2023年结束时,这个许诺被修正为一条清晰的分界线。线的一侧是游戏物理的传统疆域:廉价、快速、足够可信。线的另一侧是工程仿真的新殖民地:缓慢、精确、严格可复现。两者之间的通道被标记为可选但昂贵——一个开关、一种后端、一个独立产品版本。引擎没有放弃任何一侧。它只是承认了它们之间的不可通约。