游戏物理引擎(一) 刚体动力学

游戏物理引擎(一) 刚体动力学

开篇

简单总结下游戏中物理引擎的原理.

游戏引擎里通常直接用开源的物理引擎库.比较常见的2D物理引擎有box2d,chipmunk,3D物理引擎有physx,bullet.

刚体的运动主要基于牛顿三大定律来模拟:

1.惯性 物体在不受力时,总是保持速度不变.

2.力,质量,加速度 力在物体上产生加速度,满足 \textbf{F}=m \textbf{a}

3.力的作用是相互的

基于牛顿三大定律,在计算机中来模拟刚体的运动,物理引擎来模拟的流程大致都是这样的:

对于每个物体,使用循环的方式来模拟:

物理引擎的循环

本篇文章就先来介绍受力分析,速度位置的动力学部分.下面的分析都假设刚体不受约束.

理想粒子的运动

先不考虑物体的形状和旋转,把物体当成理想粒子来对待,根据牛顿定律来循环计算物体的速度和位置:

dt = t_{i+1} - t_{i}

\textbf{v}(t_{i+1}) = \textbf{v}(t_{i}) + (\textbf{f}(t_{i})/m)dt

\textbf{p}(t_{i+1}) = \textbf{p}(t_{i}) + \textbf{v}(t_{i + 1})dt

这种计算方式叫欧拉方法,当然这样算出的结果是有一定误差的.

來看下把真实的值展开的結果,是这样的泰勒级数:

\textbf{p}(t_{i+1}) = \textbf{p}(t_{i}) + \textbf{p}^{'}(t_{i})dt + \textbf{p}^{''}(t_{i}) \frac{dt^2}{2!} + \textbf{p}^{'''}(t_{i})\frac{dt^3}{3!} + ...

欧拉方法实际上是一个近似值,不过对于一般的游戏来说,精度完全足够了.

物体重心

考虑物体形状和旋转的情况,物体受力后总是会绕着它的重心旋转.所以先来计算物体的中心,物体重心的计算公式是:

\bm r _{G}= \frac{\Sigma m_{i}\textbf{r}_{i}}{M} = \frac{1}{M} \int_{V}^{} \rho \bm r dV

对于均匀密度的物体,物体的重心就在物体的几何中心点上.

转动惯量

转动惯量表示物体沿某个轴旋转的难易程度,类似于物体的质量,通过这样的公式来计算:

I = {\Sigma m_{i}r_{i} ^ 2} = \int_{V}^{} \rho r^2 dV

理想细棒的转动惯量

类似于 \textbf{F}=m \textbf{a} ,物体旋转有 \bm{\tau}=I\bm\alpha , \bm\tau 指力矩( \bm\tau = \bm r \times \bm f ), \bm \alpha 指角加速度.

定义相对参考轴的角动量 \bm L = \Sigma \bm r_{i} \times \bm p_{i} .

类似于 \bm p = m \bm v , 也可以写成 \bm L = I \bm \omega .

类似直线运动的动能 K = \frac{1}{2} m v^{2} , 转动动能 K = \frac{1}{2} I \omega ^2 .

转动惯量的计算满足几个定理:

平行轴定理 如果一个质量为 m 的物件,以某条经过质心 A 点的直线为轴,其转动惯量为 I_{A} .在空间取点 B ,使得 AB 垂直于原本的轴.那么如果以经过 B ,平行于原本的轴的直线为轴, AB 的距离为 d ,则 I_B = I_A + md^2 .

垂直轴定理 如果一个平面物件,以该平面内两条互相垂直,交于 A 点的直线为轴,转动惯量分别为 I_{1} , I_{2} ,则它以过 A 点且垂直于该平面的直线为轴的转动惯量 I_{3}=I_{1}+I_{2} .

伸展定理 如果一个物件中的任一质点沿平行于某条轴的方向发生任意位移,该物件对该轴的转动惯量不变.

2D物体旋转计算

因为2D世界里物体的旋转只能沿着一个轴,所以计算起来非常简单.

先算出物体的转动惯量,如果刚体由多个刚体组合而成,先计算出每个刚体相对自己质心的转动惯量,然后用平行轴定理计算出每个刚体相对总重心的转动惯量,再将所有的转动惯量求和,就可以得到组合刚体的转动惯量.

然后直接用前面计算粒子速度和位移的方式来计算即可.

\omega(t_{i+1}) = \omega (t_{i}) + (\tau(t_{i})/I)dt

将物体的旋转角度表示为 q .

q(t_{i+1}) = q (t_{i}) + \omega(t_{i + 1})dt

惯性张量

在三维空间中任取一点 Q 及一个直角坐标系 Qxyz ,可以得到物体的惯性张量:

\bm I = \begin{pmatrix} I_{xx} & I_{xy} & I_{xz} \\ I_{yx} & I_{yy} & I_{yz} \\ I_{zx} & I_{zy} & I_{zz} \end{pmatrix}

