您的位置:首页 > 运维架构

矩阵及变换,以及矩阵在DirectX和OpenGL中的运用问题:左乘/右乘,行优先/列优先,...

2010-10-20 22:10 393 查看
(一)首先,无论dx还是opengl,所表示的矢量和矩阵都是依据线性代数中的标准定义的:

“矩阵A与B的乘积矩阵C的第i行第j列的元素c(ij)等于A的第i行于B的第j列的对应元素乘积的和。”(实用数学手册,科学出版社,第二版)

例如c12 = a11*b11+a12*b21+a12*b13...

(二)在明确了这一点后,然后我们再看“矩阵的存储方式”,矩阵存储方式有两种,一种是“行主序(row-major order)/行优先”,另一种就是“列主序(column-major order)/列优先”

1)Direct3D 采用行主序存储



“Effect matrix parameters and HLSL matrix variables can define whether the value is a row-major or column-major matrix; however, the DirectX APIs always treat D3DMATRIX and D3DXMATRIX as row-major.”(见d3d9 document/Casting and Conversion 一节)

2)OpenGL 采用列主序存储

“The m parameter points to a 4x4 matrix of single- or double-precision floating-point values stored in column-major order. That is, the matrix is stored as follows”

(见msdn glLoadMatrixf API说明)

存储顺序说明了线性代数中的矩阵如何在线性的内存数组中存储,d3d 将每一行在数组中按行存储,而opengl将每一列存储到数组的每一行中:

     线性代数意义的同一个矩阵,在d3d 和 ogl 中却有不同的存储顺序

线代:a11,a12,a13,a14 d3d : a11,a12,a13,a14 gl: a11,a21,a31,a41

a21,a22,a23,a24 a21,a22,a23,a24 a12,a22,a32,a42

a31,a32,a33,a34 a31,a32,a33,a34 a13,a23,a33,a43

a41,a42,a43,a44 a41,a42,a43,a44 a14,a24,a34,a44

(三)矩阵乘法顺序和规则

矩阵乘法在线性代数中的定义是确定的,然而在不同的实现中出现了“左乘”和“右乘”的区别,或者叫做“前乘(pre-multiply),后乘(post-multiply)”

这个规则取决于vector的表示形式,即行向量还是列向量。如果是行向量,其实就是一个行矩阵。那么表示线性代数意义的“行x列”,就是前乘。矩阵乘法也是如此。

如d3d中,





D3D 是行向量,行优先存储,OpenGL是列向量,列优先存储。同一个矩阵用D3D存储还是用opengl存储虽然不同,但是变换的结果却是相同,

因为opengl 变换向量是把向量视作列向量,并同矩阵的每一列相乘,用来实现线性代数中同一个变换。

我们通常很难看到opengl变换坐标的代码,以下代码出自opengl source code,让我们一窥顶点变换的“庐山真面目”

void FASTCALL __glXForm3(__GLcoord *res, const __GLfloat v[3], const __GLmatrix *m)

{

__GLfloat x = v[0];

__GLfloat y = v[1];

__GLfloat z = v[2];

res->x = x*m->matrix[0][0] + y*m->matrix[1][0] + z*m->matrix[2][0]

+ m->matrix[3][0];

res->y = x*m->matrix[0][1] + y*m->matrix[1][1] + z*m->matrix[2][1]

+ m->matrix[3][1];

res->z = x*m->matrix[0][2] + y*m->matrix[1][2] + z*m->matrix[2][2]

+ m->matrix[3][2];

res->w = x*m->matrix[0][3] + y*m->matrix[1][3] + z*m->matrix[2][3]

+ m->matrix[3][3];

}

可见确实如上所述,“OPENGL列向量和矩阵的每一列相乘,仍然表示线性代数行向量和矩阵的每一行相乘”

再来看一下opengl 矩阵相乘,“用a的每一列去乘b的每一行”。

/*

** Compute r = a * b, where r can equal b.

*/

void FASTCALL __glMultMatrix(__GLmatrix *r, const __GLmatrix *a, const __GLmatrix *b)

