详解物理模拟中的核心力模型与代码实现
在物理模拟中,力的本质是通过 牛顿动力学方程组 驱动物体运动状态改变。以下分类解析游戏开发和物理引擎中常用的七大力学模型(含原理、公式、代码),并结合典型案例说明其应用场景。
I. 驱动力(Driving Force)
1. 基本原理
驱动力是人为定义的 “主动推力”,常用于模拟 发动机、推进器 等运动源。其方向由设计者控制,形式灵活(恒定、脉冲、随时间变化等)。
2. 经典公式
\(F_{\text{drive}} = \text{方向向量} \times \text{推力强度}\) (若物体有反冲效应,需通过质量分配合适加速度)
3. 代码实现
案例:操控一艘2D飞船的主引擎
// 船舶类扩展方法
void SpaceShip::ApplyThrust(Vector2 direction, float strength) {
Vector2 thrust = direction.Normalized() * strength;
body->ApplyForce(thrust); // body为飞船的刚体组件
}
4. 应用场景
- 星际飞船的加速飞行
- 赛车的油门控制系统
- 角色跳跃的腿部推动力
II. 线性阻力(Linear Damping)
1. 基本原理
线性阻力的强度与速度 呈正比,常用于模拟 低速 环境下的粘滞介质(如水中移动)的能量损耗。是简化后的阻尼模型。
2. 阻力公式
\(F_{\text{damp}} = -k_{\text{damp}} \cdot v\) 其中\(k_{\text{damp}}\)为阻尼系数,由介质特性决定。
3. 代码实现
void ApplyLinearDamping(RigidBody& body, float k_damp) {
Vector2 damping_force = body.velocity * (-k_damp);
body.ApplyForce(damping_force);
}
4. 参数权衡
- 低 \(k_{\text{damp}}\)(0.01~0.1):空气等稀薄介质(粒子特效)
- 高 \(k_{\text{damp}}\)(0.5~2):水下或高粘性液体
III. 流体阻力(Fluid Drag)
1. 基本原理
适用于 高速或高雷诺数 流体,阻力与速度 平方 成正比(参考牛顿阻力定律)。常用于模拟空气阻力、快速落体运动等。
2. 阻力公式
\(F_{\text{drag}} = -\frac{1}{2} \rho C_d A |v|\cdot v\) 其中:
- \(\rho\):流体密度
- \(C_d\):阻力系数(形状相关)
- \(A\):物体投影面积
3. 代码简化实现
忽略环境参数,通过调节整体比例系数 \(k_{\text{drag}}\) 适配场景:
void ApplyFluidDrag(RigidBody& body, float k_drag) {
Vector2 drag_force = body.velocity.Normalized() *
(body.velocity.LengthSquared() * -k_drag);
body.ApplyForce(drag_force);
}
4. 物理特征
- 阻力随速度指数级增长
- 与速度方向相反(作用向量中保留方向分量)
IV. 弹簧力(Spring Force)
1. 胡克定律(Hooke’s Law)
弹簧力是弹性形变的恢复力,满足 位移-力线性关系: \(F_{\text{spring}} = -k \cdot (x - x_{\text{rest}})\) 其中 \(k\) 为弹性系数,\(x_{\text{rest}}\) 为自然长度。
2. 阻尼修正
实际弹簧存在能量损耗(如空气摩擦、材料内耗),需增加速度相关阻尼项: \(F_{\text{spring}} = -k \cdot \Delta x - b \cdot v\) 其中 \(b\) 为阻尼系数,防止弹簧系统无限震荡。
3. 代码实现(双粒子弹簧)
void ApplySpringForce(Particle& p1, Particle& p2,
float k, float b, float rest_length) {
Vector2 delta_pos = p2.position - p1.position;
float curr_length = delta_pos.Length();
Vector2 dir = delta_pos / curr_length; // 单位向量
// 胡克定律计算弹性力
float stretch = curr_length - rest_length;
Vector2 spring_force = dir * (k * stretch);
// 计算相对速度(阻尼项)
Vector2 rel_velocity = p2.velocity - p1.velocity;
Vector2 damping_force = dir * (b * Dot(rel_velocity, dir));
// 总弹簧力
Vector2 total_force = spring_force + damping_force;
p1.ApplyForce(total_force);
p2.ApplyForce(-total_force);
}
4. 应用模式
- 布料模拟(粒子间的弹性连接)
- 软体动物的肌肉动力学
- 悬挂系统的减震器建模
V. 静摩擦力与动摩擦力
1. 静摩擦(Static Friction)
- 定义:阻碍物体间 相对滑动趋势 的力。
- 最大静摩擦:\(F_{\text{static\_max}} = \mu_s \cdot N\),其中 \(\mu_s\) 为静摩擦系数,\(N\) 为法向压力。
- 触发条件:外部切向力 \(F_{\text{ext}} < F_{\text{static\_max}}\) 时生效。
2. 动摩擦(Kinetic Friction)
- 定义:物体已发生滑动时的阻抗力。
- 公式:\(F_{\text{kinetic}} = \mu_k \cdot N\),\(\mu_k\) 通常小于 \(\mu_s\)。
3. 碰撞响应中的摩擦力模型(示例)
void ResolveCollisionWithFriction(RigidBody& body,
Vector2 collision_normal,
float mu_static, float mu_kinetic) {
// 1. 计算法向冲量(假设恢复系数已在冲量中处理)
Vector2 normal_impulse = ... ;
// 2. 计算切向速度
Vector2 tangent_vel = body.velocity -
Dot(body.velocity, normal) * normal;
// 3. 确定摩擦模式
float max_friction = mu_static * normal_impulse.Length();
if (tangent_vel.Length() < 1e-5) {
// 静摩擦:完全抵消切向速度
body.velocity -= tangent_vel;
} else {
// 动摩擦:施加反向力
Vector2 friction_force = -tangent_vel.Normalized() *
(mu_kinetic * normal_impulse.Length());
body.ApplyForce(friction_force);
}
}
4. 关键参数参考
| 材质组合 | \(\mu_{\text{static}}\) | \(\mu_{\text{kinetic}}\) | |——————|————————-|————————–| | 橡胶-混凝土 | 1.0 | 0.8 | | 钢铁-钢铁 | 0.6 | 0.4 | | 冰-冰 | 0.1 | 0.03 |
VI. 场力(Field Forces)—— 引力、磁力
1. 万有引力(Newtonian Gravity)
\(F_{\text{gravity}} = G \frac{m_1 m_2}{r^2} \cdot \hat{r}\) 在多体系统中,需循环遍历所有可能组合:
void ComputeGravityBetweenParticles(vector<Particle>& particles,
float G) {
for (int i = 0; i < particles.size(); ++i) {
for (int j = i+1; j < particles.size(); ++j) {
Vector2 delta = particles[j].position - particles[i].position;
float r_sq = delta.LengthSquared();
if (r_sq < 0.01f) continue; // 避免奇点
float force_mag = G * particles[i].mass * particles[j].mass / r_sq;
Vector2 force = delta.Normalized() * force_mag;
particles[i].ApplyForce(force);
particles[j].ApplyForce(-force);
}
}
}
2. 洛伦兹磁力(Lorentz Force)
\(F_{\text{magnetic}} = q (v \times B)\)
void ApplyMagneticForce(Particle& p, Vector2 B_field) {
Vector2 velocity = p.velocity;
float q = p.charge;
// 叉积结果(z分量的矢量形式)
Vector2 force = q * Vector2(velocity.y * B_field.z - velocity.z * B_field.y,
velocity.z * B_field.x - velocity.x * B_field.z);
p.ApplyForce(force);
}
VII. 冲击力(Impulse Forces)
1. 瞬态力的数学模型
冲击力表示短时间内的高强度力,数学上用 冲量(Impulse, J) 描述: \(J = \int F \, dt \quad \Rightarrow \quad \Delta v = \frac{J}{m}\)
2. 代码直接改变动量
void ApplyImpulse(RigidBody& body, Vector2 impulse) {
body.velocity += impulse * body.inv_mass; // 使用逆质量优化
}
3. 典型应用
- 子弹击中目标时传递冲量
- 跳跃瞬间的速度突变
- 爆炸冲击波的离散力作用
代码实战:综合力场演示
模拟一颗弹珠受 重力、空气阻力、地面摩擦与弹簧弹射 的物理效果:
class MarbleSimulation {
vector<Particle> marbles;
vector<Spring> springs; // 弹射弹簧系统
void Update(float dt) {
// 清除力
for (auto& m : marbles) m.force = Vector2(0,0);
// 应用全局力:重力 + 空气阻力
Vector2 gravity(0, 9.8f);
for (auto& m : marbles) {
m.ApplyForce(gravity * m.mass);
ApplyFluidDrag(m, 0.05f);
}
// 应用弹簧力(例如弹弓装置)
for (auto& s : springs) {
s.ApplyForceToMarbles();
}
// 地面摩擦计算(假设地面为y=0)
for (auto& m : marbles) {
if (m.position.y <= 0) {
ResolveGroundFriction(m, 0.4f, 0.2f);
}
}
// 更新所有状态
for (auto& m : marbles) m.Update(dt);
}
void ResolveGroundFriction(Particle& m, float mu_s, float mu_k) {
// 法向力(假设无弹跳)
float normal_force = m.mass * 9.8f;
// 切向速度
Vector2 tangent_vel(m.velocity.x, 0);
if (tangent_vel.Length() < 0.01f) { // 静摩擦
m.velocity.x = 0;
} else { // 动摩擦
Vector2 friction = -tangent_vel.Normalized() * (mu_k * normal_force);
m.ApplyForce(friction);
}
}
};
性能优化与误差控制
优化策略
- 力的作用域分区:将频繁计算的力(如粒子间引力)通过空间划分(四叉树/网格)减少遍历次数。
- 逆质量预计算:所有
mass
在初始化阶段转换为inv_mass
,避免逐帧除法。 - SIMD指令加速:使用SSE/AVX并行处理多个粒子的矢量运算。
通过精确建模各类力并合理组合,可创造出从逼真机械系统到奇幻魔法效果的全方位物理交互体验。实际开发中需在 物理准确性、计算效率 和 视觉表现力 之间寻找平衡。