真实模拟地板上的球体旋转

For*_*ick 1 3d geometry physics

我正在尝试模拟一个在地板上滚动的球体。对于模拟,我使用的是 Flash AS3 的旧版 Papervision3D 库,但这实际上并不重要,这是一个纯粹的几何问题。

假设我有一个 Sphere3D 对象,我可以为其设置rotationX、rotationY 和rotationZ 属性,如何计算该球体在地板上滚动的每个轴的旋转?

例如,我们假设球体处于静止状态。现在它向右滚动 1 米。如果我从顶部看这个球体 - 我想将它绕 Z 轴旋转 90 度。然后球体应该沿着地板“向下”滚动,所以我想围绕 X 轴旋转它,但这个问题是,与此同时,当我沿着 Z 轴旋转球体时,X 轴会自行旋转。

我该如何解决这个问题?

谢谢

Spe*_*tre 5

如果没有滑动则:

  1. 旋转轴

    将平行于您的地板并垂直于您的运动。所以你可以利用叉积来得到它。让:


    n- 地板法线向量
    t- 与地板平行的运动方向(切线)
    b- 我们的旋转轴(副法线)

    所以我们可以将其计算为:

    b = cross(t,n) // cross product create perpendicular vector to both operands
    t /= |t|       // normalize to unit vector
    b /= |b|       // normalize to unit vector
    n /= |n|       // normalize to unit vector
    
    Run Code Online (Sandbox Code Playgroud)
  2. 旋转速度

    这可以从弧长和速度导出vel [unit/s]。所以如果我们的球体是半径r那么:

    ang*r = vel*t
    ang = vel*t/r // t=1.0 sec
    omg = vel/r   // [rad/sec]
    
    Run Code Online (Sandbox Code Playgroud)

    所以我们需要每秒旋转我们的球体omg

  3. 旋转数学

    欧拉角(你的顺序旋转 X、Y、Z)是我能想到的最糟糕的事情,因为它们会导致奇点和奇怪的东西,使这个简单的例子实现起来可怕的噩梦。您是否在游戏或任何 3D 引擎中看到过,您突然无法按预期观看,或随机旋转,直到您以不同方式移动/旋转或突然旋转 180 度...?这是欧拉角奇点,没有适当的处理......

    四元数对大多数人(包括我)来说有点陌生,因为它们不像我们想象的那样工作。IIRC 您可以将它们视为计算 3x3 3D 旋转矩阵的有效方法,并且所需的测角函数较少。由于我们现在的计算能力与 20 年前相比有很大不同,如果您根本不了解它们,那么选择它们就没有多大意义。无论如何,它们还有另一个仍然相关的优点,例如您可以在旋转之间进行插值等。

    4x4 齐次变换矩阵是您的最佳选择。由于它们的几何表示与人类抽象思维兼容(您可以想象它是做什么以及如何完成的,因此您可以构建自己的矩阵,而不是将它们视为一堆无意义的数字)。

    我强烈建议从 3D 4x4 齐次变换矩阵开始。所以这个答案的其余部分将针对他们。

  4. 旋转

    现在我知道有两种方法可以实现轮换。可以使用Rodrigues_rotation_formula并将其编码为变换矩阵,也可以简单地构建您自己的旋转矩阵来表示与地板对齐的球体。运动方向和旋转轴。

    后者要简单得多,我们可以直接执行,因为我们已经知道所需的 3 个基向量 ( t,b,n)。剩下的只是应该知道的球体位置。

    因此,首先创建一个变换矩阵(假设 OpenGL 表示法):

    | tx bx nx x0 |
    | ty by ny y0 |
    | tz bz nz z0 |
    |  0  0  0  1 |
    
    Run Code Online (Sandbox Code Playgroud)

    x0,y0,z0球体的起始位置与网格对齐。因此,如果网格的中心点是(0,0,0)将球体放置r在地板上方......

    现在,只需将每个经过的时间dt [sec](如计时器)将该矩阵乘以围绕轴(如我们的旋转轴)和角度的增量旋转矩阵ybomg*dt [rad]

    我们还需要平移我们的球体,t*vel*dt只需将此向量添加到矩阵位置或将我们的矩阵乘以:

    | 1 0 0 tx*vel*dt |
    | 0 1 0 ty*vel*dt |
    | 0 0 1 tz*vel*dt |
    | 0 0 0         1 |
    
    Run Code Online (Sandbox Code Playgroud)

    并且使用我们生成的矩阵再次渲染场景...这种方法很好,因为您可以随时更改运动方向(您只需记住位置并使用新向量更改矩阵的内部 3x3 旋转部分t,b,n

    然而,这种累积矩阵有一个缺点,即随着时间的推移,这种累积矩阵会降低精度(因为我们在不重置的情况下一遍又一遍地对其进行浮点数乘法),因此矩阵会随着时间的推移而变形。t,b,n为了避免这种情况,时不时地重新计算和设置矩阵的一部分就足够了。我习惯于在 64 位double变量精度上每旋转 128 次。它也可以自动完成(当您没有关于轴的事先信息时)我这样做:

此外,使用具有不同符号的矩阵(行/列主阶、乘法阶),这可能会稍微影响方程(乘法的相反顺序和/或使用逆矩阵代替)。

现在,如果您的 3D 引擎不支持矩阵(这种情况极不可能),您需要将结果矩阵转换回欧拉角。这可以通过测角学来实现,但为此您需要知道角度的顺序。

如果是滑动,则需要按相反的顺序进行。因此,首先计算旋转,然后根据带有底板和惯性的抓地力计算平移方向。这是更复杂和纯粹的物理学......

[Edit1] rotundus风格简单OpenGL/C++/VCL示例

预览

这里是使用累积矩阵的简单控制示例(不保留精度):

ang*r = vel*t
ang = vel*t/r // t=1.0 sec
omg = vel/r   // [rad/sec]
Run Code Online (Sandbox Code Playgroud)

它是一个空的单一形式 VCL 应用程序,上面有一个 20 毫秒计时器。为了移植到您的环境,只需忽略 VCL 内容,模仿应用程序的相关事件并将渲染移植到您的组件/样式/api。唯一重要的东西只是sphere标记为的类// movement和计时器事件Timer1Timer(TObject *Sender)。剩下的只是渲染和键盘处理......我怀疑你已经自己处理了......

预览显示我用箭头控制球时的移动:

up/down - accelerate/decelerate
left/right - turn left/right in respect to forward direction around normal to surface
Run Code Online (Sandbox Code Playgroud)

这里我使用的纹理(在 mspaint 中手工绘制,因此它可能不是像素完美对称......)

质地

我的gl_simple.h可以在这里找到: