可行但需严控三处:invMass非零且正确注入约束求解器;预测步仅对invMass>0粒子更新速度并跳过固定点;约束迭代次数应8–12次且线性衰减权重,体积约束需单独多轮、防符号翻转导致塌陷。

直接在 C++ 中用原生代码从零搭 PBD 软体模拟,可行但极易卡在约束迭代发散、质量权重错位、或位置更新后速度未反推这三处。真正能跑通的起点不是写 updatePositions(),而是先确保粒子质量倒数(invMass)非零且正确注入每个约束求解器,否则所有后续迭代都在无效投影。
为什么刚写完预测步就穿模?检查 invMass 和外力积分顺序
PBD 的预测阶段看似简单:用重力和当前速度算出临时位置。但常见错误是把重力加在速度上之后,没做质量加权——比如直接写 v += gravity * dt,而忽略了该粒子是否为固定点(invMass == 0)。更隐蔽的问题是:若你用显式欧拉更新速度,却在后续约束求解中又用该速度反推位置变化量,就会引入数值漂移。
- 固定粒子(如布料锚点)必须设
invMass = 0.0,且在所有约束投影中跳过它们的位移分配 - 预测步中,只对
invMass > 0的粒子更新速度:v[i] += gravity * dt * particle.invMass[i] - 不要在预测步里更新位置;PBD 要求位置更新完全由约束投影驱动,预测位置只是初始 guess
PBD::DistanceConstraint 怎么避免弹簧抖动?关键在迭代权重与阻尼伪实现
距离约束本身不带阻尼,但软体晃动不止来自刚度不足,更常源于约束间耦合震荡。原始 PBD 没有速度项,所以不能直接加阻尼力。常用做法是在每次约束投影后,对参与该约束的粒子施加一个与相对速度同向的微小位置偏移——本质是用位置差“伪造”速度反馈。
- 每次投影计算出修正向量
delta后,额外叠加一项:delta += 0.02f * (v[j] - v[i]) * dt(需保证invMass已用于归一化) - 迭代次数别硬写死 3 或 5;对软体建议至少 8–12 次,且每轮按
weight = 1.0f / (float)numIterations线性衰减约束强度,防过冲 - 避免在单次迭代中反复修改同一粒子位置再用于其他约束——必须等本轮所有约束遍历完,再统一应用全部
delta
体积约束(VolumeConstraint)为何让网格塌陷?注意四面体符号与质量分布
软体内部体积保持靠四面体单元的有向体积约束。塌陷通常不是因为约束太弱,而是符号判错导致所有投影全往内挤。PBD 中体积误差定义为当前有向体积减目标体积,但若顶点顺序不一致(顺时针/逆时针混用),符号就翻转。
- 预处理阶段对每个四面体调用一次
computeSignedVolume(),打印前几帧的值,确认其正负号稳定 - 约束梯度计算依赖各顶点质量倒数;若某顶点
invMass过小(比如 1e-6),它在梯度中的权重会畸高,拖垮整个四面体——应限制invMass下限为 1e-3 - 别让体积约束和距离约束共享同一轮迭代次数;体积约束收敛慢,建议单独多跑 2–3 轮,或用分层迭代(distance 先 8 轮,再 volume 补 4 轮)
最易被忽略的是:PBD 软体没有“弹性模量”这种物理参数,所有刚度都映射到约束迭代次数和投影缩放系数上。调参时若只改 stiffness 却不动迭代数,结果必然失真——这不是 bug,是 PBD 方法论本身的耦合特性。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











