约束系统篇推导了 XPBD 的乘子更新公式。布料是这个公式最大的消费者:一块游戏里的斗篷,动辄几千个粒子上万条约束,每帧投影两轮。这篇把公式落到 UE 的布料实现上——数据怎么组织、编辑器参数怎么进求解器、钱花在哪。
从网格到粒子系统
Chaos Cloth 不吃三角面,它吃粒子图:导入的蒙皮网格按 Cloth 面板里的 Add Cloth Painted Vertices 选区,把顶点转成粒子(FClothingSimulationSolver::AddParticles),再把网格的边映射成约束。一个顶点在布料求解里是一个带质量的粒子,质量从顶点关联的骨骼权重反推——骨骼绑得死的区域(披风肩部)质量大、约束多,自由摆动的下摆质量小。重力、阻尼、风力(FClothingWind* 外部力接口)都作用在粒子层。
约束分两类:
- Stretch(拉伸)约束:网格每条边一条,限制相邻粒子的距离。这是布料的"骨架",刚度极高(compliance $\alpha \approx 0$),几乎没有弹性;
- Bend(弯曲)约束:隔一个顶点的粒子对(或二面角形式),限制折叠。弯曲刚度决定布料是"硬纸板"还是"丝巾",可调的余地很大。
每类约束在求解器里是独立的 FConstraint 子类(Chaos/Public/Chaos/Cloth/ 下的 StretchConstraints / BendingConstraints),各持有自己的约束数组,求解时逐个 Apply。
XPBD 在布料上的形态
约束系统篇的公式在布料里变得具体。对一条连接粒子 $p_i, p_j$ 的距离约束:
$$C(x_i, x_j) = \lVert x_i - x_j \rVert - d_0$$
$d_0$ 是静止长度。梯度 $\nabla_{x_i} C = \hat{n}$,$\hat{n}$ 是两点连线单位向量——投影方向就是"把两点沿连线拉开或压回",物理意义直白。XPBD 乘子更新:
$$\Delta \lambda = \frac{\lVert x_i - x_j \rVert - d_0 - \tilde{\alpha}\lambda}{w_i + w_j + \tilde{\alpha}}$$
分子里多了 $-\tilde{\alpha}\lambda$ 累积项,这是 XPBD 和"每条约束每帧清零重算"的朴素 PBD 的分界:乘子跨迭代保留,等价于隐式积分的弹性势能,布料在被拉扯时表现出真实的"越拉越硬"的渐进刚度,而不是每一轮都从零开始的无记忆修正。
布料求解的一帧就三件事:施加重力和外力 → 预测位置 → 对 stretch、bend 约束各做若干轮 Gauss-Seidel 投影(UE 布料默认迭代很低,个位数)→ 速度重建。迭代数是布料行为的第一旋钮:低了布会"软塌"、拉伸明显;高了CPU 成本线性涨。Chaos 布料敢用低迭代,靠的是 stretch 约束极硬时收敛极快——距离约束是最良态的约束,几轮就到位,难收敛的是弯曲约束和碰撞。
碰撞与自碰撞:钱花在这
布料和环境的碰撞走两层:披风对应的角色胶囊体(FCollisionPrimitive 系)先粗筛,三角网格部分(角色实际网格)做精细碰撞——把三角形转成隐式碰撞体,对布料粒子做球扫掠。这层很贵,通常只用于关键区域。
自碰撞(披风缠在自己身上)是性能黑洞,原因在数据结构:自碰撞需要一张粒子空间哈希表(spatial hash),每帧重建,然后对每个粒子查邻居、为靠得太近的粒子对动态生成碰撞约束。生成约束是分配行为——和刚体那套"约束数量相对固定"的假设相反,布料的碰撞约束数每帧都在变,cache 不友好、并行难做(动态约束要原子写)。Chaos 的做法是限制自碰撞候选(只在厚度参数内的粒子对生成约束)并给它单独的子迭代预算。
实践结论:自碰撞只开给视觉焦点布料(主角披风),NPC 的布料靠高弯曲刚度 + 禁自碰撞糊弄过去, Self Collision Thickness 调大反而能早点被粗筛干掉。
编辑器参数到物理系数的映射
Cloth 面板里几十个滑条,真正进求解器的通道很窄,值得记几个对应关系:
Stretch Stiffness (Horizontal/Vertical)→ stretch 约束的 compliance 缩放,1.0 基本等于 $\alpha \to 0$ 的硬约束;Bend Stiffness→ bend 约束的 $\alpha$,换算关系是非线性的,经验值 0.2 以下是软布,0.5 以上接近皮革;Damping→ 速度重建后对 $v$ 的缩放,压的是高频抖动,代价是"水蛇感";Drag / Lift→ 风力项在法向和切向的分解系数,不是物理量,是手感参数;Max Distance / Backstop→ 不是约束,是蒙皮阶段把粒子锚回骨骼的运动学混合权重,粒子离骨骼超过 Max Distance 就被拉停。披风"穿身"的最后一道防线是它,不是碰撞。
最后一组尤其容易误解:很多人调不动布料穿模就去加碰撞迭代,其实 Max Distance 设小了根本没给布料自由摆动的余量,或者设大了等于没设。正确的排查顺序是先开 p.ChaosCloth.DebugDraw 系列把 Max Distance 可视化,再动求解参数。
源码入口
- 求解器:
Chaos/Public/Chaos/Clothing/ChaosClothSolver.h的FClothingSimulationSolver,看Update里的外力、预测、迭代三段; - 约束实现:
Chaos/Cloth/ChaosClothConstraints*(stretch/bending/collision 各一个文件),看Apply里的 XPBD 更新; - 与角色的桥:
ClothingSimulation.cpp的FClothingSimulation接口,GT 侧把骨骼位置灌进来、把模拟结果写回蒙皮顶点。
粒子-约束-迭代的关系画在这张图里: