盒子与平面相撞 - 奇怪的旋转

Colliding a box with a plane - strange rotation

提问人:Georgi B. Nikolov 提问时间:11/13/2023 更新时间:11/13/2023 访问量:23

问:

我正在关注《游戏物理引擎开发》一书。我理解拆分碰撞:首先生成一个接触点,然后根据其方向、位置、穿透等解决它。我已经有并且碰撞工作正常。sphere - planesphere - sphere

但是,我遇到了重大问题。以下是它当前外观的视频: https://imgur.com/a/YqEjCoKbox - plane

请注意,多维数据集有一些非常可疑的行为。它永远不会停留在一侧,并且永远停留在单个顶点或单个侧面。

我想了解地面和箱子之间第一次发生撞击的时间。请注意,尽管底面的顶点位于同一平面上,以相同的速度行进并同时均匀地撞击地面,但最终会对每个顶点施加不同的冲量,并且错误地产生旋转。

我接下来的书找到了穿透力最大的接触点,对其应用旋转/线性变化,然后根据穿透力最大的接触的角度/线性分量调整所有其他接触点的穿透力:

  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

}

请注意基于线性和角度分量重建每个接触速度偏移的变量。在第一次箱子撞击时,当底面的 4 个顶点均匀地、同时以相同的速度撞击地面时,报告了非常奇怪的值:deltaVeldeltaVel

=======================
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])

它如何为每个顶点产生几乎相同的结果,除了 Y 分量随着每个下一个顶点逐渐减少?但为什么它会减少呢?我在 matlab 中为每个顶点做了这个方程式,它确实是正确的。我的理解是,它应该在所有顶点上相等(因为它们位于同一平面上)?这样,它们都将以相同的 Y 速度反弹,并且不会发生旋转。deltaVel

如您所见,迭代之间唯一不同的值是 .由于盒子的宽度、高度和深度为 1,因此每个轴的这些值约为 +/- 0.5。contact[i].relativeContactPosition

// 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)

鉴于两个等式之间的唯一区别是值的 Z 分量,为什么生成的 Y 值不同?如何使它们统一?relativeContactPos

TLDR:上面所附的 IMGUR 视频中的每个顶点都具有不同的 Y 速度偏移,即使它们都位于同一平面上,以相同的速度行进。该怎么办?

游戏物理物理 物理 引擎

评论


答:

1赞 William Miller 11/27/2023 #1

鉴于两个方程之间的唯一区别是 Z 分量,为什么得到的 Y 价值不同?如何使它们统一?relativeContactPos

因为你正在采取交叉产品。只有离轴项对生成的轴项有贡献:

enter image description here

你取的是叉积是正确的——毕竟扭矩是叉积——但显然取错了叉积。

为速度变化计算的值 () 是正确的,位置矢量 () 也是如此。velocityChangerelativeContactPos

我们需要的是每个顶点上由于扭矩引起的冲量,我们知道它应该采用以下形式

enter image description here

由于扭矩由下式给出

enter image description here

由于冲量等于力乘以时间,我们可以为扭矩引起的冲量构造这个方程:

enter image description here

但是由于加速度等于速度随时间的变化,因此这变成了

enter image description here

因此,叉积应为

cross(contacts[i].relativeContactPosition[b], velocityChange[d])

这将导致如下所示的完全偏移向量(自上而下视图)

enter image description here

但请注意,这不处理脉冲方面,只处理扭矩方面。扭矩的大小将按质量和时间增量进行缩放。在这种面对面碰撞的特定情况下,这并不重要,因为产生的扭矩会抵消,但在其他情况下,它们的冲量计算会错误地缩放。

由于您尚未包含完成脉冲计算所需的必要信息,因此无法确定您需要做什么,但在广义行程中,扭矩对速度变化的完整贡献将计算为:

cross(contacts[i].relativeContactPosition[b], velocityChange[d]) * deltaT * mass