OSG之矩阵与四元数之间的转换

428 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天

1 概述

通过前面的学习可知,矩阵与四元数在表达方位上各有优势。在不同的情况下,可以选择使用矩阵、四元数或两者的综合。

矩阵能够在坐标系之间转换向量,而四元数则不能。如果四元数需要在坐标系之间转换,则四元数需转换为矩阵。四元数能够提供平滑的线性插值,而矩阵基本上进行插值,即使插值也是非常粗糙的。因此,适当的时候矩阵与四元数的转换是非常有必要的。

2 四元数转换为矩阵

为了将角位移从四元数表示方式转换为矩阵表示方式,有必要将四元数用轴角方式描述,以方便计算:

2.png

其中,ax、ay、az表示轴的矢量,θ表示绕此轴的旋转角度。事实证明,可以方便地利用四元数的4个分量表示矩阵的9个元素。下面直接给出推导出的公式,不再逐一推导。推导需要很强的数学技巧,同时,它不是本书描述的重点,具体的推导过程可参见相关的线性代数书籍。

一般四元数转换为矩阵的公式如下:

3.png

规范化四元数转换为矩阵的公式如下:

4.png

根据上面的公式,具体实现代码如程序清单所示。

00001//四元数转换为矩阵
00002 void setRotate(const Quat& q) 
00003
00004 double length2=q.length20; 
00005 if(fabs(length2)<=std:numeric_limits<double>:min0)
00006 { 
00007    _mat[0][0]-0.0;_mat[1][0]=0.0;_mat[2][0]-0.0; 
00008    _mat[0][1]=0.0;_mat[1][1]=0.0;_mat[2][1]=0.0;
00009    _mat[0][2]=0.0;_mat[1][2]=0.0;_mat[2][2]=0.0; 
00010 }
00011 else
00012 {
00013    double rlength2;
00014
00015    if(length2!-1.0)
00016    {
00017        rlength2=2.0/length2; 
00018    }
00019    else
00020    {
00021        rlength2=2.0;
00022 }
00023
00024 double wx,wy,wz,xx,yy,yz,xy,xz,zz,x2,y2,z2;
00025
00026 x2=rlength2*QX; 
00027 y2=rlength2*QY;
00028 22-rlength2*QZ;
00029
00030 xx=QX*x2;
00031 xy=QX*y2;
00032 xz-QX*z2;
00033
00034 yy=QY*y2; 
00035 yz=QY*z2; 
00036 zz=QZ*z2; 
00037 
00038 wx=QW*x2;
00039 wy-QW*y2; 
00040 wz=QW*z2; 
00041
00042 _mat[0][0]=1.0-(yy+zz);
00043 _mat[1][0]=xy-wz;
00044 _mat[2][0]=xz+wy; 
00045
00046
00047 _mat[0][1]-xy+wz; 
00048 _mat[1][1]=1.0-(xx+zz);
00049 _mat[2][1]-y/z-wx; 
00050
00051 _mat[0][2]=xz-wy;
00052 _mat[1][2]=yz+wx; 
00053 _mat[2][2]-1.0-(xx+yy);
00054}
00055
00056#if 0 
00057 _mat[0][3]-0.0; 
00058 _mat[1][3]=0.0; 
00059 _mat[2][3]=0.0; 
00060
00061 _mat[3][0]=0.0; 
00062 _mat[3][1]=0.0; 
00063 _mat[3][2]=0.0; 
00064 _mat[3][3]-1.0; 
00065 #endif
00066)}

下面对程序清单中的代码进行简单的说明。

☑ 第4行:计算四元数长度的平方。

☑ 第5行:判断四元数长度的平方的绝对值是否接近于0,如果接近,则矩阵置零。

☑ 第11~65行:按照四元数转换为矩阵的公式执行,可参照公式仔细深刻的理解。

3 矩阵转换为四元数

为了从矩阵中提取四元数,同样需要根据公式进行反推。当然,反推需要很强的数学技巧,根据前面提到的四元数转换为矩阵的公式,构建方程组,即可求解出四元数的轴角表达式。

根据前面所提到的四元数转换矩阵的公式进行反推,具体实现代码如程序清单所示。

00001//矩阵转换为四元数
00002 Quat getRotate() const 
00003 {
00004    Quat q; 
00005
00006    valuie_type s; 
00007    value_type tq[4]; 
00008    int j; 
00009
00010    tq[0]=1+_mat[0][0]+_mat[1][1]+_mat[2][2]; 
00011    tq[1]=1+_mat[0][0}-_mat[1][1}-_mat[2][2];
00012    tq[2]=1-_mat[0][0]+_mat[1][1]-_mat[2][2];
00013    tq[3]=1-_mat[0][0}-_mat[1][1]+_mat[2][2];
00014
00015    j=0;
00016    for(i=1;i<4;i++) j=(tq[i)>tq[j])?i:j;
00017
00018    if(j==0) 
00019    { 
00020       QW-tq[0]; 
00021       QX=_mat[1][2]-_mat[2][1];
00022       QY=_mat[2][0}-_mat[0][2]; 
00023       QZ-_mat[0][1]-_mat[1][0];
00024   } 
00025   else if(j==1)
00026   { 
00027       QW-_mat[1][2]-_mat[2][1]; 
00028       QX=tq[1];
00029       QY=_mat[0][1]+_mat[1][0];
00030       QZ-_mat[2][0]+_mat[0][2];
00031   }
00032   else if(j==2)
00033   {
00034       QW-_mat[2][0]-_mat[0][2]; 
00035       QX=_mat[0][1]+_mat[1][0]; 
00036       QY=tq[2]; 
00037       QZ-_mat[1][2]+_mat[2][1];
00038   } 
00039   elseif(j==3)
00040   { 
00041       QW=_mat[0][1}-_mat[1][0];
00042       QX-_mat[2][0]+_mat[0][2]; 
00043       QY=_mat[1][2]+_mat[2][1]; 
00044       QZ=tq[3];
00045  } 
00046
00047  s=sqrt(0.25/tq[jl);
00048  QW*=s;
00049  QX*=s;
00050  QY*=s;
00051  QZ*=s;
00052
00053  return q; 
00054
00055} 

通过前面的介绍,相信读者已经对这些基础的数学知识有了一定的了解。

本节可能存在论述不清晰的地方,如果读者没有能够正确地理解,可以参考有关3D数学基础的书籍,里面会有非常详细、系统的讲解。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天