2D物理(19) The Projection Method

By pocaster

投影方法深度解析


I. 核心数学建模

1.1 投影方向理论

当检测到穿透时,需要沿碰撞法线( n )方向分解位移: \($ \Delta x = \lambda \cdot \mathbf{\hat{n}}\) 其中 ( \lambda ) 与穿透深度δ相关,满足: \(\Delta x_a + \Delta x_b = \delta \cdot \mathbf{\hat{n}}\)

1.2 质量比例分配原理

刚体质量影响位移分配权重: \(k = \frac{m_b}{m_a + m_b} \quad \Rightarrow \quad \begin{cases} \Delta x_a = -k \cdot \delta \cdot \mathbf{\hat{n}} \\ \Delta x_b = (1 -k) \cdot \delta \cdot \mathbf{\hat{n}} \end{cases}\) 确保动量守恒与视觉效果合理


II. 基础实现框架

2.1 位置投影公式

void PositionProjection(Rigidbody& a, Rigidbody& b, 
                        const Vec2& normal, float penetration) {
    const float slop = 0.01f;  // 允许的最小穿透修正
    const float percent = 0.8f; // 矫正比例
  
    float total_mass = a.inv_mass + b.inv_mass;
    if (total_mass <= 0) return;
  
    // 计算质量加权后的修正向量
    Vec2 correction = normal * (std::max(penetration - slop, 0.0f) / total_mass) * percent;
  
    a.position -= a.inv_mass * correction;
    b.position += b.inv_mass * correction;
}

2.2 速度投影公式

void VelocityProjection(Rigidbody& a, Rigidbody& b,
                        const Vec2& normal, float penetration) {
    // 基于恢复系数的速度响应
    const float e = std::min(a.restitution, b.restitution);
  
    Vec2 relative_vel = b.velocity - a.velocity;
    float vel_along_normal = relative_vel.Dot(normal);
  
    // 仅在接近时处理
    if (vel_along_normal > 0) return;
  
    float impulse_mag = -(1 + e) * vel_along_normal;
    impulse_mag /= (a.inv_mass + b.inv_mass);
  
    Vec2 impulse = impulse_mag * normal;
  
    a.velocity -= a.inv_mass * impulse;
    b.velocity += b.inv_mass * impulse;
}

III. 刚体旋转处理

3.1 接触点速度计算

考虑旋转带来的切线速度: \(v_i^{total} = v_i^{linear} + \omega_i \times r_i\) 代码实现:

Vec2 GetVelocityAt(const Rigidbody& body, const Vec2& contact_point) {
    return body.velocity + body.angular_velocity * 
          (contact_point - body.position).Perpendicular();
}

3.2 含扭矩的投影方程

void ApplyImpulseWithRotation(Rigidbody& body, const Vec2& impulse, 
                             const Vec2& contact_point) {
    body.velocity += impulse * body.inv_mass;
    body.angular_velocity += 
        (contact_point - body.position).Cross(impulse) * body.inv_inertia;
}

IV. 动态摩擦模型

4.1 切向速度计算

Vec2 tangent_vel = relative_vel - normal * relative_vel.Dot(normal);
float max_friction = friction_coeff * impulse_mag;
  
// 剪切冲量计算
Vec2 friction_impulse = tangent_vel.Truncate(max_friction);

4.2 切向冲量应用

a.velocity -= a.inv_mass * friction_impulse;
b.velocity += b.inv_mass * friction_impulse;

V. 迭代求解策略

5.1 多步迭代优化

for (int i = 0; i < solver_iterations; ++i) {
    for (auto& contact : contacts) {
        SolveVelocityConstraint(contact);
        SolvePositionConstraint(contact);
    }
}

典型配置:

  • 桌面应用:8-10次迭代
  • 移动端游戏:4-6次
  • 高精度仿真:20+次

VI. 矩阵形式表达

6.1 接触约束雅可比矩阵

\(J = \begin{bmatrix} -\mathbf{\hat{n}} & -(r_a \times \mathbf{\hat{n}}) & \mathbf{\hat{n}} & r_b \times \mathbf{\hat{n}} \end{bmatrix}\) 其中 ( r ) 为接触点距质心的向量

\[M_{\text{eff}} = \frac{1}{\frac{1}{m_a} + \frac{1}{m_b} + \frac{(r_a \times \mathbf{\hat{n}})^2}{I_a} + \frac{(r_b \times \mathbf{\hat{n}})^2}{I_b}}\]

VII. 实践技巧

7.1 数值稳定化处理

  • 速度阈值滤波
    if (abs(vel_along_normal) < 0.1f) {
      impulse *= 0.5f;  // 消除微小震荡
    }
    
  • 深度缓冲突变
    penetration = smooth_step(old_depth, new_depth, 0.2f);
    

7.2 混合刚体处理

刚体类型 碰撞响应策略
静态物体 仅移动动态物体
运动学物体 部分冲量传递
角色控制器 自定义响应曲线

VIII. 性能优化

8.1 Warm Starting

预热约束求解器:

// 使用上一帧冲量初始化
impulse *= persistence_factor; 

8.2 Batch 批次处理

SIMD优化后的代码架构:

__m128 impulse4 = _mm_mul_ps(vel4, inv_mass4);
// 同时处理4个接触点

IX. 进阶问题处理

9.1 堆叠稳定性

堆栈倒塌的解决方案:

  • 金字塔约束法:优先处理底层接触
  • 多级求解策略:每层单独迭代

9.2 多物体连环碰撞

使用传播标识:

bool needs_recheck = true;
while (needs_recheck) {
    needs_recheck = SolvePropagatedCollisions();
}

X. 验证指标

10.1 能量守恒检测

监测系统动能变化: \(\Delta E = \left| \frac{E_{\text{post}}}{E_{\text{pre}}} \right| \in [0.9,1.1]\)

10.2 穿透持续检测

设置碰撞维持计数器:

if (contact.persist_frames > 3) {
    ForceSeparation(); // 强制分离机制
}

设计准则 始终遵循能量流向原则:计算得到的冲量需要确保系统动能不会异常增加。建议基于误差项的平方量级调整迭代次数,实现自适应性调节。最终的解决方案应达到:

  • 位置正确性:帧末无视觉穿透
  • 速度连续性:碰撞瞬间速度突变符合预期
  • 能量稳定性:长时间仿真无能量异常积累
Tags: Gamedev Public AI