`

3D 基础矩阵推导

 
阅读更多

旋转矩阵

列矢量旋转通过下面的矩阵乘法,这个是2D的旋转:


\ {bmatrix} X'\ \ Y \ \ \ {bmatrix} = \ {bmatrix} \ COS \ THETA& -  \罪\ THETA \ \ \罪\ THETA&\ COS \ THETA \ \ \ { bmatrix} \开始{bmatrix} X \ \ Y \ \ \ {bmatrix}

上述矢量的旋转方向是逆时针方向,顺时针旋转矩阵:

注意:上述结果是建立在一个标准的右手坐标系的,所有,如果是右手坐标系,1图的结果是逆时针旋转,而如果使用的是左手坐标系,1图的结果则是顺时针旋转。

 

三个基本的3D旋转矩阵:

由于矩阵乘法表示的是转换的累积,所以任意轴旋转可以通过这三个矩阵乘来获取。

 

轴和角度旋转矩阵:

上述是给定三个角度,即延X,Y,Z轴旋转的角度来进行转换的旋转矩阵。但是并不是所有场合都很容易得到这些量,由此产生了轴和角度的旋转矩阵,通常我们会用四元数来描述这一组值,转换矩阵如下:

 

注意:这里的U是单位向量,所有在取得向量后得归一化一下。同样的,这是右手系结果,逆时针结果。

旋转矩阵的性质:

对于任何旋转矩阵R_ {A \ THETA} \ \ mathbb {R} ^ 3

  :旋转矩阵是正交矩阵

   :这个可以用于拆分旋转角,在特殊场合。

  :当角度为0的时候为单位矩阵,这也说明a其实是单位轴向量。

 

以四元数为参数的旋转矩阵:

四元数:  这里的z是归一化过的

这里有一点不清楚的是,a是什么?其实bcd很好说,方向向量嘛,但是a就那个啥了。

 

D3D中求旋转矩阵的方法:

D3DXMATRIXA16* MyMatrixRotationAxis(D3DXMATRIXA16* mat, D3DXVECTOR3* vec, float t)
{
    float cost = cos(t), sint = sin(t), one_sub_cost = 1 - cost;
    D3DXVECTOR3 v(*vec);
    D3DXVec3Normalize(&v, &v);
    *mat = D3DXMATRIXA16(
v.x * v.x * one_sub_cost + cost, v.x * v.y * one_sub_cost + v.z * sint, v.x * v.z * one_sub_cost - v.y * sint, 0, \
v.x * v.y * one_sub_cost - v.z * sint, v.y * v.y * one_sub_cost + cost, v.y * v.z * one_sub_cost + v.x * sint, 0, \
v.x * v.z * one_sub_cost + v.y * sint, v.y * v.z * one_sub_cost - v.x * sint, v.z * v.z * one_sub_cost + cost, 0, \
                        0, 0, 0, 1);
    return mat;
}

 此方法的实现对比2公式可知,D3DX系列得出的结果都是基于左手系的,但是是基于列向量的。由此,我们在使用的时候依旧需要转置一次。又因为,我们在使用旋转的时候,都是希望顺时针为正方向,所以,我们又需要转置一次,最终结果就是不变。结论:如果角度为顺时针,矩阵不需要转置,如果是逆时针则需要

// 获取的是一个绕任意轴旋转的旋转矩阵,对应第2个公式
D3DXMATRIX* WINAPI D3DXMatrixRotationAxis ( D3DXMATRIX *pOut, CONST D3DXVECTOR3 *pV, FLOAT Angle );

// 根据传入的四元数获取旋转矩阵
D3DXMATRIX* WINAPI D3DXMatrixRotationQuaternion ( D3DXMATRIX *pOut, CONST D3DXQUATERNION *pQ);

// 根据绕X,Y,Z轴旋转的角度获取 旋转矩阵
D3DXMATRIX* WINAPI D3DXMatrixRotationYawPitchRoll ( D3DXMATRIX *pOut, FLOAT Yaw, FLOAT Pitch, FLOAT Roll );
D3DXMATRIX* WINAPI D3DXMatrixRotationX ( D3DXMATRIX *pOut, FLOAT Angle );
D3DXMATRIX* WINAPI D3DXMatrixRotationY ( D3DXMATRIX *pOut, FLOAT Angle );
D3DXMATRIX* WINAPI D3DXMatrixRotationZ ( D3DXMATRIX *pOut, FLOAT Angle );

 D3DXMatrixRotationYawPitchRoll系列都是以当前坐标系的轴旋转的,同样是顺时针旋转 。四元数就不用看了,肯定也是顺时针旋转。

我们知道,矩阵*向量,和向量*矩阵所得到的结果是不一样的,那么上述方法只是获取到一个转换矩阵而已,它又不知道别人使用的时候乘的顺序是怎样的,它却义正言辞的在那里说自己的结论,微软比IBM蛋疼多了。

D3DXVECTOR4* WINAPI D3DXVec4Transform(D3DXVECTOR4 *pout, CONST D3DXVECTOR4 *pv, CONST D3DXMATRIX *pm)
{
    pout->x = pm->m[0][0] * pv->x + pm->m[1][0] * pv->y + pm->m[2][0] * pv->z + pm->m[3][0] * pv->w;
    pout->y = pm->m[0][1] * pv->x + pm->m[1][1] * pv->y + pm->m[2][1] * pv->z + pm->m[3][1] * pv->w;
    pout->z = pm->m[0][2] * pv->x + pm->m[1][2] * pv->y + pm->m[2][2] * pv->z + pm->m[3][2] * pv->w;
    pout->w = pm->m[0][3] * pv->x + pm->m[1][3] * pv->y + pm->m[2][3] * pv->z + pm->m[3][3] * pv->w;
    return pout;
}

  在C++里面所有的向量与矩阵乘都是使用的XXXTransform,XXX只的是向量的维度,上述是四维向量。从上述,参数顺序,以及代码可以看出。D3DX使用的标准顺势是 向量*矩阵的顺序,由此上述所有的结论也是建立在这个顺序上得到的,如果你的顺序错了,结果应该是不对的。

 

总结:我们在使用的时候在意的是什么呢?1.采用的是什么坐标系。2.旋转顺序是顺时针还是逆时针。3.传入的参数是列向量还是行向量,其实就是乘的时候矩阵在前还是在后,矩阵在前就是列向量。所以,D3DX为我们提供的API都是:1.左手系,2.顺时针为正,3.向量*矩阵。(API所描述的结果都是建立在这3个条件基础之上的,如果,在使用的时候其中一个条件不对,所得的结果就不正确,要想正确就得对矩阵进行转置,每一个条件需要转置一次。)

 

列向量:

行向量:

 

缩放矩阵:

相比于旋转矩阵,缩放矩阵来的简单多了,最嗨皮的一点是,所有的缩放因子都在矩阵的正轴上,这样的话向量与矩阵乘的顺序就无关了。

 

 

平移矩阵:

 

反射矩阵:

这个图上有两个点v和v1点,v1点是v的反射点,反射一起在高中的数学题里面就做过,其实就是求点在平面的法向量方向上移动的距离。这里面需要知道两个参数,一个是平面的法向量n,一个是v与平面的距离d,这样把v点延n移动2d的距离就行了。推导过程别人已经写了,这里把结果拿出来:

 

| -2nx*nx + 1       -2ny*nx           -2nz*nx           0 |

| -2nx*ny             -2ny*ny + 1     -2nz*ny          0 |

| -2nx*nz            -2ny*nz            -2nz*nz + 1     0 |

| -2nx*d              -2ny*d              -2nz*d            1 |

由于D3DX里面有获取反射矩阵的方法,所以这里就不研究这个矩阵的变换条件了。变换的条件有两个:一个是法向量的方向,一个平面是有两个法向量的,他们的方向正好相反。另外一个就是这个矩阵想得出正确的结果是要向量*矩阵还是矩阵*向量。用D3DX的函数有个好处是他统一规则都是向量*矩阵。

 

 

 

// 缩放矩阵
D3DXMATRIX* WINAPI D3DXMatrixScaling ( D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz );

// 平移矩阵
D3DXMATRIX* WINAPI D3DXMatrixTranslation ( D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z );

// 这个是转置,必须拿出来晒晒
D3DXMATRIX* WINAPI D3DXMatrixTranspose ( D3DXMATRIX *pOut, CONST D3DXMATRIX *pM );

// 反射矩阵 这里传入的参数是一个PLANE,相比顶点和法向量而已,PLANE更灵活,也许你只需要3个点作为参数就可以创建PLANE了,这样就省去了计算法向量的过程了
D3DXMATRIX* WINAPI D3DXMatrixReflect ( D3DXMATRIX *pOut, CONST D3DXPLANE *pPlane );

 

几个综合变换的函数:

 

// Mout = (Msc)-1 * (Msr)-1 * Ms * Msr * Msc * (Mrc)-1 * Mr * Mrc * Mt
// Mout = 输出矩阵 (pOut)
// Msc = 缩放中心矩阵 (pScalingCenter)
// Msr = 缩放旋转矩阵 (pScalingRotation)
// Ms = 缩放矩阵 (pScaling)
// Mrc = 旋转中心矩阵 (pRotationCenter)
// Mr = 旋转矩阵 (pRotation)
// Mt = 平移矩阵 (pTranslation)
D3DXMATRIX* WINAPI D3DXMatrixTransformation ( D3DXMATRIX *pOut, CONST D3DXVECTOR3 *pScalingCenter, CONST D3DXQUATERNION *pScalingRotation, CONST D3DXVECTOR3 *pScaling, CONST D3DXVECTOR3 *pRotationCenter, CONST D3DXQUATERNION *pRotation, CONST D3DXVECTOR3 *pTranslation);

// Mout = Ms * Mrc-1 * Mr * Mrc * Mt
// Mout = 输出矩阵 (pOut)
// Ms = 缩放矩阵 (Scaling)
// Mrc = 旋转矩阵中心 (pRotationCenter)
// Mr = 旋转矩阵 (pRotation)
// Mt = 平移矩阵 (pTranslation)
D3DXMATRIX* WINAPI D3DXMatrixAffineTransformation ( D3DXMATRIX *pOut, FLOAT Scaling, CONST D3DXVECTOR3 *pRotationCenter, CONST D3DXQUATERNION *pRotation, CONST D3DXVECTOR3 *pTranslation);
 这两个是生成综合变换矩阵的方法,所谓的放射就是对平移,旋转,缩放的综合体现。只不过这里多了个旋转中心的概念,我们在谈论旋转的时候把中心定位坐标系的原点,而这里是指定点。

 

原则上来说,优先考虑使用综合的方法获取矩阵,这样会减少使用的错误率。如果要分别获取然后组合,那么在组合的时候必须按照:先平移,再旋转,后缩放。

 

仿射矩阵:一个向量空间进行一次线性变换并接上一个平移

 如图,向量x进行了一次A矩阵变换和一次b向量平移,而y就是一次仿射变换。

 仿射矩阵因为有一次平移,所以会比向量多以列,然后为了乘把向量补一位,然后把矩阵补一行,就如上图结果了。这应该也是齐次式的作用吧。

 

D3DX矩阵的使用:

 

// Transform (x, y, z, 1) by matrix.
D3DXVECTOR4* WINAPI D3DXVec3Transform ( D3DXVECTOR4 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM );

// Transform (x, y, z, 1) by matrix, project result back into w=1.
D3DXVECTOR3* WINAPI D3DXVec3TransformCoord ( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM );

// Transform (x, y, z, 0) by matrix.  If you transforming a normal by a 
// non-affine matrix, the matrix you pass to this function should be the 
// transpose of the inverse of the matrix you would use to transform a coord.
D3DXVECTOR3* WINAPI D3DXVec3TransformNormal ( D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV, CONST D3DXMATRIX *pM );
D3DXVec3Transform :这个忽略吧,描述不是一般的少,或许是它内部用的呢。

 

D3DXVec3TransformCoord :这个用于坐标的转换,因为这个可以平移。

D3DXVec3TransformNormal :这个用于向量的转换,后面加的那句话网上有分歧,有的人说是自己提供转换矩阵的时候,先把矩阵逆然后转置然后作为参数传递进去。有的人说,这是它内部获取到后,进行的操作。反正网上有一位同学自己做了测试,证明是可以直接用的。

 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics