1. 程式人生 > 實用技巧 >矩陣及變換,以及矩陣在DirectX和OpenGL中的運用問題:左乘 or 右乘,儲存問題:行優先 or 列優先,

矩陣及變換,以及矩陣在DirectX和OpenGL中的運用問題:左乘 or 右乘,儲存問題:行優先 or 列優先,

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.2OpenGL 採用列主序(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;