游戏数学之linear algebra(二)

游戏数学之linear algebra(二)

本文转自我的公众号—游戏开发手账
转载请标明出处

来看看游戏世界中的物体是怎么旋转的。

​ ——我说的

上一篇提到了使用矩阵变换来帮助旋转,并且4 x 4的仿射矩阵还能完成包括旋转、平移、缩放和切边及其组合在内的仿射变换。

功能确实是能完成,但是总觉得有些冗余,因为旋转明明只有三个自由度(偏航角Yaw、俯仰角Pitch、滚动角Roll),却用了9个浮点值去表示旋转部分,并且用矢量矩阵乘法来进行矢量的旋转需要进行三次点积计算。
[ r x r y r z ] [ 1 0 0 0 cos ⁡ Φ sin ⁡ Φ 0 − sin ⁡ Φ cos ⁡ Φ ] \begin{bmatrix} r_{x} & r_{y} & r_{z} \end{bmatrix} \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos Φ & \sin Φ \\ 0 & -\sin Φ & \cos Φ \\ \end{bmatrix} [rxryrz]1000cosΦsinΦ0sinΦcosΦ
有没有什么更好的方法来减少浮点值的存储并且加速🚀旋转计算呢?

四元数,它似乎听到了你的抱怨。

四元数

先一句话概括四元数:四元数利用旋转轴方向和旋转角度来表达三维旋转,四个元素中,三个矢量一个标量。

具体来说,单位四元数的构成为三维矢量加上第四维的标量,矢量部分是旋转的单位轴乘以旋转半角的正弦值,标量部分是旋转半角余弦值,公式如下👇。
q = [ q ⃗ v q s ] = [ a ⃗ sin ⁡ θ / 2 cos ⁡ θ / 2 ] = [ q x q y q z q w ] q = \begin{bmatrix}\vec q_{v}& q_{s}\end{bmatrix} = \begin{bmatrix} \vec a \sin θ/2 & \cos θ/2\end{bmatrix} = \begin{bmatrix}q_{x}& q_{y}& q_{z} & q_{w}\end{bmatrix} q=[q vqs]=[a sinθ/2cosθ/2]=[qxqyqzqw]
a ⃗ \vec a a 就是旋转轴方向的单位矢量,θ为旋转角度(所以根据公式可以看到使用的是旋转半角θ/2)。

两个旋转的合成旋转用四元数的乘积来表示,对于旋转m和旋转n,mn表示旋转n之后再旋转m,它们的乘法称为格拉斯曼积(Grassmann product),计算公式如下👇。
m n = ( m s n ⃗ v + n s m ⃗ v + m ⃗ v ⋅ n ⃗ v ) ( m s n s − m ⃗ v ⋅ n ⃗ v ) mn = (m_{s}\vec n_{v} + n_{s}\vec m_{v}+ \vec m_{v} \cdot \vec n_{v}) (m_{s}n_{s} - \vec m_{v}\cdot \vec n_{v}) mn=(msn v+nsm v+m vn v)(msnsm vn v)
四元数是单位长度的,这给计算四元数的逆带来了相对更快的速度,因为四元数的共轭为 q ∗ = [ − q ⃗ v q s ] q^{*} = \begin{bmatrix} -\vec q_{v}& q_{s} \end{bmatrix} q=[q vqs],并且四元数的逆的计算为 q − 1 = q ∗ / ∣ q ∣ 2 q^{-1} = q^{*} / |q|^{2} q1=q/q2,四元素的单位长度特性使得逆的计算无需除以模平方。

好了,到了具体的问题:怎样利用上面提到的四元数去旋转一个矢量?

其实就两步:

  • 把你要旋转的矢量写成对应的四元数形式
  • 左乘 q q q,右乘 q − 1 q^{-1} q1

把矢量写成四元数形式只需要在他后面的标量位置添0即可,也就是这样:
v = [ v ⃗ 0 0 ] = [ v x v y v z 0 ] v= \begin{bmatrix}\vec v_{0}& 0\end{bmatrix} = \begin{bmatrix}v_{x}& v_{y}& v_{z}& 0\end{bmatrix} v=[v 00]=[vxvyvz0]
左乘 q q q,右乘 q − 1 q^{-1} q1也就是这样: v ′ = q v q − 1 = q v q ∗ v' = qvq^{-1} = qvq^{*} v=qvq1=qvq(上面已经提到了,因为四元数是单位长度的,所以 q ∗ = q − 1 q^{*} = q^{-1} q=q1)。

再具体一点的例子,上一篇提到了模型空间向世界空间的转换,也涉及到了旋转,那么求一下飞机飞行方向的单位矢量?

飞机飞行的方向,自然就是它的向前方向,在飞机的模型空间中,它的向前方向是 [ 0 0 1 ] \begin{bmatrix}0& 0& 1\end{bmatrix} [001],把模型空间的这个方向(先转换成对应的四元数 [ 0 0 1 0 ] \begin{bmatrix}0& 0& 1& 0\end{bmatrix} [0010])转换至世界空间就需要用上面的方法左乘代表飞机定向的四元数 q q q,右乘其逆 q − 1 q^{-1} q1,则从模型空间的方向 F ⃗ M \vec F_{M} F M向世界空间方向 F ⃗ W \vec F_{W} F W的转变为:
F ⃗ W = q F ⃗ M q − 1 \vec F_{W} = q\vec F_{M}q^{-1} F W=qF Mq1

四元数和旋转矩阵

既然四元数扮演着和旋转矩阵相同的功能,那很容易联想到它们二者之间具有等价转换的关系,由于篇幅关系,这里仅给出从四元数转换至等价的旋转矩阵的结果。

设四元数q = [x y z w](按照上文习惯,标量放在第四位,有些学术文章中是放在首位的),对应的旋转矩阵R如👇。
[ 1 − 2 y 2 − 2 z 2 2 x y + 2 w z 2 x z − 2 w y 2 x y − 2 w z 1 − 2 x 2 − 2 z 2 2 y z + 2 w x 2 x z + 2 w y 2 y z − 2 w x 1 − 2 x 2 − 2 y 2 ] \begin{bmatrix} 1-2y^{2}-2z^{2} & 2xy+2wz & 2xz-2wy \\ 2xy-2wz & 1-2x^{2}-2z^{2} & 2yz+2wx\\ 2xz+2wy& 2yz-2wx& 1-2x^{2}-2y^{2}\end{bmatrix} 12y22z22xy2wz2xz+2wy2xy+2wz12x22z22yz2wx2xz2wy2yz+2wx12x22y2
旋转插值

按照惯例,先放结论:四元数相比旋转矩阵,能更轻易地用LERP或者SLERP运算进行旋转插值。

游戏场景中,旋转的插值几乎无处不在,最快速简单的方法就是套用四维矢量的线性插值(LERP)到四元数,据此,在A旋转至B点的过程中的第β个百分点的位置的四元数为:
q L E R P = n o r m a l i z e ( ( 1 − β ) q A + β q B ∣ ( 1 − β ) q A + β q B ∣ ) q_{LERP} = normalize(\frac{(1-\beta)q_{A}+\beta q_{B}}{\lvert (1-\beta)q_{A}+\beta q_{B}\rvert}) qLERP=normalize((1β)qA+βqB(1β)qA+βqB)
这里在进行LERP后要注意进行归一化,因为LERP运算对四元数不保持矢量长度。

不过LERP虽然简单,但却有一定的缺陷,因为四元数实际上是四维超球上的点,LERP做的插值实际上是在超球的弦上进行,而不是在超球的面的大圆上进行,所以这种插值并不均匀,实际场景中的表现为:旋转在始末两端比较缓慢,而在动画的中间相对更快。

解决此问题的方法是使用球面线性插值(SLERP),SLERP在LERP基础上添加正余弦使得插值在四维超球面的大圆上,公式如👇(对照上面LERP公式一同食用效果更佳):
q S L E R P = sin ⁡ ( 1 − β ) θ sin ⁡ θ + sin ⁡ β θ sin ⁡ θ q B q_{SLERP} = \frac{\sin (1-\beta)\theta}{\sin \theta}+\frac{\sin\beta\theta}{\sin\theta}q_{B} qSLERP=sinθsin(1β)θ+sinθsinβθqB
这里两四元数之间的夹角的余弦值等于它们的四维点积:
cos ⁡ θ = p ⋅ q = p x q x + p y q y + p z q z + p w q w \cos θ = p \cdot q = p_{x}q_{x} + p_{y}q_{y} + p_{z}q_{z} + p_{w}q_{w} cosθ=pq=pxqx+pyqy+pzqz+pwqw
求出 c o s θ cosθ cosθ后便可以用反三角函数求出θ。

最后简单总结一下
  • 用四元数代替旋转矩阵进行旋转可以减少存储上的冗余,加快计算
  • 四元数相比于旋转矩阵,能更容易完成游戏中的旋转插值操作(使用LERP / SLERP)
  • SLERP能够解决LERP的非匀速旋转的问题(但实际中其实更多还是采用LERP,因为这种非匀速还可接受,而且SLERP计算开销大)
  • 如果拿轴角这种表示方式(这一部分较为简单,文中没有提及)和四元数进行比较,轴角表示会更加直观,同样没有存储上的冗余,但是不能像四元数一样进行简单插值,并且轴角形式的旋转不能直接施加于点和矢量,而四元数可通过 q v q − 1 qvq^{-1} qvq1方式完成

原文链接:游戏数学之linear algebra(二)
相关文章:游戏数学之linear algebra(一)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲倾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值