对角元素 I_{xx} , I_{yy} , I_{zz} 是物体分别相对于 x , y , z 轴的转动惯量.

I_{xx} = \int_{}^{} (r_{y}^2 + r_{z} ^2 )dm

I_{yy} = \int_{}^{} (r_{x}^2 + r_{z} ^2 )dm

I_{zz} = \int_{}^{} (r_{x}^2 + r_{y} ^2 )dm

非对角元素叫做惯量积.

I_{xy} = I_{yx} = -\int_{}^{} (r_{x}r_{y} )dm

I_{xz} = I_{zx} = -\int_{}^{} (r_{x} r_{z} )dm

I_{yz} = I_{zy} = -\int_{}^{} (r_{y} r_{z} )dm

理想细棒的惯性张量


惯性张量的平行轴定理 从前面的平行轴定理可以推导出惯性张量的平行轴定理.假设刚体在质心 G 处的惯性张量为 I_{G} ,相对 G 点偏移为 \bm d 处的惯性张量为:

I_{xx} = I_{G,xx} + m ( d_{y}^2 + d_{z}^2)

I_{yy} = I_{G,yy} + m ( d_{x}^2 +d_{z}^2)

I_{zz} = I_{G,zz} + m (d_{x}^2 + d_{y}^2)

I_{xy} = I_{yx} = I_{G,xy} - m d_{x} d_{y}

I_{xz} = I_{zx} = I_{G,xz} - m d_{x} d_{z}

I_{yz} = I_{zy} = I_{G,yz} - m d_{y} d_{z}


惯性张量的旋转

已知某个坐标系下的惯性张量 \bm I 和新的坐标系(原点重合)的旋转矩阵 \bm R ,可以推导出新坐标系下的惯性张量:

\bm I^{'} = \bm {R IR^{T}}

结合以上两个方法,就可以计算出物体在任意坐标系下的惯性张量.

从惯性张量来计算转动惯量:

已知刚体在 Q 点及 Qxyz 坐标系的惯性张量为 \bm I ,任取一轴 QR , \eta 为沿着 QR 方向的单位向量,刚体在 QR 上的转动惯量为:

I_{QR} = \int_{}^{} \left| \bm \eta \times \bm r \right| ^2 dm

展开后得到:

{\displaystyle I_{QR}=\int \ [(\eta _{y}r_{z}-\eta _{z}r_{y})^{2}+(\eta _{x}r_{z}-\eta _{z}r_{x})^{2}+(\eta _{x}r_{y}-\eta _{y}r_{x})^{2}]\ dm\,\!}

{\displaystyle {\begin{aligned}I_{QR}=&\eta _{x}^{2}\int \ (r_{y}^{2}+r_{z}^{2})\ dm+\eta _{y}^{2}\int \ (r_{x}^{2}+r_{z}^{2})\ dm+\eta _{z}^{2}\int \ (r_{x}^{2}+r_{y}^{2})\ dm\\&-2\eta _{x}\eta _{y}\int \ r_{x}r_{y}\ dm-2\eta _{x}\eta _{z}\int \ r_{x}r_{z}\ dm-2\eta _{y}\eta _{z}\int \ r_{y}r_{z}\ dm\ \\\end{aligned}}\,\!}

和我们计算出来的惯性张量对应一下:

I_{QR}=\eta_x^2I_{xx}+\eta_y^2I_{yy}+\eta_z^2I_{zz}+2\eta_x\eta_yI_{xy}+2\eta_x\eta_zI_{xz}+2\eta_y\eta_zI_{yz}

这样,从惯性张量就可以算出沿任意轴的转动惯量.


前面讲转动惯量时,我们写了对应某个轴的角动量,有了惯性张量,我们把角动量也扩展一下.

已知物体相对某个点的惯性张量 \bm I ,相对该点的角动量为:

\bm L = \int_{}^{} \bm r \times \bm v \space dm = \int_{}^{} \bm r \times (\bm \omega \times \bm r ) \space dm

{\begin{aligned}L_{x}&=\int \ r_{y}({\boldsymbol {\omega }}\times \mathbf {r} )_{z}-r_{z}({\boldsymbol {\omega }}\times \mathbf {r} )_{y}\ dm\\&=\int \ r_{y}\omega _{x}r_{y}-r_{y}\omega _{y}r_{x}+r_{z}\omega _{x}r_{z}-r_{z}\omega _{z} r_{x}\ dm\\&=\int \ \omega _{x}(r_{y}^{2}+r_{z}^{2})-\omega _{y} r_{x} r_{y}-\omega _{z}r_{x} r_{z}\ dm\\&=\omega _{x}\int \ (r_{y}^{2}+r_{z}^{2})\ dm-\omega _{y}\int \ r_{x} r_{y}\ dm-\omega _{z}\int \ r_{x} r_{z}\ dm\ \end{aligned}}

同理可得

{\displaystyle L_{y}=-\omega _{x}\int \ r_{x} r_{y}\ dm+\omega _{y}\int \ (r_{x}^{2}+r_{z}^{2})\ dm-\omega _{z}\int \ r_{y} r_{z}\ dm\,\!}

{\displaystyle L_{z}=-\omega _{x}\int \ r_{x} r_{z}\ dm-\omega _{y}\int \ r_{y} r_{z} \ dm+\omega _{z}\int \ (r_{x}^{2}+r_{y}^{2})\ dm\,\!}

写成惯性张量的方式:

\bm L = \bm I \space \bm \omega

用惯性张量来表示转动动能:

\begin{aligned} K & = \frac{1}{2} \int_{}^{} \bm v ^2 \space dm \\&= \frac{1}{2} \int_{}^{} \bm (\bm \omega \times \bm r) \cdot (\bm \omega \times \bm r) \space dm \\& = \frac{1}{2} \bm \omega \cdot \int_{}^{} \bm r \times (\bm \omega \times \bm r) \space dm \\& = \frac{1}{2} \bm \omega \cdot \bm L \\&=\frac{1}{2} \bm \omega ^ T \bm I \bm \omega \end{aligned}

3D物体旋转计算

开始循环计算前,先来计算刚体在本地坐标系下的惯性张量 \bm I_{local} :

如果是规则几何体,直接根据公式计算即可.

如果是组合刚体,需要将子刚体的惯性张量求和: 先计算出组合刚体的重心点. 再计算出每个子刚体在自己空间坐标系下的惯性张量,通过惯性张量的旋转和平行轴定理,就可以算出子刚体在组合刚体的坐标系下相对重心点的惯性张量. 将所有子刚体的惯性张量各个分量求和,得到组合刚体的惯性张量.

每次循环时,先根据刚体的旋转状态算出此时在世界空间的惯性张量 \bm I , 再计算出物体受到的力矩 \bm \tau ,接着来算物体的角加速度和角速度:

\frac{d\bm L}{dt} = \bm I \space \frac{d \bm \omega}{dt} =\bm \tau

d \bm \omega = \bm I ^ {-1} \space \bm \tau \space dt

\bm \omega(t_{i+1}) = \bm \omega (t_{i}) + (\bm I ^ {-1}\bm \tau(t_{i}))dt


下面就是来根据角速度来更新物体的旋转角度.

如果物体通过旋转矩阵 \bm R 来保存旋转角度信息,来看下如何根据角速度 \bm \omega 更新旋转矩阵 \bm R :

设想一个跟随刚体不断旋转的向量 \bm l (只有方向),在恒定不变的 \bm \omega 角速度下,容易求得

\frac{d \bm l}{dt}= \bm \omega \times \bm l

定义矩阵 \bm \omega ^ * = \begin{pmatrix} 0 & -\omega_{z} & \omega_{y} \\ \omega_{z} & 0 & -\omega_{x} \\ -\omega_{y} & \omega_{x} & 0 \end{pmatrix}

这样,就可以把向量叉乘写成矩阵的方式:

\bm \omega \times \bm l = \bm \omega ^ * \space \bm l

旋转矩阵 \bm R 可以看成一个坐标系的三个基组成的矩阵 \bm R = \begin{pmatrix} \bm e_{1}^T & \bm e_{2}^T & \bm e_{3}^T \end{pmatrix} ,矩阵 \bm R 的旋转实际上就是分别将三个基旋转,因此我们得到

\frac{d \bm R}{dt} = \begin{pmatrix} \bm \omega \times \bm e_{1}^T &\bm \omega \times \bm e_{2}^T &\bm \omega \times \bm e_{3}^T \end{pmatrix} = \bm \omega ^ * \bm R

但是这种方式会不断累积误差,3X3的旋转矩阵会逐渐变得不仅仅是一个旋转矩阵,而是包含了多余的缩放误差,产生变形.

来看下用四元数来计算旋转, 非常直观.

一个沿轴向单位向量 \bm u 转动 \theta 的旋转,用四元数来表示

\bm q_{r} = (cos \frac{\theta}{2}, sin \frac{\theta}{2} u_{x}\bm i, sin \frac{\theta}{2} u_{y} \bm j, sin \frac{\theta}{2} u_{v} \bm k) = [cos \frac{\theta}{2}, sin \frac{\theta}{2} \bm u]

用四元数 \bm q 来表示刚体的旋转状态, 刚体在 dt 时间内沿着 \bm \omega 旋转了 |\bm \omega dt| 的角度,得到

\bm q(t_{i+1}) = \bm q(t_{i}) \ast [cos(\frac{|\bm \omega dt|}{2}), sin(\frac{|\bm \omega dt|}{2}) \frac{\bm \omega}{{|\bm \omega|}} ]

参考链接

toptal.com/game/video-g

cs.cmu.edu/~baraff/sigc

zh.wikipedia.org/wiki/%

box2d.org/publications/

编辑于 2020-06-01 22:10

文章被以下专栏收录