本节摘要:关节是持久约束:球铰锁三点重合,铰链再锁两根轴的相对旋转,滑移锁旋转留一根平移。写成速度层等式,有效质量和接触相同,只是 lambda 通常不钳在零(可拉可推),角度限制才变成不等式。电机是带目标速度的软约束。关节与接触一起 PGS 迭代,破碎和极限用冲量上限模拟。
阅读完本节,你应当能够:
球铰:物体 A 上局部点 pA 与 B 上 pB 世界坐标应重合。位置误差 C = xA + rA - (xB + rB)。速度约束希望 vA + wA×rA - vB - wB×rB = -beta * C / dt(偏置把位置误差赶掉)。三个分量三条约束,或一次解 3x3 有效质量矩阵。顺序冲量也可以按 x/y/z 三个方向各一条,像三个能拉能推的接触。
铰链:球铰加上两根正交轴的相对角速度为 0(只留一根轴自由转)。滑移(棱柱):锁旋转,留一根平移。固定关节:六自由度全锁,等于焊死,可用更便宜的“合成刚体”代替——能合就不要用六约束硬焊,数值更差。
JointBall: handleA, handleB localAnchorA, localAnchorB gamma # 软化 beta # 位置偏置
脚手架上的扣件:球铰像万向接头,铰链像门轴,滑移像电梯导轨。扣件太松,架子晃;太紧(beta 大、无软化),风一来整架抖。关节参数就是扣件松紧。
| 关节 | 锁住的自由度 | 游戏例子 | 实现难度 |
|---|---|---|---|
| 球铰 | 3 平移 | 链条、布娃娃肩 | 低 |
| 铰链 | 3 平移 + 2 旋转 | 门、轮胎转向节 | 中 |
| 滑移 | 2 平移 + 3 旋转 | 活塞、电梯 | 中 |
| 距离 | 1 | 绳、杆 | 低 |
| 电机 | 加在铰链/滑移上 | 电门、载具驱动 | 中 |
| 焊接 | 6 | 临时粘连 | 高,优先合并刚体 |
距离关节就是弹簧的硬版本:沿当前连线一条约束。绳索能松不能压,lambda 只拉不推,又回到互补。第 5.3 节绳索会用一串距离约束或 PBD。
铰链角度限制:算出当前相对角,超出区间则加一条不等式,方向沿铰链轴,偏置把角度推回。不要每帧在限位处用很大 e,那会像橡皮筋弹门。限位用 e=0 加偏置。
电机:目标相对角速度 w_target,约束 relative_w_along_axis = w_target,但 lambda 限制在最大力矩对应的冲量里(力矩×dt)。力矩不够时达不到目标转速,这是正确的过载行为。不要无限 lambda,否则电机是无穷力矩,一帧把门甩飞。
软关节:有效质量加 gamma/dt,像弹簧阻尼。布娃娃用软球铰更耐看,硬铰链在拉扯时数值爆炸。柔软程度按动画需求,不要全局一个 gamma。
💡 关键直觉:接触默认“只推不拉”,关节默认“可推可拉”,限位和绳是“只拉不推”。同一套 PGS,合法集合不同。
破碎:累计 lambda 超过阈值则删除关节。要过滤瞬时冲击,用若干帧平均,否则鞭炮式碰撞误碎。删除后唤醒两岛。
门被箱子顶住:接触想把箱子推开,铰链想把门留在轴上。两者合法。若迭代少,门轴会慢慢漂。提高关节约束在列表中的重复次数(每轮解两次关节一次接触)能让轴更硬。反过来,载具悬挂太硬会顶穿地面,应让接触优先或给悬挂更多软化。
⚠️ 常见坑:关节锚点用世界坐标存储,物体一动锚点留在原地,下一帧巨大位置误差,bias 把物体射向远方。锚点必须局部坐标,每帧用当前变换算世界位置。
奇异:两物体共线铰链加另一条共线约束,有效质量矩阵不可逆。检测 K 过小就跳过该分量。固定关节六条约束在数值上接近相关,优先合并刚体。
调试画锚点、画自由轴、画限位扇形。轴漂了先看局部锚点是否写错物体。电机不转先看 lambda 是否顶满——顶满是力矩不够,不是约束写错。
子步里关节和接触一起重新生成位置误差。关节不要隔帧更新,轴会抖。休眠:关节连接的物体必须同岛,一端动全岛醒。这是 6.2 的前置约定,现在实现关节唤醒时就要把两体放进同一唤醒图。
2D 里铰链退化为一个点约束加可选角度限位,非常适合先写。3D 铰链的旋转约束要用铰链轴与两物体局部轴的叉积构造雅可比,写错会锁死错误的轴,门在斜面上“研磨”。用已知门模型:只重力下门应在铰链轴上摆,不应侧倾。侧倾就是旋转约束漏了。
重力对关节:不要在关节里补偿重力,积分已经加了。长链条下垂应呈悬链线状,若链条飞起,多半是偏置符号把误差往错误方向赶。C 的定义和 bias 符号要对着相对速度的定义一起推,不要凭感觉加负号。用静止球铰吊着一个物体,稳定后 lambda 应接近重量冲量。
性能:关节数量通常远少于接触,但每条关节 3–6 行有效质量,比接触贵。布娃娃 15 个球铰仍很小。不要为关节单独上复杂求解器,PGS 够。除非你在做并联机构高精度仿真。
毕业实验:一条门,玩家推,限位挡住,松手后门若有阻尼应停下。再在门前堆箱子,门不应离轴。再给电机开门,碰上箱子应力矩顶满而不是穿模。三连过了,关节这一节才算能接载具轮胎(轮胎往往是铰链加电机加悬挂滑移的组合,5.4 再用)。
布娃娃用软球铰加角度限位,不要硬铰链。硬约束在摔倒冲击下需要巨大 lambda,数值爆炸或穿透地面。柔软用 gamma,看起来像有肌肉阻尼。动画混合:走路时关节关掉用动画,摔倒时打开物理,切换要对齐锚点,同 5.4。
焊接两块动态体:优先合并成一个刚体,重算质心惯量。运行时焊来焊去再用六约束,堆木板游戏会抖。真要可拆,焊接约束加冲量上限,超过就断,断后唤醒两岛。
电机开门撞到箱子:力矩顶满,门停在箱子上,不应穿模。若穿过,接触迭代相对关节太弱,提高接触优先级或给电机更小最大力矩。玩法调的是最大力矩,不是 beta。
2D 距离关节当绳:只拉不推,lambda 钳在大于等于 0。松弛时链条下垂。和下节粒子绳的差别是刚体段有转动惯量,观感更“铁链”。粒子绳更便宜。条数少用刚体链,条数多用粒子。
不行。物体一动误差爆炸,bias 射飞。局部锚点每帧变世界。传送时当关节重新初始化,清误差。
通常不是,是力矩不够。看 lambda 是否在上限。顶满而门仍该动,才加力矩。顶满却穿模,是接触太软。
六约束相关,数值差。能焊死就合成一个质量惯量。可拆再上约束加破碎阈值。
bias 与 C 的定义相反,误差往错误方向赶。用静止吊重物对账:lambda 接近重量冲量。对上了再加多节。
否则一端睡一端动,轴被撕开。唤醒沿关节传播。实现关节时就把边加进岛图,不要等 6.2 才补。
旋转约束漏轴。验收:只重力下只绕轴摆。
一帧甩飞。最大力矩对应冲量上限。
橡皮门。限位 e=0 加偏置。
数值爆炸。软球铰加 gamma。
两岛变一岛睡眠失效。设计问题也是性能问题。
鞭炮误碎。用平均。
奇异。共线约束要防护。
分不清投影还是旋转约束。先 2D。
在「03-关节铰链与限制」路径上加计数器要进 step 末尾快照,不要在热循环里格式化字符串。计数器本身若分配内存,你会优化一个被测量污染的世界。发布版可用宏剥掉绘制,但计数器的定义要保留,方便线上开一个极轻的统计开关。 本条对应「03-关节铰链与限制」第 1 号备忘,和相邻条目不要合并成一句空话,分开验收。
「03-关节铰链与限制」一旦和随机数沾边,随机必须绑在 tick 上。墙钟、哈希表遍历、线程完成顺序都是隐藏输入。隐藏输入会让哈希校验在某一帧突然红,而你却以为是公式写错。先排除隐藏输入,再怀疑有效质量。 本条对应「03-关节铰链与限制」第 2 号备忘,和相邻条目不要合并成一句空话,分开验收。
调「03-关节铰链与限制」时一次只改一个量,写下场景名、旧值、新值、醒岛数或能量变化。没有笔记的调参会在一周后变成传说。传说不能回归,不能交给同事,也不能在换库时当合同。 本条对应「03-关节铰链与限制」第 3 号备忘,和相邻条目不要合并成一句空话,分开验收。
「03-关节铰链与限制」的单元测试尽量不依赖图形窗口。能在无头模式跑的断言,才会每次提交都跑。只靠进游戏看一看,等于没有测试。把金字塔睡眠、速度交换、自由落体距离这类金标准写成断言。 本条对应「03-关节铰链与限制」第 4 号备忘,和相邻条目不要合并成一句空话,分开验收。
文档里的公式符号必须和代码注释同一张表。尤其是法向从谁指向谁、相对速度谁减谁、lambda 是力还是冲量。「03-关节铰链与限制」相关代码若混用两套符号,吸引接触和飞天会轮流出现,现场非常像随机 bug。 本条对应「03-关节铰链与限制」第 5 号备忘,和相邻条目不要合并成一句空话,分开验收。
性能上先问能不能少做。「03-关节铰链与限制」再快,也快不过把睡着的岛整岛跳过、把碎片到期删掉、把静态体移出动态表。减工作量优于把同样工作向量化。向量化平方算法只是错得更稳更快。 本条对应「03-关节铰链与限制」第 6 号备忘,和相邻条目不要合并成一句空话,分开验收。
下一章离开纯刚体:软体、流体、布料、载具和角色,用位置约束或专用控制器,不再强求一个 PGS 走天下。