我正在关注游戏物理引擎开发书。我理解分割碰撞:首先生成接触,然后根据其方向、位置、穿透等来解决它。我已经有
sphere - plane
和 sphere - sphere
碰撞正常工作。
但是,我在
box - plane
方面遇到了重大问题。这是当前外观的视频:https://imgur.com/a/YqEjCoK
请注意,立方体有一些非常可疑的行为。它永远不会落在一侧,并且永远卡在单个顶点或单侧上。
我想了解地面和盒子之间第一次发生碰撞的情况。请注意,虽然底面的顶点位于同一平面上,以相同的速度行进并同时均匀地撞击地面,但最终会向每个顶点施加不同的冲量,从而错误地产生旋转。
我关注的书找到具有最大穿透力的接触,对其应用旋转/线性变化,然后根据具有最大穿透力的接触的角度/线性分量调整所有其他接触的穿透力:
var velocityChange = [float3.zero, float3.zero]
var rotationChange = [float3.zero, float3.zero]
velocityIterationsUsed = 0
while (velocityIterationsUsed < velocityIterations) {
var max = velocityEpsilon
var index = contacts.count
// find contact with greatest "penetration"
for i in 0 ..< contacts.count {
if (contacts[i].desiredDeltaVelocity > max) {
max = contacts[i].desiredDeltaVelocity
index = i
}
}
if (index == contacts.count) {
break
}
// Match the awake state at the contact
contacts[index].matchAwakeState()
// Apply velocity change to the greatest penetration contact
contacts[index].applyVelocityChange(
velocityChange: &velocityChange,
rotationChange: &rotationChange
)
// Loop all generated contacts
for i in 0 ..< contacts.count {
// Check each body in the contact
for b in 0 ..< 2 {
if (contacts[i].bodies[b] != nil) {
// Check for a match with each body in the newly resolved contact
for d in 0 ..< 2 {
if (contacts[i].bodies[b] == contacts[index].bodies[d]) {
let deltaVel = velocityChange[d] + cross(rotationChange[d], contacts[i].relativeContactPosition[b])
print("""
=======================
contact idx \(i):
velocityChange: \(velocityChange[d])
rotationChange: \(rotationChange[d])
relativeContactPosition: (\(contacts[i].relativeContactPosition[b].x) \(contacts[i].relativeContactPosition[b].y) \(contacts[i].relativeContactPosition[b].z))
deltaVel: (\(deltaVel.x) \(deltaVel.y) \(deltaVel.z))
""")
// deltaVel does not make sense on first impact
}
}
}
}
}
}
velocityIterationsUsed += 1
}
注意
deltaVel
变量,它根据线性和角度分量重建每个接触速度偏移。在第一次盒子撞击时,由于底面的 4 个顶点同时以相同的速度均匀撞击地面,deltaVel
报告了非常奇怪的值:
=======================
contact idx 0:
velocityChange: SIMD3<Float>(0.0, 3.1689641, 0.0)
rotationChange: SIMD3<Float>(0.26407933, 0.0, 0.2640793)
relativeContactPosition: (0.5 -0.5010185 -0.50000006)
deltaVel: (0.13230862 3.4330435 -0.13230863)
=======================
contact idx 1:
velocityChange: SIMD3<Float>(0.0, 3.1689641, 0.0)
rotationChange: SIMD3<Float>(0.26407933, 0.0, 0.2640793)
relativeContactPosition: (-0.5 -0.5010185 -0.50000006)
deltaVel: (0.13230862 3.1689641 -0.13230863)
=======================
contact idx 2:
velocityChange: SIMD3<Float>(0.0, 3.1689641, 0.0)
rotationChange: SIMD3<Float>(0.26407933, 0.0, 0.2640793)
relativeContactPosition: (0.5 -0.5010185 0.4999999)
deltaVel: (0.13230862 3.1689641 -0.13230863)
=======================
contact idx 3:
velocityChange: SIMD3<Float>(0.0, 3.1689641, 0.0)
rotationChange: SIMD3<Float>(0.26407933, 0.0, 0.2640793)
relativeContactPosition: (-0.5 -0.5010185 0.4999999)
deltaVel: (0.13230862 2.9048848 -0.13230863)
注意这条线
let deltaVel = velocityChange[d] + cross(rotationChange[d], contacts[i].relativeContactPosition[b])
它如何为每个顶点产生几乎相同的
deltaVel
,除了 Y 分量随着每个下一个顶点逐渐减小?但为什么会减少呢?我在 matlab 中对每个顶点做了这个方程,它确实是正确的。据我了解,它应该沿着所有顶点相等(因为它们位于同一平面上)?这样它们都会以相同的 Y 速度反弹,并且不会发生旋转。
如您所见,迭代之间唯一不同的值是
contact[i].relativeContactPosition
。由于盒子的宽度、高度和深度均为 1,因此每个轴上的这些值约为 +/- 0.5。
// vertex #1
let linearChange0 = float3(0, 3.1854, 0.0)
let angularChange0 = float3(0.265, 0, 0.265)
let relativeContactPos0 = float3(-0.5, -0.5265012, -0.50000006)
linearChange0 + cross(angularChange0, relativeContactPos0) = (0.139522806, 3.18540001, -0.139522806)
// vertex #2
let linearChange1 = float3(0, 3.1854, 0.0)
let angularChange1 = float3(0.265, 0, 0.265)
let relativeContactPos1 = float3(-0.5, -0.5265012, 0.4999999)
linearChange1 + cross(angularChange1, relativeContactPos1) = (0.139522806, 2.92040014, -0.139522806)
鉴于两个方程之间的唯一区别是
relativeContactPos
值的 Z 分量,为什么生成的 Y 值不同?如何让它们统一?
TLDR:上面所附的 IMGUR 视频中的每个顶点都会获得不同的 Y 速度偏移,即使它们都位于同一平面上,以相同的速度行进。该怎么办?
考虑到两个方程之间的唯一区别是 Z
值的分量,为什么得到的Y 价值不同?如何让它们统一?relativeContactPos
因为你正在计算叉积。只有离轴项对所得轴项有贡献:
您采用的叉积是正确的——毕竟,扭矩是叉积——但显然采用的是错误的叉积。
计算出的速度变化值 (
velocityChange
) 看起来是正确的,位置矢量 (relativeContactPos
) 也是如此。
我们需要的是每个顶点上的扭矩产生的冲量,我们知道它应该采用以下形式
由于扭矩由下式给出
由于冲量等于力乘以时间,我们可以为扭矩引起的冲量构建以下方程:
但是由于加速度等于速度随时间的变化,这变成了
因此,叉积应该是
cross(contacts[i].relativeContactPosition[b], velocityChange[d])
这将导致完全偏移的向量如下所示(自上而下视图)
但请注意,这不处理脉冲方面,仅处理扭矩方面。扭矩的大小将根据质量和时间增量来缩放。在这种正面碰撞的特定情况下,这并不重要,因为产生的扭矩相互抵消,但在其他情况下,它们的脉冲计算会被错误地缩放。
由于您没有包含完成冲量计算所需的必要信息,因此无法确定您需要做什么,但从广义上讲,扭矩对速度变化的完整贡献将计算为:
cross(contacts[i].relativeContactPosition[b], velocityChange[d]) * deltaT * mass