菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
286
0

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

原创
05/13 14:22
阅读数 94850

1. 矢量和矩阵的乘法的线性代数表示

  首先,无论Direct3D还是opengl,所表示的矢量和矩阵都是依据线性代数中的标准定义的:“矩阵A与B的乘积为矩阵C,则C的第i行第j列的元素c(ij)等于A的第i行与B的第j列的对应元素乘积的和。”(实用数学手册,科学出版社,第二版)例如c12 = a11*b11+a12*b21+a12*b13...

2. “矩阵的存储方式”

  矩阵存储方式有两种,一种是“行主序(row-major order)/行优先”,另一种就是“列主序(column-major order)/列优先”/

2.1 Direct3D 采用行主序(Row major)存储

“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.”

2.2 OpenGL 采用列主序(Colume major)存储

“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”

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

  因此,对于线程代数中的同一个矩阵,则在d3d和OpenGL中有不同的表示形式:

                  线代矩阵:a11,a12,a13,a14            d3d保存:  a11,a12,a13,a14            OpenGL保存: 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

 

3. 矩阵乘法顺序和规则

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

  这个规则取决于vector的表示形式,即行向量还是列向量。

  如果是行向量,其实就是一个行矩阵。那么表示线性代数意义的“行x列”,就是前乘。矩阵乘法也是如此。

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

     (1)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中的向量是采用Col-major保存的,即将向量需要保存为列向量形式。从而在执行向量和矩阵的乘法的时候,需要将“列”保存的【向量】,还原为“行”向量,然后采用“标准向量与矩阵相乘”的标准规则进行进行计算即可。

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

    A*B = B‘ *A’= C

    由于在OpenGL中A和B都是列为主的矩阵,B’ * A‘ 的效果,就是用B的列 向量 X A的行向量,从而就是运算后的矩阵中的每一个列的值。

         从下列代码中可以看出,计算的结果还是以Col-Major保存的矩阵。且C的每个列向量中的每个元素即为:B的列向量与A的行向量 逐个乘机的和。

/*
** 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;
  }
}

 

 

 

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

      

 

 

 

 

 

 

 

 

 

endl;

 

发表评论

0/200
286 点赞
0 评论
收藏
为你推荐 换一批