Skip to main content

Chapter 67: Soft Body Dynamics

软体动力学(Soft Body Dynamics)处理一类会发生形状变化的物体:布料会被重力拉长,披风会随角色运动摆动,果冻块会在碰撞后压缩并回弹。这一章要建立的能力是:读者能够把一个软体效果追踪到模拟网格、约束求解、渲染网格绑定、碰撞约束和调试证据,并能根据画面症状判断该调 timestep、stiffness、iteration、damping、pin 约束还是碰撞处理。

本章贯穿材料是一条统一的软体数据路径:一块悬挂布料和一个果冻块都先被表达成 simulation mesh,求解器更新节点位置,再把变形传给 render mesh。布料更接近二维网格约束系统,果冻块更接近三维体积约束系统;二者共享同一类核心状态:节点位置、速度、质量、约束、碰撞接触、rest shape 和本帧输出。

软体模拟在实时图形中的目标通常是视觉可信、交互稳定和帧预算可控。影视离线模拟可以追求更细的材料模型和更高的迭代精度;实时引擎更关心每帧是否稳定、是否能和角色骨骼、碰撞体、shader 变形、LOD、debug view 一起工作。这个差异会直接影响模型选择:mass-spring 便于实现,FEM 便于表达体积材料,Position Based Dynamics 便于实时控制,shape matching 便于保持整体形状。

读完本章后,判断一个软体效果的顺序应收敛为一条工程链:先看 simulation mesh 如何表达材料,再看约束怎样从 rest state 生成,再看 solver 每帧怎样推进位置,再看 render mesh 如何接收变形,最后用视觉结果和 debug view 复查拉伸、抖动、穿透、体积损失和帧耗时。

67.1 Soft Body Dynamics Equations and Solver State

软体动力学的核心问题是用有限状态表达可变形材料。真实材料有连续的内部应力、应变和接触过程;实时图形系统会把它离散成节点、边、面、四面体或粒子约束。离散之后,求解器每帧接收上一帧状态、外力、碰撞体和时间步长,输出新的节点位置、速度和可渲染变形。

四类模型服务不同的工程目标

mass-spring 模型把软体看成一组带质量的节点和弹簧。节点保存位置、速度和质量,弹簧保存两端节点、rest length、stiffness 和 damping。对一条弹簧,常用力模型可以写成 $f = -k(|x_i - x_j| - L_0)n - c((v_i - v_j) \cdot n)n$。这里 $L_0$ 是静止长度,$n$ 是两端节点方向,$k$ 控制回弹强度,$c$ 控制相对速度衰减。这个模型适合布料、绳索、简单果冻和教学实现,因为它直接把视觉拉伸映射到边长变化。

FEM(Finite Element Method,有限元方法)把软体看成由三角形或四面体组成的连续材料近似。二维布料可以用三角形表达薄膜,三维软体可以用四面体表达体积。FEM 的关键状态是每个 element 的 rest shape、当前形变梯度、材料参数和由能量或应力得到的节点修正。它能表达拉伸、剪切、弯曲、体积保持和各向异性材料,代价是装配、求解和调参成本更高。

Position Based Dynamics(PBD,位置约束动力学)把求解目标转成“把预测位置投影到满足约束的位置”。Müller 等人在 Position Based Dynamics 中给出了一条实时图形常用路径:先用外力预测位置,再迭代投影距离、弯曲、碰撞等约束,最后根据位置变化更新速度。它对美术和引擎更友好,因为 pin、碰撞、最大拉伸、形状保持都能表达为位置约束。

shape matching 把一组节点的当前形状匹配回 rest shape。它先计算当前节点簇和静止节点簇的最佳刚体变换,再把节点推向目标位置。这个方法适合果冻块、软体玩具、角色局部软组织和需要保持整体体积感的效果。它的参数感知方式接近“整体回到原始形状的速度”,因此调试时应关注簇大小、目标权重、局部重叠和速度更新。

模型核心状态直接控制对象主要收益典型风险
Mass-spring节点、速度、弹簧、阻尼边长和相对速度实现直接,适合布料和绳索高 stiffness 与大 timestep 组合会产生抖动
FEMelement、材料参数、形变梯度应变、应力或能量材料表达更完整,适合体积软体求解成本和网格质量敏感
PBD预测位置、约束、inverse mass距离、弯曲、碰撞、pin实时稳定,交互约束清晰迭代不足会残留拉伸和体积损失
Shape matching节点簇、rest shape、目标位置整体形状记忆果冻和局部软组织效果稳定簇设计粗糙会显得过硬或塌缩

求解器状态要区分 simulation mesh 和 render mesh

simulation mesh 是求解器真正更新的数据。它可以是布料网格的粒子,也可以是果冻块内部的四面体节点。render mesh 是屏幕上显示的模型,它可能比 simulation mesh 更密,也可能带有法线、切线、UV、材质、skin 权重和形态细节。软体模拟的工程边界从这里开始:求解器追求稳定状态,渲染器追求高质量表面。

一帧软体求解最小状态通常包含以下对象。position 是当前帧节点位置,previousPositionvelocity 用来恢复运动趋势,inverseMass 控制 pin 和不同质量节点的响应,restPosition 保存静止形状,constraints 保存距离、弯曲、体积、碰撞和绑定条件,collider 提供环境接触,renderBinding 把 simulation mesh 的变形传给 render mesh。

求解过程可以用一条帧内路径表达。图中的每个节点都对应一个可调试对象:输入网格、预测位置、约束残差、碰撞修正、速度回写和渲染绑定。

这条路径的关键点是“预测”和“投影”分离。预测阶段根据速度和外力给出本帧候选位置,投影阶段根据约束把候选位置推回可接受范围。布料被拉长时,距离约束残差会增大;果冻块被压扁时,体积或 shape matching 残差会增大;穿透发生时,碰撞约束会把节点推出碰撞体。

方程在图形工程中要落到可观察量

对实时图形而言,方程的价值在于把视觉症状映射成状态差异。布料边缘像橡皮筋一样被拉长时,观察对象是约束前后的边长比。披风贴在角色背上抖动时,观察对象是 pin 节点速度、角色驱动位移和碰撞修正量。果冻块碰撞后持续弹跳时,观察对象是阻尼、速度回写、shape matching 权重和接触法线方向。

PBD 的核心更新可以简化成以下顺序:用速度和外力预测 $p_i$,对每个约束计算 $C(p)$,沿约束梯度移动相关节点,迭代若干轮,最后用 $v_i = (x_i^{new} - x_i^{old}) / \Delta t$ 回写速度。这个速度回写把位置投影的结果变成下一帧运动趋势,因此碰撞、pin 和距离修正都会影响后续帧。

一个合格的 solver state debug view 至少应能显示四类量:节点是否被 pin,约束残差是否集中在局部,碰撞修正是否持续增大,render mesh 是否和 simulation mesh 脱节。只看最终画面会把这些问题混在一起;把状态画出来后,才能判断问题来自模型表达、求解迭代、碰撞生成还是渲染绑定。

67.2 网格与弹簧模型实现细节

网格与弹簧模型的实现目标是把“材料会拉伸、剪切、弯曲、碰撞”转成可迭代求解的约束集合。布料的 simulation mesh 常用规则网格或三角网格;果冻块可以用体素格、四面体网格或粒子簇。实现时先确定节点,再从节点邻接关系生成 structural、shear、bend 等约束。

Structural、shear、bend 约束负责不同形变

structural spring 连接网格中水平和垂直相邻节点,主要控制布料沿经纬方向的拉伸。它对应最直观的视觉症状:布料被重力拉长,边长超过 rest length。结构约束残差大,画面会表现为整体变长、衣角下坠或连接点附近拉丝。

shear spring 连接对角节点,主要控制单元格从正方形或三角形变成菱形的程度。没有 shear 约束时,布料可以在不明显改变边长的情况下发生斜向滑移,角色披风会出现网格方向的扭曲。shear 约束的强度应和 structural 约束分开调,因为布料可以允许一定剪切,同时保持主要拉伸受控。

bend spring 连接相隔一条边的节点,主要控制弯曲。它影响布料褶皱宽度和柔软程度,也影响披风摆动后的高频波纹。bend 约束过强会让布料像薄塑料板,过弱会让网格产生尖锐折痕。真实引擎常用二面角约束、距离近似或三角形弯曲能量表达弯曲,工程选择取决于帧预算和需要的视觉质量。

对一块规则布料,约束生成顺序可以稳定表达为:先生成节点和 rest position,再生成 structural 约束,再生成 shear 约束,再生成 bend 约束,最后标记 pin 节点和碰撞层。这个顺序有利于调试,因为每类约束都能单独开启,画面差异可以直接对应到材料维度。

距离约束投影是最小可复用实现

下面的简化代码展示距离约束的投影步骤。代码使用 C++ 风格,省略向量类实现和边界检查,目的在于说明 residual、inverse mass 和 stiffness 如何共同决定节点修正。

struct Particle {
Vec3 position;
Vec3 velocity;
float inverseMass;
};

struct DistanceConstraint {
int a;
int b;
float restLength;
float stiffness;
};

void solveDistanceConstraint(Particle& pa, Particle& pb, const DistanceConstraint& c) {
Vec3 delta = pa.position - pb.position;
float length = max(delta.length(), 1e-6f);
Vec3 direction = delta / length;
float residual = length - c.restLength;
float weightSum = pa.inverseMass + pb.inverseMass;

if (weightSum <= 0.0f) {
return;
}

Vec3 correction = c.stiffness * residual * direction / weightSum;
pa.position -= pa.inverseMass * correction;
pb.position += pb.inverseMass * correction;
}

这段代码里,residual 是当前边长和 rest length 的差值。inverseMass 决定节点是否参与修正,pin 节点通常使用 0stiffness 决定单次投影的强度。多个约束共享节点时,求解器会反复遍历约束集合,让局部修正逐渐传播到全网格。

这个实现能直接解释布料调参。若 stiffness 偏低,单次投影修正小,布料会显得松。若 iteration 偏低,修正传播不充分,固定点附近会残留拉伸。若 timestep 偏大,预测位置离 rest state 太远,约束残差在一帧内增大,画面会产生抖动或穿透。

Damping 要服务速度更新,不能掩盖错误约束

damping(阻尼)用来减少高频振动。它可以作用在速度上,也可以作用在弹簧方向的相对速度上。速度 damping 简单直接,但会同时消耗整体运动;弹簧方向 damping 更局部,适合压制边长方向的颤动。对披风和布料,常见做法是保留主要摆动,再压制节点间高频抖动。

阻尼调试的观察对象是速度场。若布料整体摆动合理,但边缘出现细碎颤动,应先观察约束残差和相对速度,再增加局部 damping。若整块布料像被粘住,说明 damping 已经消耗了主运动,需要回到 stiffness、iteration 和 timestep 检查。

碰撞会放大阻尼问题。节点碰到角色或球体后,位置被推出碰撞体,速度回写又把这次位移变成后续速度。若接触法线方向的速度处理粗糙,布料会在表面抖动。稳定做法是碰撞修正后对法线速度做受控处理,再让切向速度进入摩擦模型。

Collision 约束需要明确 inside/outside 判定

布料和软体碰撞的输入是节点预测位置和环境碰撞体。球体、胶囊体、平面和 signed distance field 都能提供相对稳定的 inside/outside 判定。以球体为例,若节点到球心距离小于半径,就把节点沿法线推出球面,再根据摩擦和 restitution 修改速度。

对角色披风,胶囊体通常比三角网格更稳定,因为胶囊体能提供连续的 inside/outside 判定和清晰法线。三角网格碰撞能贴合模型,但离散三角面会增加漏检、卡边和法线突变风险。工程上常用简化碰撞体驱动布料,再让 render mesh 通过材质和法线细节补足视觉。

自碰撞成本更高。布料自碰撞需要查找节点与三角形、边与边或粒子半径之间的接触。实时系统通常先用 spatial hashing、grid 或 BVH 缩小候选对,再生成局部接触约束。调试时应分别显示候选对数量、实际接触数量和修正量,否则帧耗时上升后很难判断瓶颈在 broad phase 还是 projection。

67.3 软体模拟在引擎中的实现

软体模拟进入引擎后,问题从单个求解器扩展为资源生命周期。simulation mesh 由资产导入或运行时生成,constraint buffer 由 rest state 构建,animation system 可以驱动 pin 节点,physics step 更新节点位置,render system 把变形送入 vertex shader 或 CPU mesh update,debug view 输出约束残差、碰撞接触和节点状态。

Simulation mesh 是物理状态,render mesh 是视觉表面

simulation mesh 通常比 render mesh 更简单。布料可以用较低分辨率网格求解,再通过 skinning、barycentric binding 或插值驱动高分辨率表面。果冻块可以用内部粒子或四面体节点求解,再把表面顶点绑定到最近四面体或 shape matching 簇。这样做把求解成本和视觉细节解耦。

render binding 的核心是记录每个渲染顶点从哪些 simulation 节点获得位置。布料表面可以直接一一对应,也可以通过细分网格插值。体积软体常用四面体重心坐标:渲染顶点落在哪个四面体内,就保存四个节点权重;求解后用四个节点新位置重建渲染顶点。

绑定错误会产生独特症状。simulation mesh 稳定,但 render mesh 出现局部撕裂,通常是绑定权重或拓扑映射错误。render mesh 法线闪烁,通常是变形后法线没有重算,或切线空间没有随变形更新。软体表面穿过碰撞体,可能是 simulation mesh 节点被推出了碰撞体,但 render mesh 插值表面在节点之间仍穿透。

一帧集成要明确模拟、同步和渲染时机

引擎中的软体更新通常放在固定 physics step 或 frame graph 的 simulation pass 中。若渲染帧率和物理帧率不同,需要插值或外推。对角色披风,角色骨骼先更新,pin 节点从骨骼获得目标位置,软体 solver 再根据这些目标求解,最后渲染系统读取当前软体输出。

下面的伪代码展示一个 API 无关的帧内组织方式。它强调对象边界:动画写 pin target,soft solver 写 simulation buffer,render binding 写 deform buffer,渲染 pass 读取 deform buffer。

void updateSoftBodyFrame(FrameContext& frame, SoftBody& body) {
body.pinTargets = sampleAnimationPins(frame.skeletonPose);
body.colliderSet = buildColliderSet(frame.sceneColliders);

for (int step = 0; step < frame.physicsSubsteps; ++step) {
predictPositions(body.particles, frame.substepDt, body.externalForces);
generateCollisionConstraints(body.particles, body.colliderSet, body.contacts);

for (int iter = 0; iter < body.solverIterations; ++iter) {
solvePinConstraints(body.particles, body.pinTargets);
solveDistanceConstraints(body.particles, body.distanceConstraints);
solveBendConstraints(body.particles, body.bendConstraints);
solveCollisionConstraints(body.particles, body.contacts);
}

updateVelocities(body.particles, frame.substepDt);
}

updateRenderBinding(body.particles, body.renderBinding, body.deformVertices);
enqueueSoftBodyDraw(frame.renderGraph, body.deformVertices, body.material);
}

这段路径里,pin 约束放在每轮迭代中求解,能让披风固定点持续跟随骨骼。碰撞约束在预测后生成,在迭代中参与投影,能让碰撞和距离约束相互协商。若碰撞只在迭代结束后做一次修正,布料容易在贴近碰撞体时产生局部拉伸,因为距离约束没有机会重新分配修正量。

GPU update 适合大规模并行,但同步边界要清楚

软体求解天然有大量节点和约束,适合放到 compute shader 或 GPU compute queue。节点预测、约束残差计算、碰撞候选筛选和渲染绑定都能并行。难点在于约束之间共享节点,同一轮投影可能产生写冲突。常见处理方式包括 graph coloring、Jacobi 式累积修正、多 pass 分组、atomic 累积或把约束按独立集合拆分。

GPU 实现的资源路径通常包含 particle buffer、constraint buffer、contact buffer、deform vertex buffer 和 debug buffer。compute pass 写 particle buffer,render pass 读 deform vertex buffer。若同一帧在 GPU 上完成模拟和渲染,需要在 frame graph 中声明读写依赖,确保 compute 写入对后续 vertex shader 可见。

CPU 与 GPU 之间的 readback 是常见瓶颈。调试时可以只回读少量统计值,例如最大约束残差、平均残差、接触数量和异常节点索引。完整节点数据每帧回读会制造同步等待,使性能症状从 solver 计算转移到 CPU/GPU 同步。

Debug view 是软体工程的交付接口

软体模拟的 debug view 应直接回答“哪个状态导致画面问题”。对布料,应显示 pin 节点、距离约束残差、弯曲残差、碰撞点和 self-collision 候选密度。对果冻块,应显示 shape matching 簇、体积残差、接触法线和绑定权重。对渲染,应显示 simulation mesh 与 render mesh 的对应关系。

一个实用的调试顺序是:先单步固定外力,确认节点预测方向;再关闭碰撞,只看距离和弯曲约束是否收敛;然后开启碰撞,观察接触修正是否局部集中;最后开启 render binding,检查表面是否跟随 simulation mesh。这个顺序把求解问题、碰撞问题和渲染绑定问题拆开,定位速度会明显提高。

工具层面,RenderDoc 或引擎内 GPU capture 可以查看 compute pass 的 buffer 读写和渲染 pass 的 vertex buffer 输入。Nsight、Xcode GPU tools 或平台 profiler 可以观察 compute pass 时间、memory bandwidth 和 queue 同步。工具输出应服务于具体判断:约束残差大说明迭代或参数问题,buffer stall 说明同步问题,draw 输入异常说明绑定或资源状态问题。

67.4 稳定性和平衡求解优化技巧

软体稳定性来自多个参数的平衡。stiffness 决定约束单次修正强度,iteration 决定修正传播程度,timestep 决定预测位置偏离 rest state 的规模,damping 决定能量衰减,volume preservation 决定体积感,self-collision 决定几何接触可信度,pin constraints 决定动画驱动边界。

Stiffness、iteration 和 timestep 要联合判断

stiffness 的直观含义是材料抵抗变形的强度。在 PBD 路径里,它经常表现为单次投影权重。iteration 决定约束被重复修正的次数。timestep 决定每次预测会把节点推进多远。三者共同决定“本帧能把残差消化到什么程度”。

若布料整体被拉长,先看最大边长比和平均边长比。最大值高、平均值正常,说明局部约束或 pin 附近压力集中;平均值也高,说明 iteration 或 stiffness 总体不足。若提高 stiffness 后抖动增加,应看 timestep 和速度回写,因为预测位移过大时,强投影会制造更大的速度变化。

substep 是稳定性的常用手段。把一个大 timestep 拆成多个小 substep,可以减少每次预测的位移,降低碰撞穿透和约束残差峰值。代价是 solver 调用次数上升。实时引擎常把 substep 用在高速碰撞、角色披风和需要贴身接触的布料上,而远景布料可以降低 substep 和 iteration。

Volume preservation 解决果冻块塌缩

三维软体在碰撞和重力作用下容易丢失体积感。果冻块若只用表面距离约束,受到挤压后会像空壳一样塌陷。volume preservation(体积保持)通过四面体体积约束、pressure constraint 或 shape matching 让内部结构维持占用空间。

四面体体积约束会比较当前体积和 rest volume。当前体积过低时,相关节点沿梯度被推开。pressure constraint 更像内部压力,适合气球、软包和膨胀效果。shape matching 通过目标形状拉回整体轮廓,适合果冻、软玩具和低成本体积感。

体积保持也会引入刚硬感。若 volume 权重过强,果冻块碰撞后会像橡胶块;若权重过弱,形体会被压扁后恢复缓慢。调试时应同时观察轮廓、内部节点分布和接触面。如果接触面合理但整体塌缩,调 volume;如果接触面已经穿透,先处理碰撞和 timestep。

Self-collision 的成本来自候选对和约束冲突

self-collision 让布料折叠时互相分离,让软体局部压缩时保持表面关系。它的成本来自两部分:寻找候选对和求解接触约束。候选对过多会增加 broad phase 时间,约束冲突过多会让投影迭代震荡。

稳定的 self-collision 需要空间结构和厚度参数。布料通常给节点或边设置 collision thickness,使两层布料保持最小间距。厚度过小会漏穿,厚度过大折叠会显得蓬松。空间结构的 cell size 应和厚度、平均边长和最大速度匹配,否则候选对会暴涨或漏检。

碰撞约束和距离约束会竞争同一批节点。布料被夹在角色和自身之间时,碰撞把节点推出,距离约束又把节点拉回。这种冲突会表现为抖动、卡住或局部爆开。处理顺序可以先降低单次修正强度,再增加 substep 或 iteration,让多个约束在更小位移范围内协商。

Pin constraints 要表达动画驱动边界

pin constraint 把软体节点绑定到动画或世界目标。布料悬挂的上边缘、披风肩部、裙摆腰部和软体挂点都属于 pin 驱动。pin 既可以是完全固定,也可以是带权重的软绑定。完全固定稳定清晰,软绑定能保留惯性和延迟。

角色披风的 pin 调试要关注骨骼速度。角色快速转身时,pin target 在一帧内移动很大,布料预测位置和目标位置差异会增大。若 pin 太强,固定点附近会产生拉伸尖峰;若 pin 太弱,披风会脱离角色。工程上常把 pin 区域做成梯度权重,从肩部固定逐渐过渡到自由布料。

pin 还会影响 render mesh 绑定。若渲染顶点在 pin 附近权重分布粗糙,表面会出现折线或硬边。正确观察方式是同时显示 pin 权重热图和 render mesh 变形。只有 simulation mesh 稳定且权重连续,披风肩部才能在角色动画中保持自然过渡。

症状到参数的检查顺序

软体调参应从可观察症状进入,而后回到 solver state。下面的表格给出常见症状、优先检查状态和稳定动作。

画面症状优先检查状态稳定动作
布料整体拉长平均边长比、iteration、stiffness增加 iteration 或 stiffness,必要时增加 substep
固定点附近拉丝pin 位移、局部残差、质量分布使用 pin 权重过渡,增加局部约束密度
接触面抖动碰撞修正量、法线速度、timestep减小 substep 位移,处理法线速度和摩擦
果冻块塌缩体积残差、内部节点分布增加体积约束或 shape matching 权重
布料自穿透self-collision 候选、厚度、速度峰值调整 thickness、cell size 和 substep
GPU 帧耗时升高constraint count、contact count、buffer stall分层 LOD、减少回读、分组约束求解

这套顺序的价值在于先定位状态,再改变参数。直接反复拖动 stiffness 和 damping 会让问题互相掩盖;按残差、接触、速度、绑定和帧耗时逐层检查,才能把稳定性和视觉效果同时收敛。

67.5 布料与软体效果演示

布料和软体演示应服务于参数理解。一个合格演示要让读者看到同一组参数如何影响悬挂布料、角色披风、软体碰撞和果冻形变。演示的目标并非追求复杂场景,而是把每个画面变化绑定到 solver state。

悬挂布料展示距离和弯曲约束

悬挂布料可以从一个规则网格开始,上边缘两个角设为 pin,重力向下,关闭风力和自碰撞。这个场景最适合观察 structural、shear 和 bend 约束。structural 变弱,布料会整体拉长;shear 变弱,网格会斜向扭曲;bend 变弱,褶皱会变尖且频率升高。

第一轮演示应只开启距离约束,观察布料下垂曲线和边长热图。第二轮开启 bend 约束,观察褶皱宽度变化。第三轮加入球体碰撞,观察接触区域是否局部拉伸。每一轮都记录最大约束残差和平均残差,这样画面变化就能回到求解状态。

布料悬挂还适合验证 timestep。使用同一组 stiffness 和 iteration,逐步增大 timestep,画面会从稳定下垂变成抖动、拉伸或穿透。这个演示能说明 timestep 属于联动参数,它会改变预测位移和约束残差规模。

角色披风展示 pin 权重和碰撞边界

角色披风在工程上更接近真实游戏问题。披风上边缘绑定到肩部或背部骨骼,身体用胶囊体或简化 mesh 做碰撞,布料求解后再驱动渲染网格。这个场景同时考验 pin、碰撞、damping 和 render binding。

演示时应先播放角色静止站立,再播放慢速转身,最后播放快速转身。静止站立用于验证 rest shape 和重力下垂;慢速转身用于验证披风惯性和碰撞贴合;快速转身用于暴露 pin 拉伸、碰撞抖动和速度回写问题。每一段都应打开 pin 权重热图和碰撞接触点。

披风的调参顺序建议从 pin 权重开始。肩部完全固定区域要窄,过渡区域要连续,自由区域保留足够节点。然后调碰撞体数量和厚度,最后调 damping。若先调 damping,披风会变得迟钝,接触和绑定问题会暂时隐藏,后续动画速度提高后仍会暴露。

果冻块展示 shape matching 和体积保持

果冻块演示可以使用一个立方体软体落到斜坡或球体上。simulation mesh 使用内部节点或四面体,render mesh 绑定到内部节点。这个场景用于观察压缩、回弹、体积保持和接触面稳定。

shape matching 权重低时,果冻块会保持较长时间的形变,碰撞后轮廓恢复慢。权重高时,果冻块会快速回到 rest shape,视觉接近橡胶块。阻尼低时,碰撞后会持续振荡;阻尼高时,回弹被快速衰减。volume preservation 控制压缩过程中的体积感,权重不足会让果冻块像薄壳塌陷。

果冻演示需要同时显示内部节点和表面 render mesh。若内部节点稳定但表面出现局部尖刺,应检查绑定权重和法线重算。若内部节点在接触面堆叠,应检查碰撞法线、substep 和体积约束。若整体漂移或旋转异常,应检查速度回写和 shape matching 的刚体模式处理。

最终演示要输出可复查的参数表

软体效果容易受场景尺度影响。演示时应记录节点数量、约束数量、timestep、substep、iteration、stiffness、damping、collision thickness、pin 权重范围和 GPU/CPU 耗时。没有这些参数,视觉结果很难复现,也无法判断同类场景是否适用。

可复查演示还应记录 debug 量:最大距离残差、平均距离残差、最大碰撞修正、接触数量、self-collision 候选数量、render binding 异常计数。视觉截图只能说明结果,debug 量能说明结果来自哪条状态路径。软体模拟的工程能力最终体现在这两者能互相解释。

本章从方程和 solver state 开始,经过网格约束、引擎集成、稳定性调参,落到布料和果冻演示。软体效果的稳定理解来自同一条链:材料被离散成 simulation mesh,约束把 rest state 变成可求解条件,solver 把预测位置投影到可接受状态,render binding 把模拟结果转成画面,debug view 把画面症状重新映射回状态。

最小自检任务

给定一个 16×16 布料网格,上边缘左右两个角为 pin,重力向下,使用 PBD 距离约束和弯曲约束,场景中有一个静止球体碰撞体。运行后出现三个现象:布料整体被拉长,球体接触区域有抖动,渲染表面在 pin 附近出现折线。请按本章的工程链说明应该检查哪些状态、每个状态对应哪类问题、参数调整顺序如何安排。

答案要点

先检查 simulation mesh 的平均边长比和最大边长比。若两者都偏高,说明距离约束整体残差大,应先提高 iteration 或 stiffness;若最大值集中在 pin 附近,应检查 pin 节点位移、pin 权重过渡和局部约束密度。timestep 较大时应增加 substep,使每次预测位移减小。

接着检查球体接触区域的碰撞修正量、接触法线方向和法线速度回写。持续抖动通常来自碰撞修正和距离约束在同一区域竞争,处理顺序是减小单次位移、增加 substep 或 iteration,再处理法线速度和摩擦。碰撞体半径、collision thickness 和节点间距也要一起查看。

最后检查 render mesh 与 simulation mesh 的绑定。pin 附近出现折线时,应显示 pin 权重热图和 render binding 权重。simulation mesh 稳定而 render mesh 折线明显,问题更可能在绑定权重、表面法线或切线更新。正确调整顺序是先让 simulation mesh 残差收敛,再修 pin 权重连续性,最后更新 render mesh 法线和绑定数据。

本章知识点总结

  • 软体状态:软体模拟把连续材料离散成节点、约束、碰撞接触、rest shape 和渲染绑定。
  • 模型边界:mass-spring 控制边长,FEM 控制材料应变,PBD 控制位置约束,shape matching 控制整体形状记忆。
  • 求解路径:实时 PBD 通常先预测位置,再生成约束,迭代投影,回写速度,最后驱动渲染网格。
  • 网格分层:simulation mesh 负责稳定求解,render mesh 负责视觉表面,二者通过绑定权重传递变形。
  • 弹簧类型:structural、shear、bend 约束分别控制拉伸、剪切和弯曲,调试时应分开观察残差。
  • 碰撞约束:稳定碰撞依赖 inside/outside 判定、接触法线、thickness、速度回写和约束迭代协商。
  • 引擎集成:软体系统要明确动画 pin、physics step、GPU buffer、render binding 和 debug view 的读写边界。
  • GPU 路径:GPU soft body 适合并行节点和约束处理,但共享节点写入、buffer 依赖和 readback 会影响性能。
  • 稳定参数:stiffness、iteration、timestep、substep 和 damping 要联合判断,单个参数无法解释全部画面症状。
  • 体积保持:果冻和体积软体需要 volume constraint、pressure 或 shape matching 维持空间占用和轮廓恢复。
  • 自碰撞成本:self-collision 的瓶颈来自候选对查找和接触约束冲突,debug view 要显示候选数量和修正量。
  • Pin 约束:pin 权重表达动画驱动边界,连续权重能减少固定区域附近的拉伸尖峰和表面折线。
  • 调试顺序:先查残差和速度,再查碰撞和 pin,最后查 render binding 和工具中的资源同步。
  • 演示交付:布料和果冻演示应同时输出视觉结果、参数表和 debug 量,保证效果能复查和迁移。