{

__GLfloat b00, b01, b02, b03;

__GLfloat b10, b11, b12, b13;

__GLfloat b20, b21, b22, b23;

__GLfloat b30, b31, b32, b33;

GLint i;

b00 = b->matrix[0][0]; b01 = b->matrix[0][1];

b02 = b->matrix[0][2]; b03 = b->matrix[0][3];

b10 = b->matrix[1][0]; b11 = b->matrix[1][1];

b12 = b->matrix[1][2]; b13 = b->matrix[1][3];

b20 = b->matrix[2][0]; b21 = b->matrix[2][1];

b22 = b->matrix[2][2]; b23 = b->matrix[2][3];

b30 = b->matrix[3][0]; b31 = b->matrix[3][1];

b32 = b->matrix[3][2]; b33 = b->matrix[3][3];

for (i = 0; i < 4; i++) {

r->matrix[i][0] = a->matrix[i][0]*b00 + a->matrix[i][1]*b10

+ a->matrix[i][2]*b20 + a->matrix[i][3]*b30;

r->matrix[i][1] = a->matrix[i][0]*b01 + a->matrix[i][1]*b11

+ a->matrix[i][2]*b21 + a->matrix[i][3]*b31;

r->matrix[i][2] = a->matrix[i][0]*b02 + a->matrix[i][1]*b12

+ a->matrix[i][2]*b22 + a->matrix[i][3]*b32;

r->matrix[i][3] = a->matrix[i][0]*b03 + a->matrix[i][1]*b13

+ a->matrix[i][2]*b23 + a->matrix[i][3]*b33;

1。矩阵和线性变换:一一对应

矩阵是用来表示线性变换的一种工具,它和线性变换之间是一一对应的。

考虑线性变换:

a11*x1 + a12*x2 + ...+a1n*xn = x1'

a21*x1 + a22*x2 + ...+a2n*xn = x2'

...

am1*x1 + am2*x2 + ...+amn*xn = xm'

对应地,用矩阵来表示就是:

|a11 a12 ... a1n | |x1| |x1'|

|a21 a22 ... a2n | |x2| |x2'|

|... |* |...|= |... |

|am1 am2 ... amn | |xn| |xm'|

也可以如下来表示:

|a11 a21 ... am1|

|a12 a22 ... am2|

|x1 x2...xn|*|... |= |x1' x2'... xm'|

|a1n a2n ... amn|

其中涉及到6个矩阵。分别为A[m*n],X[n*1],X'[m*1]以及X[1*n],A[n*m],X'[1*m]。

可以理解成向量x(x1,x2,...,xn)经过一个变换矩阵A[m*n]或A[n*m]后变成另外一个向量x'(x1',x2',...,xm'))。



2。矩阵的表示法:行矩阵 vs. 列矩阵


行矩阵和列矩阵的叫法是衍生自行向量和列向量。

其实,矩阵A[m*n]可以看成是m个n维的row vector构成的row matrix,也可看成是n个m维的column vector构成的column matrix。

其中,X[n*1]/X'[m*1]就等价于1个n/m维的column vector。X[1*n]/X'[1*m]就等价于1个n/m维的row vector。

Row matrix和Column matrix只是两种不同的表示法,前者表示把一个向量映射到矩阵的一行,后者表示把一个向量映射到矩阵的一列。

本质上体现的是同一线性变换。矩阵运算规定了它们可以通过转置运算来改变这个映射关系。



3。矩阵的相乘顺序:前乘或左乘 vs. 后乘或右乘


需要注意的是两种不同的表示法对应不同的运算顺序:

如果对一个column vector做变换,则变换矩阵(row matrix/vectors)必须出现在乘号的左边,即pre-multiply,又叫前乘或左乘。

如果对一个row vector做变换,则变换矩阵(column matrix/vectors)必须出现在乘号的右边,即post-multiply,又叫后乘或右乘。

一般不会弄错,因为矩阵乘法性质决定了相同的内维数的矩阵才能相乘。至于为什么是这个规律,为什么要row vector乘以column vector或column vector乘以row vector???想想吧。。。

所以左乘还是右乘,跟被变换的vector的表示形式相关,而非存储顺序决定。

4。矩阵的存储顺序:按行优先存储 vs. 按列优先存储

涉及到在计算机中使用矩阵时,首先会碰到存储矩阵的问题。

因为计算机存储空间是先后有序的,如何存储A[m*n]的m*n个元素是个问题,一般有两种:按行优先存储和按列优先存储。

row-major:存成a11,a12,...,amn的顺序。

column-major:存成a11,a21,...,amn的顺序。

这样问题就来了,给你一个存储好的矩阵元素集合,你不知道如何读取元素组成一个矩阵,比如你不知道a12该放在几行几列上。

所以,每个系统都有自己的规定,比如以什么规则存储的就以什么规则读取。DX使用Row-major,OGL使用Column-major.即一个相同的矩阵A[m*n]在DX和OGL中的存储序列是不一样的,这带来了系统间转换的麻烦。

不过,一个巧合的事情是:DX中,点/向量是用Row Vector来表示的,所以对应的变换矩阵是Column Matrix/Vectors,而OGL中,点/向量是用Column Vector来表示的,所以对应的变换矩阵是Row Matrix/Vectors.所以,如果在DX中对一个向量x(x1,x2,x3,1)或点(x(x1,x2,x3,1))应用A[4*4]的矩阵变换,就是x' = x(x1,x2,x3,1) * A[4*4],由于采用Row-major,所以它的存储序列是a11,a12,...,a43,a44。在OGL中,做同样的向量或点的变换,因为其使用Row
Matrix/Vectors,其应用的变换矩阵应该是A'[4*4] = A[4*4]( ' 表示Transpose/转置),就是x' = A'[4*4] * x'(x1,x2,x3,1),但是由于采用Column-major,它的存储序列正好也是a11,a12,...,a43,a44!!!

所以实际上,对DX和OGL来讲,同一个变换,存储的矩阵元素序列是一样的.比如:都是第13,14,15个元素存储了平移变化量deltaZ,deltaY,deltaZ.

Refs:
http://mathworld.wolfram.com/Matrix.html http://www.gamedev.net/community/forums/topic.asp?topic_id=321862
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: