摘要:
1.基本的空间数学概念
向量运算和点乘和叉乘很重要。
求多个单位向量的平均向量,是将这些向量求和然后单位化。非单位向量之间很难求平均,因为向量除了大小还有方向。
向量齐次化为:(x,y,z,w)其中w用来进行平移,w一般是1,如果不唯一那么转换后需要变换为1例如透视投影钟,向量齐次化可以代表空间点和向量,当w= 0时候是向量,w非0是代表空间点,因为空间点有大小方向和位置。
矩阵本质行是新坐标系相对于上一个坐标系的基向量用于对一个点在原来各个轴方向的分解,列是对一个点在原来各个轴的汇总,所以矩阵在空间几何上的解释是新的相对于原来坐标系的新的坐标轴,也就是对空间中的点进行重新分解和组合从而实现对点的变换,矩阵就是一个变换。矩阵插值会产生蠕变。
欧拉角,是对物体的坐标轴进行变换,但是还是用旧的坐标轴来衡量的,所以物体变换取反,和变换轴一样的顺序但相反的量变换物体即可,欧拉角有万向锁和二义性问题,用限制欧拉角可以解决,但是欧拉角插值不平滑。欧拉角物体变换到惯性,和惯性变换到物体才是相反的顺序相反的量进行变换,因为他们之间是相互逆变换。
例如:
欧拉角惯性到物体变换为head pitch back:旋转角度为:h p b,那么物体旋转方位计算为:M(-h) * M(-p) * M(-b), 如果是欧拉角那么直接用旋转向量标记,如果是四元数那么用四元数方式进行变换。
若从惯性到物体变换来计算物体到惯性的逆变换,那么变换为:M(-b)^-1 M(-p)^-1 M(-h)^-1, 也就是roll pitch yaw变换,yaw相对于当前物体坐标系的z轴,head是惯性坐标系的y轴 。
四元数中的旋转四元数,将旋转角度和旋转轴放入到了四元数中,但是旋转角度是半角因为另一半的角放置到了该四元数的 逆(共轭)四元数中,四元数变换一个顶点在标准乘法时候[w1,v1]*[w2,v2) = (w1*w2 - v1.v2 w1*V2 + w2*V1 + V1xV2),用q四元数变换向量p公式是:p' = qpq-1。
本引擎定义的乘法的四元数乘法为[w1,v1]*[w2,v2) = (w1*w2 - v1.v2 w1*V2 + w2*V1 + V2xV1),用q四元数变换向量p公式是:p' = q-1pq,连续进行a,b四元数变换得到:(ab)-1p(ab),等价于标准乘法定义下的(ba)p(ba)-1,所以如果需要连续四元数变换那么改变四元数标准乘法规则更好。使用四元数连续a,接着b变换,在标准乘法下先进行ba乘法(反正a,b都是已知,如OGRE中直接即可)记录整个变换,真正实施变换时候才对向量进行ba * p * (ba)-1变换,或者将ba转换为矩阵M(矩阵包含了ba,(ba)-1信息)包含了所有变换信息,向量进行p*M变换即可;如果是非标准本引擎乘法那么进行ab乘法记录整个变换。OGRE中用了标准乘法,但是父节点变换a和子节点变换b?他们之间的连续变换依然是用a*b进行的,每次子节点渲染时候,需要递归到根节点进行变换刚好是从根节点向下连续变换。
因为右手坐标系中顺时针四元数变换为:a-1pa,连续变换为: (ab)-1 p(ab),所以是父节点乘以子节点刚好实现连续四元数旋转变换。
记得右手坐标系顺时针变换,因为相对于左手坐标系的顺时针变换apa-1,因为左右手变换了乘法规则还是不变,a和a-1刚好是互逆的,所以到右手中就变成了a-1pa。
且在左手坐标系中逆时针旋转也是a-1pa,在右手坐标系中逆时针为:apa-1,所以对于四元数变换在标准乘法下在右手坐标系中顺时针下刚好是可以按照先后连续变换的(ab)-1p(ab)。
左手坐标系下顺时针变换如果用标准四元数连续乘法就要变为(ba) p(ba)-1。如果左手坐标系下想要获得按照先后乘法顺序就要改变标准乘法,例如:
[w1,v1]*[w2,v2) = (w1*w2 - v1.v2 w1*V2 + w2*V1 + V2xV1) 才能用(ab)-1p(ab)实现连续变换。
四元数变换向量,一定要小心求逆,四元数的求逆都是唯一的(w, -x, -y, -z)无论是在变换旋转方向,还是变换轴下。
2.物体变换等价于相反的量变换坐标系(如果都是用旧坐标系表达,则用相反量,相同的顺序得到变换物体矩阵)。
3.坐标系间转换(如果是不同坐标系表达),例如物惯到惯物,那么需要相反的顺序,相同的量求逆执行变换。
4.连续变换,都是基于上一个变换的,因为变换是相对的,所以变换不满足交换律,但是满足结合律。
5.不同坐标系统间的变换,一个需要注意轴的方向和点的模型位置一致对轴变化所有顶点需要进行这样的变化,二个是点积方向发生改变所以网格法向量需要改变(三角网格的索引缓存和顶点法向量,三角形法向量具体改变计算),三是绕序绕序改变旋转改变三角网格的正面也改变,四个是坐标系位置改变了那么观察坐标系中观察点也要做调整否则观察到的情况会发生变化。
矩阵:
每个变换都可以解释为一个矩阵,也就是一个新的坐标系,如果新坐标系是用旧坐标系描述的那么是物体变换,如果对这个变换求逆那么是坐标变换。
连续变换就是连续的建立坐标系,且都是基于上一个坐标系的,所以变换的顺序不同得到的结果完全不同。至于相对任意轴不在原点的变换,先平移到原点再进行放缩旋转变换,在平移回去(平移矩阵求逆);这时只是得到相对于该任意轴的变换,对于具体的变换还需要先平移到物体所在的坐标系(物体每个点都是基于物体坐标系的情况下),然后再对物体进行变换。
1)同坐标系描述,直接变换物体(参考坐标系用旧坐标系表达):
一定要搞清楚前提条件(例如过原点的仿射变换和不过原点的差别巨大的); 也要搞清楚是一个变换,和多个变换的连续变换(例如非过原点的仿射平移是轴或者平面的,R = P_1 * R * P), 然后T是平移的是独立的,那么最终M = T * R); 也要弄清楚DX或者OPENGL变换函数的含义,有些情形下是不支持的或者通过恰当转化后改变了前提条件,那么才是可以使用的。
矩阵是代表物体的变换(虽然也可以代表坐标系变换但是最终还是要作用在物体上),该变换是用物体变换,假设变换到新的用旧坐标系表示的新的坐标系。那么物体进行该变换(缩放旋转平移)就相当于是到了物体变换到了这个参考坐标系,只是这个参考坐标系是用旧坐标系来表达的,但是从旧坐标系来看确实是变换了物体得到想要的效果。
矩阵的行向量是新坐标系的基向量,是用原坐标系轴假设变换(旋转或平移)到一个新的位置后在各个原轴上面分解的向量之和;
矩阵的列向量是新坐标系各个基向量在某一个原轴上分解出来的向量的汇总,用于对物体向量的某分量在新坐标系下面重新组合计算位置。
例子:
// 如果在同一个坐标系中直接变换就是了,但是每一个变换都是基于上一个解释的变换的(具有层级关系的不仅是欧拉角中),所以变换顺序很重要
// 将在原点位置描述的茶壶(茶壶网格顶点是相对于原点位置的)平移到这个地方,进行绘制
// 这里也可以看做是物体坐标系,到世界坐标系的变换
D3DXMATRIX W;
// 将茶壶平移到这个位置
D3DXMatrixTranslation(&W,
TeapotPosition.x,
TeapotPosition.y,
TeapotPosition.z);
Device->SetTransform(D3DTS_WORLD, &W);
Teapot->DrawSubset(0);
2)同坐标系描述,变换坐标系来变换物体(参考坐标系用旧坐标系表达):
不是用新坐标系描述,而是假设变换坐标系,那么物体就往相反的方向来变换,其实就是在变换物体,所以每一步只需要相反的量(相反量和 逆变换是完全不同的),不需要相反的顺序来变换。其实这里的变换坐标系只是变换物体。
例如欧拉角变换转换为矩阵变换中,因为欧拉角描述的是坐标系变换,所以需要转换为物体变换(相反的量即可),因为每次都是坐标系的变换转换为每次物体的变换,得到的都是物体的变换,所以变换只用相反的量,不需要相反的顺序进行。得到的矩阵就是在旧坐标系中变换物体的矩阵。
M = M(-y) M(-x)M(-z), 相同的顺序,相反的量进行变换( 不是相反的顺序,因为变换中的坐标系都是相对于同一个坐标系的,每一步坐标系变换和物体变换相反,欧拉角本身就是如此定义的hpb都是局部坐标系的变换并非物体变换,至于进一步的证明还需要更多的积累和研究开源引擎? )。
欧拉角中的,惯性到物体是M = M(-y) M(-x)M(-z),物体到惯性是:M = M(-z) ^-1M(-x)^-1M(-y)^-1,这里是不包括平移的,包括平移则和新坐标系描述变换一样的方式进行。因为欧拉角描述的是物体的变换,但是变换的内部小步骤又是通过变换坐标系方式来讲述的(整个感觉描述得比较模糊),所以根据head pitch row得到的是物体坐标系中描述的物体,如果需要渲染转换到世界坐标系,还是需要进行物惯转换和平移来做的。
3)新坐标系描述,变换物体来变换坐标系(参考坐标系用新坐标系表达):
用新的坐标系来描述物体,那么需要先将物体某个向量从旧坐标系变换到该新坐标系中,得到变换矩阵然后对变换求逆即得物体用新坐标系描述的变换。将需要变换到新坐标系中的物体乘以该矩阵就得到了物体在新坐标系中的位置,且不需要旋转观察视角就是正确的结果因为新坐标系默认也是x向右,y向上,z向内的。具体应用中所谓有时候变换物体,有时候变换坐标系来达到转换到同一个坐标系中以便计算碰撞检测相对运动等中的有时变换坐标系更方便就是这个意思。所以变换坐标系是变换物体严格相反的顺序,相反的量来进行变换。
例如惯物变换之间,世界坐标系变换到观察坐标系之间,因为是用新坐标系来描述,且是变换物体,所以变换物体不能用相反的量(因为坐标系改变了),而是用相同的量变换物体然后求逆(逆并不同于相反的量,因为相反的量是反向的变换,而逆是变换的撤销),要求相同的量相反的变换,得到坐标系的变换。
M =(PM)^-1 = M^-1P^-1,相反的顺序,相同的量求逆进行变换。
坐标系间的转换:
物体到世界坐标系,通常是先缩放 -> 旋转 -> 平移; 世界到物体就要先平移->旋转->缩放,这是因为矩阵乘法不满足交换律导致的,深层次的原因是变换之间是层层嵌套的,每一个前提条件都代表了基于不同的基向量坐标系,所以变换次序是非常重要的。
例子:物体坐标系到世界坐标系:缩放旋转到惯性坐标系,进行相对于世界坐标系的平移,设置这样的变换坐标系,所有接下来渲染的物体都会进行这样的变换,其实物体本身是不清楚的,它把每一个变换作为一个新的坐标系,只不过这个新的相对的坐标系是世界坐标系而已。同样要得到相对于物体坐标系的变换,那么对物体到世界坐标系的变换矩阵求逆即可,求逆也就是相反的顺序相反的变换矩阵进行相乘,就会得到世界坐标系描述物体到物体坐标系(复杂模型或摄像机)描述物体的变换。
4)连续变换:每个都是基于上一个观察坐标系(不管该坐标系是用旧坐标还是新坐标来表达)的变换,而不是基于原坐标系的变换(除了第一个变换)。
如果物体的连续变换(一般物体到世界是先缩放旋转平移,世界到物体是平移旋转缩放变换,都是不能改变变换顺序的,因为连续变换符合结合律不符合交换律,都是基于前一个坐标系的)
,是矩阵直接连乘即可;如果是坐标系变换,先转换为物体的连续变换,然后求逆得到坐标系的连续变换结果。所以欧拉角中的连续旋转变换,是符合物体连续变换的直接变换即可。
一定要搞清楚前提条件(例如过原点的仿射变换和不过原点的差别巨大的); 也要搞清楚是一个变换,和多个变换的连续变换(例如非过原点的仿射平移是轴或者平面的,R = P_1 * R * P), 然后T是平移的是独立的,那么最终M = T * R); 也要弄清楚DX或者OPENGL变换函数的含义,有些情形下是不支持的或者通过恰当转化后改变了前提条件,那么才是可以使用的。
例子:
// 直接变换和连续变换问题
1.前提条件问题,如果是不包含平移的放射变换,那么缩放还是旋转的轴都是过原点的,否则需要先平移到原点然后进行仿射变换再平移回去才会得到正确的结果;两者该直接仿射变换时候就直接变换,该平移回来仿射变换再平移回去是不能混淆使用的。
2.空间变换思维方式,矩阵行向量就是变换后的新坐标系的位置,用简单例子情景分析即可。
3.非经过原点的,若用D3DX的进行变换,因为它变换默认是认为绕过原点的轴进行变换,且变换一刀切(缩放旋转部分和平移部分都直接用仿射公式进行变换),所以这种情况下是不能直接用DX仿射变换函数进行变换的。这个时候有两种方法:
1)通过改变变换轴或者平面,去掉平移部分,让其是过原点的轴和平面,令其为P2;然后将平移部分抽取为P,求逆回原点平移_P;用DX仿射变换基于P2条件求得变换矩阵R; 则最终变换为W = _P * R * P。
2)自定义过原点的仿射变换矩阵R,直接对不过原点的轴或者平面取得平移部分P,求逆得到_P,那么最终变换为:W = _P * R * P。
最后如果还有其他的连续变换,比如仿射变换前需要平移先,那么变换为:M = T * W;如果先仿射变换后再平移,那么为:M = W * T。
继续不断实践各种各样的变换,和研究开源引擎,看还有没有更好的方法和结论。
例子:
// 非过原点的镜面反射变换实现,position reflection
D3DXMATRIX T;
// 镜像平面定义,平面为ax + by + cz + dw = 0. n法向量为(a,b,c), dw为平面到原点的距离
D3DXPLANE plane(0.0f, 0.0f, 1.0f, 1.0f); // xy plane
// 平移到茶壶所在的坐标系,因为茶壶用本地坐标系描述
D3DXVECTOR3 TeapotPosition(0.0f, 3.0f, -7.5f);
D3DXMatrixTranslation(&T,
TeapotPosition.x,
TeapotPosition.y,
TeapotPosition.z);
// 方法1:抽取非过原点轴或者平面的平移部分,且用DX函数求取过原点的仿射变换矩阵
D3DXMATRIX R,W,M;
// 抽取非过原点的变换平面的平移部分
D3DXMATRIX P(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 1, 1);
// 对平移部分求反
D3DXMATRIX _P;
D3DXMatrixInverse(&_P, NULL, &P);
// 去掉平移部分的平面(有时候是轴),得到过原点的仿射变换
D3DXPLANE plane2(0.0f, 0.0f, 1.0f, 0.0f); // xy plane
D3DXMatrixReflect(&R, &plane2);
// 关于D3DXMatrixReflect,见https://msdn.microsoft.com/en-us/library/windows/desktop/bb205356%28v=vs.85%29.aspx
// 会先对平面进行单位化,但是也是没有用的
// 得到非过原点的仿射变换矩阵
W = _P * R * P;
// 继续进行平移
M = T * W;
// 方法2:抽取非过原点轴或者平面的平移部分,自定义的过原点的仿射变换矩阵
D3DXMATRIX RF(1-2*plane.a*plane.a, -2*plane.a *plane.b, -2 * plane.a * plane.c, 0,
-2*plane.b*plane.a, 1-2*plane.b *plane.b, -2 * plane.b* plane.c, 0,
-2*plane.c*plane.a, -2*plane.c *plane.b, 1 -2 * plane.c * plane.c, 0,
0/*-2*plane.d*plane.a*/, 0/*-2*plane.d*plane.b*/, 0/*-2*plane.d*plane.c*/, 1);
W = _P * RF * P;
M = T * W;
// 设置变换
Device->SetTransform(D3DTS_WORLD, &M);
// 绘制出来
Teapot->DrawSubset(0);
其实一个变换矩阵就是从一个新的观察坐标系来描述物体空间位置。
如果这个新的观察坐标系,也就是矩阵的基向量都是用旧坐标系来表达的,那么物体向量进行该矩阵变换就是相对于旧坐标系的物体变换。
如果这个新的观察坐标系,也就是矩阵是用旧坐标系描述的物体变换(直观简单)的 逆来表达,物体向量进行该矩阵变换,就是坐标系变换。
坐标系间的连续变换,都是基于上一个观察坐标系的变换(不管该坐标系是用旧坐标还是新坐标来表达),所以不能随便交换次序,仿射变换都要先平移到原点,再在原点进行放缩旋转变换,再平移回去。如果是平移和旋转之间却是可以相互变换的,因为他们得到的旋转矩阵都是一样的。
欧拉角的连续变换,也都是基于上一个变换进行的变换,虽然说是惯性坐标空间到物体坐标空间进行M=H(-h)P(-p)B(-b)变换,但是这些变换矩阵都是基于旧坐标系的,所以并没有真正转换到物体空间来描述,所以只是一个基于旧坐标空间(惯性坐标空间)的旋转序列变换而已,至于为何用-h、-p、-b那是因为对欧拉角的不同规则的定义,完全是可以M=H(h)P(p)B(b)进行变换的(需要更多的研究开源引擎证明之?)。所谓的从物体坐标系到惯性坐标系的变换,M=B(-b)^-1P(-p)^-1H(-h)^-1也不算是从物体坐标系转换到惯性坐标系,因为不是真正的物体坐标系,而是变换物体的逆向变换(如果用惯性坐标系中物体进行该逆向变换,就是变换了坐标系用新的物体坐标系来表达惯性坐标系中的物体位置,但是这里是用物体坐标系中物体进行该变换,所以是欧拉旋转的逆向变换)。所以欧拉角连续变换就是同坐标系描述,变换坐标系来变换物体的变换,如果一定要强说是惯性坐标系变换到物体坐标系,物体坐标系变换到惯性坐标系也是可以的,只是这个物体坐标系都是用旧坐标系(惯性坐标系)来表达的。
D3DXMATRIX My;
D3DXMatrixRotationY(&My, D3DX_PI / 6);
D3DXMATRIX Mx;
D3DXMatrixRotationX(&Mx, D3DX_PI / 6);
D3DXMATRIX Mall = My * Mx;
D3DXVECTOR3 v(0,0,1);
D3DXVECTOR3 Q ;
D3DXVec3TransformCoord(&Q, &v, &Mall);
// Q为{x=0.49999997 y=-0.43301266 z=0.74999994 }
// v旋转My后为v'=(0.5, 0, 0.866)
// v'根据自身的坐标系(x', y, z')旋转Mx后得到的比例是(0, -0.5, 0.866)
// v'进行Mx变换后为v''=(0.5, 0.866*-0.5, 0.866*0.866)刚好为Q
// 如果v'根据的不是自身的坐标系基础上而是世界坐标系基础上变换,
// 那么进行Mx旋转的y方向的比例变化一定小于0.5,z方向的比例也一定小于0.866。
// 证明:
// 旋转和平移,平移和旋转之间却是可以相互变换的,因为他们得到的旋转矩阵都是一样的。
// 旋转序列之间都是每个基于上一个得到的参考坐标系来进行变换的,欧拉角其实是描述物体的连续变换,至于用-h-p-b应该是对欧拉角的不同定义
?更深入研究下开源代码证明之。
欧拉角:
欧拉角变换是变换坐标系而不是变换物体,欧拉角变换是h(惯性y轴)->p(物体x轴)->b(物体z轴)顺序进行,被限制在hb在[-pi,pi]内,p在[-pi/2,pi/2]内。转换为四元数或者矩阵时,注意坐标变换转换为物体变换,对物体进行连续的变换计算即可。
欧拉角本身的别名问题:pitch角度变换为自身的补角,那么head,bank同时正向旋转pi即得到。
当为万向锁别名问题,此时定义规则z轴和y轴同轴且方向一致(万向锁出现前已定义原因是绕z轴的旋转平面和绕y轴的旋转平面重合了,而重合平面不区分正面和反面,都认为是绕z轴的正面和绕y轴的正面重合了,故认为z轴和y轴重合了),bank的旋转全部用head代替即可。
四元数:
旋转四元数q = (w,x,y,z) = ( cos(θ/2), sin(θ/2)nx, sin(θ/2)ny, sin(θ/2)nz)对于空间几何最有用,
θ为旋转的角度,(
nx, ny, nz)为旋转轴。连续四元数变换得到的结果四元数是标准的四元数,即和乘法定义无关,四元数的连续变换并不等价于四元数连续乘法,而是刚好相反。
四元数和矩阵之间转换是纯粹的四元数转换,而该四元数只是一半的方位变换信息,还有一半在四元数的逆中,
但四元数转换为矩阵,已经包含了另外的一半逆四元数的信息。
内容:
1.变换物体等价于相反的顺序相反的量变换坐标系
变换坐标对于所有的点都适用,且可以组合多个变换最终计算一次(不过变换物体也有这些优点,看下面矩阵的理解)。
2.点和向量的关系
点是终点描述的是位置(相对位置),向量描述的是大小和方向,也可以描述相对位置,所以向量可以代替点,点不能代替向量,且有些计算不需要知道原点,用向量来描述3D几何计算更加灵活,3D中的所有于位置、大小、方向、方位等几何数学相关的计算都应该优先考虑用向量来表达。
3.向量的运算
1)模来表示大小,单位化来表示方向。
2)加减法用“三角”法则,分而治之。
3)数乘用于放缩。
4)点乘计算cos
θ
, cos θ = (vectorA * vectorB) / (vectorA.Length()
*
vectorB.Length());
θ = arccos θ;
vectorA和到
vectorN上的投影向量A'// =
vectorA.Length() * cos θ * vectorN.normalize();
vectorA垂直vectorN的向量A'| = vectorA - A'//;
5)叉乘计算法向量,vectorN = vectorA x vectorB; vectorN的方向是vectorA终点平移到vectorB的始点,左手四指绕vectorA到vectorB的向量方向,拇指指向的方向就是vectorN的方向。
叉乘 (vectorA x vectorB).Length() = vectorA.Length() * vectorB.Length() * sin θ;
vectorA x vectorB = (AyBz - AzBy, AzBx - AxBz, AyBz - AzBy)。
4.矩阵
Unfortunately, no one can be told what the Matrix is. You have to see it for yourself.
矩阵在3D几何应用中表示变换,具体是用新的坐标系用旧的坐标系来表达(假设)。目的是对空间物体向量乘以矩阵可实现需要的变换。
矩阵的行向量是新坐标系的基向量,是用原坐标系轴假设变换(旋转或平移)到一个新的位置后在各个原轴上面分解的向量之和;
矩阵的列向量是新坐标系各个基向量在某一个原轴上分解出来的向量的汇总,用于对物体向量的某分量在新坐标系下面重新组合计算位置。
实际变换中,虽然经常说坐标系变换,但是
图形库实际中无论是物体变换还是坐标系变换都是用变换物体形式得到变换矩阵,也就是当前坐标系假设是不变的,而物体进行旋转平移(或者逆向的平移旋转),
变换矩阵的计算方法是物体变换到了一个新的位置同时假设这是变换到了一个新的坐标系, 用旧的坐标系来描述新的坐标系( 具体的求取假设一个基准向量用变换前基向量来表示变换后的基向量 ),得到新的坐标系就是变换矩阵。物体变换是这样,坐标系间的变换可以通过变换物体(变换坐标系需要相反顺序相反量且用新坐标系描述假设旧的变换了的坐标系得到变换矩阵,但是结果都是物体乘以变换矩阵实现变换)描述变换物体过程中的旧的坐标系已经变换到了目标坐标系(可以通过三角形定则计算物体变换到的位置)表示新的假设的坐标系得到变换矩阵,即使是世界到物体坐标系也是这样。
这样物体向量乘以矩阵就可以实现变换,原坐标系的所有物体向量乘以矩阵(单个或者组合)就可以变换到一个新的坐标系来描述(这个坐标系是真正新的坐标系不是上述假设的)。
变换矩阵的求取:
证明思想:特例 :1)将任意轴旋转的向量取得特例p绕n轴旋转
θ角度到p'。
分解:2)将特例分解为p平行,p垂直。
转换:3)分别在平行和垂直上面转换,发现平行方向不用变换,那么只需要垂直方向变换即可,垂直方向变换可以转换为已 知的绕某一个轴的旋转而求得。
抽象组合:4)由p' = ?p抽象为对原来的基向量进行对应的转换,所有基向量转换后组成的矩阵就是所求的变换矩阵。
矩阵的逆:
用代数余子式(代数余子式是余子式矩阵的行列式加上符号)组成的矩阵的转置得到伴随矩阵,伴随矩阵除以行列式值得到矩阵的逆。
行列式为二维向量的面积,三维向量的体积,如果行列式值为负数则矩阵变换包括镜像,行列式值为0则包括正交投影(或理论透视投影)。正交矩阵(单位化且轴两两垂直如旋转和镜像)的转置矩阵就是矩阵的逆。矩阵的逆可以用于逆向变换非常有用。
矩阵正交化:
矩阵正交化用施密特正交化,基本思想是先确定一个轴,下一个轴消除(减去)投影到已知轴的投影向量得到下一个轴的向量,这样依次进行,就正交化了所有 的向量。
这样可能得到的正交矩阵是歪的非标准,改进的做法是先确定一个向上的轴向量如果可以,然后计算其它轴的向量。否则用渐进式的正交化方法,不断的调整。
4D向量:
4d向量是代数上的实现平移变换的技巧,也可以理解为4D上的向量切变到3D中,使得3D向量实现平移:x = x + delataX, y
= y + delataY, z = z+ delataZ,当向量的4D w开关为0时候可以为纯向量如表面法向量不需要知道平移位置,当w = 1时向量可以表示点的相对位置。
将平移T结合线性变换,先平移回原点,再R变换,再平移T'逆回到原来的点,TRT'= ...发现,放射变换只是映射变换矩阵的最后一行,也就是平移哪行其它的矩阵基向量都不变,其实这也是符合几何变换原理的。
透视投影:
透视投影在图形学中非常重要,用小孔成像原理:
y'/-d = y / z ->y' = -y * d/ z;
x'/-d = x / z ->x' = -x * d/ z;
z' = -d;
为了避免负数可以将焦点还是放置到原点,但是 z = d处进行透视投影。但是这样的结果还是舍去了z轴,无法表示深度剔除渲染信息。
所以实际中还是用透视原理,x,y,z值都会变小或也会变反,实际 透视投影中会将视锥六面体投影到(-1,-1,-1)-(1,1,1)的正方体中,透视投影矩阵当然更加复杂些。
5.方位
一个物体除了大小,方向,相对位置,还有朝向,方位就是描述物体朝向的度量,而方位的定义来自于旋转的角位移。
方位是旋转结果度量值,角位移是旋转的过程描述,方位的变化来自于角位移的变换。方位和角位移的量都是相对的,一般相对于惯性坐标系或者物体坐标系的笛卡尔坐标系。如果是相对于惯性坐标系那么方位的转换时惯性坐标系到物体坐标系的旋转,反之就是相对于物体坐标系旋转。
(1).旋转矩阵:
旋转矩阵本身就可以表示方位或过程中的角位移。矩阵表示方位的优点是可以组合变换,底层图形渲染库都是支持矩阵,弱点是使用的内存多,且太多操作变换镜像投影等会使得矩阵蠕变(需要正交化)。
(2).欧拉角:
欧拉证明了角位移序列等价于单个角位移,所以单个复杂的角位移可以用多个简单的位移序列来表达。
欧拉角,欧拉角分为从惯性到物体的旋转head-pitch-bank模式,模式中记得第一个变换旋转y轴是基于惯性坐标系,x轴会随着物体变化是基于物体坐标系,z值也会随着物体变化是物体的原来前面的方向也是基于物体坐标系旋转的。
roll-pitch-yaw是物体到惯性坐标系,head是基于惯性的,yaw是基于物体坐标系的,所以head和yaw是有区别的。
欧拉角的别名问题可以通过限制head在[-180,180]度,pitch在[-90,90]度,bank在[-180,180]度之间来进行别名区分,但遇到万向锁时候那么bank变换转换为head变换,也是可以处理好别名问题的。
欧拉角的别名问题:pitch角度变换为自身的补角,那么head,bank同时正向旋转pi即得到。
欧拉角线性插值问题:欧拉角遇到万向锁角位移就不是线性变化的了,因此会遇到问题,物体突然飞起旋转到一个位置的情况。
万向锁问题:
万向锁问题产生的原因:考虑一战中的大炮和飞机问题,大炮旋转必须绕着一个轴而不是一个点,当大炮前后旋转到炮口垂直指向天空时候,就无法进行左右旋转了,因为原来的左右旋转是基于向上的y轴,但是现在继续绕着y轴旋转只能自旋了,如果这个方位要实现左右旋转除非大炮是基于一个点(着地点)进行旋转,但是一个点是固定不了大炮的,如果要想继续绕基于轴的旋转那么只有前后轴了,但是前后轴旋转的大炮是没有做的且会搅动土地。
放到图形系统中,也是同理的,一方面需要一个y轴是基于惯性坐标系轴的,否则旋转从一开始就不对了。所以欧拉角万向锁pitch旋转到+-90时候就会发生物体坐标系的z轴和y轴重合或者相反了。所以万向锁在欧拉角旋转中一定存在的,且会影响线性插值。所以欧拉角不能实现线性插值。
万向锁出现原因,欧拉角变换坐标系间是有层次关系的,h是惯性坐标系,处在旋转的根节点旋转后会导致p和b旋转,p是第二节点p旋转的时候会导致b旋转,b旋转是最末的旋转节点,当中间的p旋转上下90度时候会使得h轴和b轴重合或者相反(定义为重合)。
当为万向锁,pitch是+-pi/2度时候,因为存在万向锁别名,此时定义规则z轴和y轴同轴且方向一致(万向锁出现前已定义),bank的旋转全部用head代替即可。
bank全部用heading代替这里只是定义规则的问题,不考虑z轴和y轴重合还是相反(定义为重合方向也也一样) ,如果需要b旋转不一样改进正负就好。 bank的旋转全部用head代替目的是解决别名问题,heading 45、pitch90等价于pitch90、bank45(z轴和y轴重合了)现在统一了,如果pitch90、back45度那么也是等于 heading 45、pitch90度。
(3).四元数
1)虚数(复数)的实际意义
虚数的模代表向量的长度,虚数的加减可以代表向量的加减。
虚数的乘法为,当前方位的虚数乘以变量量代表的虚数,得到结果虚数表示该方位变换后的方位。
p(x,yi)
q(cos
θ, i*sinθ)
p'= p * q = ( xcosθ - ysinθ, (xsinθ + ycosθ)i );
和p(x,y)乘以旋转矩阵( cosθ, sinθ
-sinθ, cosθ) 结果是( xcosθ - ysinθ, xsinθ + ycosθ)结果是一样的。
但是三维中的虚数却要用四元数来表示,且四元数实现旋转不是p*q,而是p*q*p-1形式实现和矩阵旋转等同的变换。
2)四元数的定义
四元数表示三维空间中的虚数,由william hamilton于1843年在去皇家爱尔兰学院路上时意识到将复数从2D空间拓展到3D空间不是用一个实部和两个虚部,而是一个实部和三个虚部,于是他将四元数的定义刻在Broome桥上。
四元数的定义:
(w,x,y,z) = w + xi + yj + zk;
i*i = j*j = k*k = -1;
ij = k, ji= -k;
jk = i, kj = -i;
ki = j, ik= -j;
3)四元数的解释和性质
四元数都可以单位化,且单位四元数对于旋转才是有用的,对单位四元数的三维几何解释为:
q = (w,x,y,z) = ( cos(θ/2), sin(θ/2)nx, sin(θ/2)ny, sin(θ/2)nz);
四元数q是绕轴n(nx,ny,nz)旋转θ,因为具体求取方位时候要求θ属于[0,2pi]这样可以用acos求取弧度值和四元数之间的关系。
因为绕θ角,但是计算四元数时候却用了θ/2的关系(之所以是θ/2是因为四元数旋转物体需要qVq-1来对物体进行旋转,进行了两次θ/2的角度旋转),且cos(θ + pi) = -cos(θ),sin(θ + pi)=-sin(θ)所以任意的角位移都有两种不同的四元数表示,四元数q和-q表示相同的角位移。
负四元数为:
q = (w,x,y,z)那么-q = (-w, -x, -y, -z)。
知道角位移可以用两个四元数表示。
单位四元数和角位移的关系:
当限定旋转角度属于[0,2pi]时候,知道角位移可以确定唯一的单位四元数,知道单位四元数也可以唯一确定角位移([0,2pi]内)。
单位四元数:
数学上(1,0,0,0)为单位四元数,几何上(1,0,0,0)和(-1,0,0,0)都可以作为单位四元数因为角位移都一样,一般用(1,0,0,0)表示为单位四元数。
表示旋转的单位四元数模为1:
| cos(θ/2), sin(θ/2)nx, sin(θ/2)ny, sin(θ/2)nz | = 1。
表示旋转,仅使用单位四元数,单位四元数的模为1非常有用,因为求单位四元数的逆就简单多了。
四元数的共轭和逆:
四元数的共轭为:q = (w,x,y,z)那么q*= (w, -x, -y, -z)。
四元数的逆为:q' = q* / |q| = q*。
四元数共轭的定义可以为:q*= (w, -x, -y, -z)相反的轴旋转相同的量,也可以为q*= (-w, x, y, z)相同的轴旋转相反的量。
四元数的差(只是四元数之间求得的角位移四元数,不是向量旋转):
ad = b;
a-1ad = a-1b;
=>d = a-1b.
d是a到b的角位移差,差更类似于除,所以和更类似于乘法,具体计算差时候用的还是四元数乘法来解决,所以四元数乘法很重要。
四元数的点乘:
(w1,x1,y1,z1).(w2,x2,y2,z2) = w1*w2 + x1*x2 + y1*y2 + z1*z2;
不用考虑i,j,k这些。
四元数的点积和向量的点积一样,点乘结果表示的含义也一样,结果越大表示两个方位越接近。
其它一些四元数运算性质,四元数的对数,指数,标量乘法:
alpha = θ/2;
q = (cos alpha, x*sin alpha, y*sin alpha, z*sin alpha);
q的对数定义为公式:
log q = log(cos alpha, n sin alpha] = (0, n*alpha);
四元数指数定义公式:
exp q = exp(0, n*alpha) = (cos alpha, n sin alpha);
exp(log q) = q;
四元数标量乘法:
kq = k(w,x,y,z) = (kw, kx, ky, kz)因为得到结果不是单位四元数所以标量乘法没有什么作用。
4)四元数(角位移)旋转向量四元数的实现,四元数的乘法(叉乘):
向量四元数是该3D向量用四元数表示为(0,x,y,z)形式。
两个四元数相乘,和多项式相乘一样,只是结果用四元数定义的性质进行变换合并多项式。
四元数的乘法满足结合律,但是不满足交换律。
四元数乘积的模等于四元数模的乘积,这个结论非常有用,因为证明了两个四元数相乘还是单位四元数,可以实现多次旋转合并的效果。
四元数乘积的逆为各个四元数的逆以相反的顺序相乘,这样对于连续旋转可以最后求逆。
关于四元数旋转向量的实现原理和证明:
1).矩阵旋转和四元数旋转是等价的 ,标准乘法定义的四元数旋转为p' = qpq-1,本引擎定义的乘法的四元数乘法为p' = q-1pq。
2).标准的四元数乘法(叉乘)是[w1,v1]*[w2,v2) = (w1*w2 - v1.v2 w1*V2 + w2*V1 + V1xV2)
本引擎中使用的是[w1,v1]*[w2,v2) = (w1*w2 - v1.v2 w1*V2 + w2*V1 + V2xV1) 。
3).四元数的连乘在本引擎定义的乘法规则下:(ab)-1q(ab)等价于标准乘法定义下的(ba)q(ba)-1,所以本引擎定义的规则在多旋转情况下能够实现
从先到后顺序进行乘法,只是最后乘以向量四元数的时候需要p-1qp的形式进行乘法。
四元数的标准乘法:
Quaternion Quaternion::operator* (const Quaternion& rkQ) const
{
// NOTE: Multiplication is not generally commutative, so in most
// cases p*q != q*p.
return Quaternion
(
w * rkQ.w - x * rkQ.x - y * rkQ.y - z * rkQ.z,
w * rkQ.x + x * rkQ.w + y * rkQ.z - z * rkQ.y,
w * rkQ.y + y * rkQ.w + z * rkQ.x - x * rkQ.z,
w * rkQ.z + z * rkQ.w + x * rkQ.y - y * rkQ.x
);
}
右手坐标系中:用标准乘法且顺时针旋转下,那么变换向量的公式为:a-1va,连续变换为:b-1a-1vab = (ab)-1vab。
因为左手坐标系中标准乘法下变换向量为:ava-1,因为乘法顺序不变,a和a-1是互逆的,左手坐标系转换为右手坐标系刚好从旋转方向的左手法则变为右手法则,旋转的方向刚好改变,也就是在右手坐标系下乘法规则用标准乘法,且在顺时针下,那么变换向量的公式就变为了:a-1va,连续变换就是:(ab)-1v(ab)。
如果是左手坐标系下,例如《游戏引擎架构》顽皮狗的引擎中用的
四元数标准乘法在左手坐标系下为:ava-1,则连续变换就变为了:(ba)v(ba)-1。
关于顺时针和逆时针变换,也是和左右手坐标系一样的道理,在右手坐标系标准乘法逆时针变换为:ava-1, 在左手坐标系标准乘法逆时针变换为:a-1va.
OGRE中的四元数变换向量的源码,用nVidia SDK,证明见:http://www.xuebuyuan.com/2181596.html大概了解但是内部包含的数学模型有待深入,现在先验证之:
Vector3 Quaternion::operator* (const Vector3& v) const
{
// nVidia SDK implementation
Vector3 uv, uuv;
Vector3 qvec(x, y, z);
uv = qvec.crossProduct(v);
uuv = qvec.crossProduct(uv);
uv *= (2.0f * w);
uuv *= 2.0f;
return v + uv + uuv;
}
经过测试证明和标准四元数变换向量:
Vector3 Quaternion::stlChangeVector(const Vector3 &v) const
{
Quaternion targetQ(0.0f, v.x, v.y, v.z);
Quaternion curQ(w, x, y, z);
Quaternion curReverse = curQ.GetReverse();
Quaternion resQ = /*StlMultiply(StlMultiply(curQ, targetQ), curReverse)*/curReverse * targetQ * curQ;
return Vector3(resQ.x, resQ.y, resQ.z);
}
是一样的效果。
但是用标准四元数变换向量时候一定要记得,四元数的逆是唯一的,无论是变换轴,还是变换角度,都是(w, -x, -y, -z):
<pre name="code" class="cpp">Quaternion Quaternion::GetReverse()
{
// 逆用,轴不变,旋转角度变为了负数,例如:左手顺时针60度,变为左手-60则:cos-30为正,sin-30为负,轴不变。
//return Quaternion(w, -x, -y, -z);
// 逆用,轴发生了改变,旋转角度不变,例如:左手顺时针60度,轴变为左手60则:cos30为正,sin30为正,轴负。
// 还是原来一样,绝对不能够是(-w, x, y, z)这样的求逆这样是错误的
return Quaternion(w, -x, -y, -z);
}
5)四元数求幂(四元数代表的旋转内部插值得到的方位四元数):
q t = exp(tlogq),当t属于[0,1]时,q属于[1,0,0,0]到[1,x,y,z],t线性变化,那么tx,ty,tz也是线性变化。
求取四元数的幂,不能够连续划分角度累计后计算,而是应该每次划分计算一次,这样才能够得到正确的结果。
因为表示旋转的四元数是单位四元数,所以t>1的情形运算结果都是本身。
理论上q t = exp(tlogq)表达四元数求幂很完美,但是用代码表达却是很复杂的,代码中使用更加简单的方法:
前提限定旋转轴是任意的单位向量,旋转的角度θ在[0,2pi]内,这样避免了一个方位有两个四元素表达,一个四元数又可以解释为多个θ表达的方位。限定θ在[0,2pi]内,那么一个四元数可以求得确定的旋转角度,确定的旋转轴,那么就可以表示一个确定的方位。
同理一个确定的方位也可以唯一的确定一个四元数。
四元数内部插值的计算代码:
Quaternion pow(const Quaternion &q,float exponent){
// 避免除零运算,cos(theta/2) 过小就不计算内部插值了
if (fabs(q.w) > 0.9999f){
return q;
}
float alpha = acos(q.w);
float newAlpha = alpha * exponent;
Quaternion result;
// 新的cos newAlpha
result.w = cos(newAlpha);
// 新的q.x' = nx sin(newAlpha) = nx * sin(alpha) * ( sin(newAlpha) / sin(alpha) ) = q.x * mult;
float mult = sin(newAlpha) / sin(alpha);
result.x = q.x * mult;
result.y = q.y * mult;
result.z = q.z * mult;
return result;
}
因为θ在[0,2pi]内那么alpha = θ/2 则在[0,pi]内,这样求取alpha用acos即可。
double acos(double x),x范围在[-1,1], 返回值为弧度在[0,pi]内, 这样就可以唯一确定正向旋转的角度。通过对旋转的角度乘以幂再重新计算cos alpha和sin alpha,而旋转轴不变,那么就得到四元数的幂了
。如果是逆向的旋转的四元数,也是可以解释为正向旋转的四元数的,得到的结果是对正向四元数插值的结果。如果不满足要求,一定要逆向旋转的插值结果,那么改变轴向来改变原四元数,然后进行正向旋转的插值计算,得到结果后再改回原来的轴向,就得到了逆向旋转的插值结果。
6)四元数插值Slerp
Slerp是Spherical Linear Interpolation球面线性插值的缩写。
解决的问题两点间插值,已知q0,q1插值参数设置为变量t,t从(0,1)求方位在指定t下的q'。
理论上可以用:slerp(q0,q1,t) = q0(delta q)^t = q0(q0^-1q1)^t。
q0^-1q1可以用两个方位之间的差求取,(delta q)^t 可以用四元数求幂得到,期间四元数的乘法可以用标准四元数乘法得到。
但是实际上并没有采用该方法,而是用了几何弧线插值的方法。
用的是剖析一个旋转平面,向量加法:得到Vt = K0V0 + K1V1;
通过对截取的平面进行向量加法,作平行辅助线,设旋转到的角度为tw,(1-t)w,设未知量k0,k1; 利用平行线的同位角相等,球面内半径上的向量都是单位向量,对一个角应用三角函数关系,未知量用另外的已知量来表达。假设未知量,找到多于未知量的关系式,那么就可以求得未知量变为已知量的方程思想。
具体的Slerp函数为:
Quaternion slerp(const Quaternion &q0,const Quaternion &q1,float t){
if (t <= 0.0f)
return q0;
if (t >= 1.0f)
return q1;
/*1.点积夹角含义
四元数的点积可以计算两个四元数夹角cos theta/2, 该theta/2是两个四元数代表的方位真实角度的一半,因为是用真实角度的一半的
cos和sin值来表达四元数的,四元数内部使用的角的量上面本来就减半了。
*/
float cosOmega = dotProduct(q0,q1);
float q1w = q1.w;//do not change the q1,use &q1 make fast,so pay the space.
float q1x = q1.x;
float q1y = q1.y;
float q1z = q1.z;
/*2.四元数的负数,和一个方位可以用两个四元数表示的含义,以及四元数的共轭
如果两个四元数的半角的差在[0,pi/2]或者[-pi/2,0]以外,也就是四元数表示的旋转方位差在[0,pi]或[-pi,0]以外,那么反转一个四元数,
使得半角的差的绝对值在[0,pi/2]内。
具体做法是全部反转,也就是四元数的负数,四元数的负数是再旋转一周2pi的结果(四元数的旋转是加上(2n + 1)pi得到负数,加上2npi有得到非负数),
也可以解释为绕同一个轴逆向旋转2pi-theta的角度,
原因是在[0,2pi]内就是cos (theta/2),sin (theta/2)变反了,也就是theta/2本来在第二象限的现在到了第四象限(或者theta/2在第三象限变到第一象限中)。
四元数的共轭,除以单位四元数也就是四元数的逆,四元数的逆表示相反的变换,在撤销变换和惯物物惯变换中非常有用。且不同于四元数的负数的含义。 */
if (cosOmega < 0.0f){
q1w = -q1w;
q1x = -q1x;
q1y = -q1y;
q1z = -q1z;
cosOmega = -cosOmega;
}
assert(cosOmega < 1.1f);
float k0,k1;
/*3.普通线性插值
如果太小了,那么使用普通的线性插值,也就是Vt =q0 + t*(q1 - q0) = (1 - t)q0 + tq1
对比Vt = k0V0 + k1V1得到k0 = 1 - t , k1 = t */
if (cosOmega > 0.9999f)
{
k0 = 1.0f - t;
k1 = t;
}else{
/*4.sinOmega取得正数而不是负数,因为旋转角度Omega是一个两角间的夹角,无论是正向旋转还是逆向旋转两个四元数的夹角都在
[0, pi / 2]内,所以sinOmega取正数就可以了。
atan返回[-pi,pi]内的值,
因为sinOmega是正数cosOmega也是正数,所以omega也会是一个正数 。这样k0,k1都是正数,四元数乘以k0,k1代表一个旋转的量变,并不修改旋转的方向,这样使得插值更职责唯一
*/
float sinOmega = sqrt(1.0f - cosOmega*cosOmega);
float oneOverSinOmega = 1.0f / sinOmega;
float omega = atan2(sinOmega,cosOmega);
k0 = sin((1.0f - t) * omega) * oneOverSinOmega;
k1 = sin(omega * t) * oneOverSinOmega;
}
Quaternion result;
// 5.对四元数进行线性插值结果的计算,返回线性插值的四元数
result.w = k0*q0.w + k1*q1w;
result.x = k0*q0.x + k1*q1x;
result.y = k0*q0.y + k1*q1y;
result.z = k0*q0.z + k1*q1z;
return result;
}
7)四元数样条squad
slerp是两个控制点之间的插值,如果是多于两个方位序列间的插值,也就是这个时候两个方位间的插值不仅受到最近两个方位间的影响,附近的控制点也会影响最终的插值。squad是Spherical and Quadrangle的公式用于描绘控制点的路径。
设控制点由四元数序列定义:
Q1,Q2,Q3,...,Qn-2,Qn-1,Qn。
在方位序列间插值需要引入辅助四元数,它作为临时控制点:
Si = exp( -(log(Qi+1*Qi^-1) + log(Qi-1Qi^-1)) / 4 )*Qi;
Si由Qi-1QiQi+1求得,所以S1,Sn是未定义的,如果一定要S1,Sn那么可以让S1等于S2,Sn等于Sn-1。
具体squad序列间的插值例如四个控制点:
Qi-1QiQi+1Qi+2
引入一个插值变量h,h从0变化到1时,squad描绘Qi到Qi+1之间的曲线。
squad(Qi,Qi+1,Si,Si+1,h) = slerp( slerp(Qi,Qi+1,h), slerp(Si, Si+1, h), 2h(1-h) );
2h(1-h) = 2(h - h^2), h属于(0,1), 2h(1-h)属于(0, 1/2)。
该公式中涉及的数学原理比较复杂,直接应用就可以了,对于多控制点间的Qi到Qi+1, 先计算辅助控制点Si和Si+1的四元数,然后根据h的具体值用三次球面插值算法即得到多个控制点间的样条线性插值结果,具体需要使用的时候还要搞清楚Si的求法。
6.矩阵、欧拉角和四元数的优缺点
欧拉角的优点:
人机交互很方便,旋转限制定head[-180,180],pitch[-90,90],bank[-180,180]角度内即可,插值也可以做到,只是遇到万向锁Pitch到垂直位置会导致问题。缺点不能平滑插值,不能表示平移,所以不能在物体坐标系和惯性坐标系间旋转点。
四元数的优点:
slerp和Squad使得方位间提供的平滑插值的唯一方法,且插值速度比较快,如果是动画数据需要在物体嵌套坐标系间连接那么最好使用四元数,但单纯是为了保持动画数据方位,那么欧拉角也可以做到且使用的数据量更少。
四元数的缺点是不能表示物体坐标系到惯性坐标系之间的转换,因为四元数不能代表位移,欧拉角也一样。
矩阵的优点:
表示旋转的时候可以表示平移,可以在物体坐标系间旋转点(物体坐标系和惯性坐标系)。图形底层是用矩阵来实现旋转,其它形式保存的旋转提交到图形底层进行渲染时候,都需要转换为旋转矩阵,因为显卡只通过旋转矩阵来识别方位变换,但是保存方位数据时候可以使用欧拉角或四元数。
缺点使用的数据比较多,转换太频繁会发生矩阵蠕变。
7.矩阵、欧拉角和四元数间的转换
1)欧拉角定义的是旋转坐标轴,而不是旋转物体,每次旋转坐标轴都是相反的方向来旋转物体,所以这个情况下用相反的量不需要相反的顺序(因为相反的顺序也是一样的),也就是相反的量相同的旋转顺序变换物体即可。如果是物体到惯性,那么需要与惯物下严格相反的顺序相反的量也就是逆矩阵逆四元数进行变换。
2)欧拉角转换到矩阵或者四元数,用矩阵或者四元数的方式来表示欧拉角的单个旋转,然后连接旋转即可;相反的转换逆推表示即可。
万向锁出现原因,欧拉角变换坐标系间是有层次关系的,h是惯性坐标系,处在旋转的根节点旋转后会导致p和b旋转,p是第二节点p旋转的时候会导致b旋转,b旋转是最末的旋转节点,当中间的p旋转上下90度时候会使得h轴和b轴重合或者相反(定义为重合原因是绕z轴的旋转平面和绕y轴的旋转平面重合了,而重合平面不区分正面和反面,都认为是绕z轴的正面和绕y轴的正面重合了,故认为z轴和y轴重合了)。出现欧拉角别名时,bank的旋转全部用head代替即可。
3)四元数的叉乘定义改变了,如果是对四元数乘法的顺序也改变了,也就是用交换律,那么结果和标准乘法一样的结果。如果不是用交换乘法顺序,那么得到的结果是不正确的四元数,但是欧拉角转换为四元数却用了自定义乘法且没有交换顺序,其正确性有待验证,和矩阵转换比较?和矩阵乘法比较得出的结论是还是需要交换顺序的否则结果不对,或者用标准乘法不交换顺序直接相乘。
解答1:欧拉角转换为四元数,用两种定义的乘法规则,得到的四元数那个为准的问题?
四元数变换顺序并不等于四元数乘法顺序:
// 其中的乘法在于四元数hpb的乘法,用Q惯物=bph顺序转换
// 转换过程为 bphvh^-1p^-1b^-1得到bphv(bph)^-1,故标准乘法表示的旋转四元数为bph
新的乘法:
// 这个乘法结果是用本引擎定义的乘法,得到的四元数也是标准形式的四元数
// 因为标准四元数的变换为进行hpb变换,hpb变换并不等价于hpb乘法,而是bphvh^-1p^-1^b-1变换,该变换的四元数为bph.
// 该bph乘法是标准四元数的乘法结果,而新的乘法v2xv1刚好可以进行交换律得到相同的结果,所以新的乘法下hpb相乘可以得到
// 相同的结果。
结论:两种定义的乘法规则,都是为了得到标准四元数连续转换得到的标准旋转四元数,乘法定义规则改变了,那么乘法顺序也相应的改变了,就会得到相同的结果。
遇到复杂的问题和理论,多举例实践验证证明下,用数学思想和证明方法,多深入和跳出来发散思考就可以解决理解复杂的问题和理论。
解答2:四元数和矩阵间的信息转换问题?
// 四元数和矩阵之间转换是纯粹的四元数转换,而该四元数只是一半的方位变换信息,还有一半在四元数的逆中
// 但四元数转换为矩阵,已经包含了另外的一半逆四元数的信息,转换到矩阵中,
// 所以转换到矩阵后就可以用矩阵的方式来变换向量了。
// 之前怀疑转换信息丢失的问题其实是转换函数有bug,这样的问题需要严格的数学证明方法来定位bug。
结论:四元数转换为矩阵后可以做矩阵变换向量,矩阵转四元数可以表示相同的变换,但是四元数变换向量却是要计算四元数的逆的,矩阵变换到四元数可以做想要的插值。
4)四元数用欧拉角表示,那么需要旋转的矩阵也用四元数表示,欧拉角转换为用矩阵表示,然后计算出四元数表达的欧拉角。
是惯物,还是物惯取决于欧拉角的用的矩阵表示法。
其实四元数的共轭,就是物惯的表示,因为刚好是一个轴反转而角度相同的旋转。