2013-07-22 82 views
4

我有以下功能:4x4矩陣前乘VS後乘

void Matrix::Scale(const float xScale, const float yScale, const float zScale) 
{ 
    Matrix scaleMatrix; 
    scaleMatrix.m_data[M11] = xScale; 
    scaleMatrix.m_data[M22] = yScale; 
    scaleMatrix.m_data[M33] = zScale; 
    *this *= scaleMatrix; 
} 

void Matrix::Translate(const float xTranslation, const float yTranslation, const float zTranslation) 
{ 
    Matrix translationMatrix; 
    translationMatrix.m_data[M14] = xTranslation; 
    translationMatrix.m_data[M24] = yTranslation; 
    translationMatrix.m_data[M34] = zTranslation; 
    *this *= translationMatrix; 
} 

而且我不確定這兩種功能中的最後幾行。我應該做前乘法還是後乘法(即我現在在做什麼)。它對使用這個類有什麼影響?我使用OpenGL的類,所以任何相似之處可能會有用。

編輯:

我的shader代碼看起來是這樣的:

void main() 
{ 
    gl_Position = vec4(v_xy, 0.0, 1.0) * v_ModelMatrix * v_ViewMatrix * v_ProjectionMatrix; 
    f_uv = v_uv; 
} 

我的矩陣乘法功能如下所示:

// Row 1 
result[M11] = lhs[M11] * rhs[M11] + lhs[M12] * rhs[M21] + lhs[M13] * rhs[M31] + lhs[M14] * rhs[M41]; // Column 1 
result[M12] = lhs[M11] * rhs[M12] + lhs[M12] * rhs[M22] + lhs[M13] * rhs[M32] + lhs[M14] * rhs[M42]; // Column 2 
result[M13] = lhs[M11] * rhs[M13] + lhs[M12] * rhs[M23] + lhs[M13] * rhs[M33] + lhs[M14] * rhs[M43]; // Column 3 
result[M14] = lhs[M11] * rhs[M14] + lhs[M12] * rhs[M24] + lhs[M13] * rhs[M34] + lhs[M14] * rhs[M44]; // Column 4 

// Row 2 
result[M21] = lhs[M21] * rhs[M11] + lhs[M22] * rhs[M21] + lhs[M23] * rhs[M31] + lhs[M24] * rhs[M41]; // Column 1 
result[M22] = lhs[M21] * rhs[M12] + lhs[M22] * rhs[M22] + lhs[M23] * rhs[M32] + lhs[M24] * rhs[M42]; // Column 2 
result[M23] = lhs[M21] * rhs[M13] + lhs[M22] * rhs[M23] + lhs[M23] * rhs[M33] + lhs[M24] * rhs[M43]; // Column 3 
result[M24] = lhs[M21] * rhs[M14] + lhs[M22] * rhs[M24] + lhs[M23] * rhs[M34] + lhs[M24] * rhs[M44]; // Column 4 

// Row 3 
result[M31] = lhs[M31] * rhs[M11] + lhs[M32] * rhs[M21] + lhs[M33] * rhs[M31] + lhs[M34] * rhs[M41]; // Column 1 
result[M32] = lhs[M31] * rhs[M12] + lhs[M32] * rhs[M22] + lhs[M33] * rhs[M32] + lhs[M34] * rhs[M42]; // Column 2 
result[M33] = lhs[M31] * rhs[M13] + lhs[M32] * rhs[M23] + lhs[M33] * rhs[M33] + lhs[M34] * rhs[M43]; // Column 3 
result[M34] = lhs[M31] * rhs[M14] + lhs[M32] * rhs[M24] + lhs[M33] * rhs[M34] + lhs[M34] * rhs[M44]; // Column 4 

// Row 4 
result[M41] = lhs[M41] * rhs[M11] + lhs[M42] * rhs[M21] + lhs[M43] * rhs[M31] + lhs[M44] * rhs[M41]; // Column 1 
result[M42] = lhs[M41] * rhs[M12] + lhs[M42] * rhs[M22] + lhs[M43] * rhs[M32] + lhs[M44] * rhs[M42]; // Column 2 
result[M43] = lhs[M41] * rhs[M13] + lhs[M42] * rhs[M23] + lhs[M43] * rhs[M33] + lhs[M44] * rhs[M43]; // Column 3 
result[M44] = lhs[M41] * rhs[M14] + lhs[M42] * rhs[M24] + lhs[M43] * rhs[M34] + lhs[M44] * rhs[M44]; // Column 4 

我的印象是,如果你發佈 - 乘上你的矩陣(即viewMatrix = transform * viewMatrix;),那麼你的着色器代碼需要按照我現在所用的相反順序應用MVP?

EDIT2:

http://scratchapixel.com/lessons/3d-basic-lessons/lesson-4-geometry/conventions-again-row-major-vs-column-major-vector/彙總表是混淆了我,因爲我使用的是用OpenGL後乘(指示列爲主),但我的矩陣,在內存佈局爲行主?

+4

這不是一個C++的問題,而只是一個mathmatrix問題。對我而言,這取決於你的慣例:如果你所有的變換都是右乘法,那麼一個比例/平移也應該是一個正確的乘法。 – Synxis

+0

隨着縮放,它沒有區別,因爲縮放只包含對角矩陣。翻譯雖然不同,但它不同。 – arne

+2

@arne它*確實與縮放有差異。左乘以對角線矩陣縮放行,而右乘法縮放列。 –

回答

15

你似乎在這裏混合兩個問題,這是我猜想什麼網頁上的scratchapixel正試圖解釋。從你提到的網頁上的信息來看,事情看起來很清楚,但把這種東西放在頭腦裏很難。你有理論(你用數學筆和紙做的)和你的實現(C++)。這是兩個不同的問題。

數學:你可以使用兩個符號,列或行主要。正如GraphicsMuncher在這個網頁中所提到的那樣,對於行主矢量,在紙面上,你需要寫矢量矩陣乘法vM,其中v是行向量(1x4),M是你的4x4矩陣,爲什麼因爲你可以用數學方法只寫[1x4] * [4x4],而不是相反。同樣,如果使用列,則矢量需要垂直寫入,或者使用符號[4x1](4行,1列)。因此,與矩陣的乘法只能寫成:[4x4] [4x1]。矩陣放在矢量Mv的前面。第一個符號稱爲後乘法,第二個符號稱爲前乘法(矩陣在前面)。 現在,正如GraphicsMuncher所提到的那樣,如果您需要轉換矢量(或點),那麼當您在ON PAPER上寫下它們時,需要注意乘法的順序。如果你想用矩陣T翻譯某些東西,然後用R旋轉然後用S來縮放,那麼在一個專欄的主要世界中,你需要寫出v'= S * R * T * v。寫v'= v * T * R * S.

這就是理論。

計算機:那麼當你決定在C++中實現這一點時就會出現這種情況。關於這一點的好處是,C++不會強加給你任何關於任何東西的東西。您可以按照自己想要的方式將矩陣係數的值映射到內存中,並且可以編寫代碼以您希望的方式執行另一個矩陣的矩陣乘法。同樣,如何訪問矢量矩陣乘法的係數完全取決於您。

您需要明確區分如何將係數映射到內存中,以及需要從數學角度使用哪些約定來表示向量。這是兩個獨立的問題。例如,在你的情況下,你可能會聲明你的矩陣類爲16個連續的浮點數組。沒關係。在係數m14,m24,m34表示矩陣(Tx,Ty,Tz)的翻譯部分的情況下,即使您被告知要使用OpenGL矩陣約定(稱爲列矩陣約定),您仍然認爲您的「約定」重大的。這裏你的困惑來自這樣一個事實,即記憶係數的映射與你自己創建的「列主要」矩陣的心理表徵不同。你編碼「行」,但你說(使用從數學的角度來看)「列」,因此你難以理解你做對或錯的事情。

重要的是看一個矩陣作爲由三個軸定義的座標系和一個平移的表示。您在何處以及如何將這些數據存儲在內存中完全取決於您。假設表示座標系的三個軸的三個矢量被命名爲AX(x,y,z),AY(x,y,z),AZ(x,y,z),並且平移矢量由(Tx ,泰,Tz的),那麼數學,如果你使用你(不支持乳膠列向量我猜):

AXx AYx AZx Tx 
M = AXy AYy AZy Ty 
    AXz AYz AZz Tz 
    0 0 0 1 

座標系的軸垂直寫入。現在,如果您使用行主要:

AXx AXy AXz 0 
M = AYx AYy AYz 0 
    AZx AZy AZz 0 
    Tx Ty Tz 1 

座標系的軸水平寫入。所以當涉及到計算機世界時,問題是如何將這些係數存儲在內存中。你也可以這樣做:

float m[16] = { AXx, AXy, AXz, 0, AYx, AYy, AYz, 0, AZx, AZy, AZz, 0, Tx, Ty, Tz, 1}; 

它是否告訴你,你使用哪種約定?沒有。你也可以這樣寫:

float m[16] = { AXx, AXy, AXz, Tx, AYx, AYy, AYz, Ty, AZx, AZy, AZz, Tz, 0, 0, 0, 1}; 

float m[16] = { AXx, AYx, AZx, Tx, AXy, AYy, AZy, Ty, AXz, AYz, AZz, Tz, 0, 0, 0, 1}; 

再次,不給你其中「數學」約定的使用特定指示。你只是以不同的方式在存儲器中存儲16個係數,只要你知道這個方法是什麼,那麼你可以在稍後進行適當的訪問。現在請記住,無論使用行還是列 - 數學符號,乘以矩陣的矢量都會給出相同的矢量。因此,真正重要的是,將矢量的(x,y,z)座標乘以矩陣的正確係數,這需要知道「你」如何決定將矩陣係數存儲在存儲器中:

Vector3 vecMatMult (Vector3 v, 
    float AXx, float AXy, float AXz, float Tx, 
    float AYx, float AYy, float AYz, float Ty, 
    float AZz, float AZy, float AZz, float Tz) { 
    return Vector3(
     v.x * AXx + v.y * AYx + v.z * AZx + Tx, 
     v.x * AXy + v.y * AYy + v.z * AZy + Ty, 
     v.x * AXz + v.y * AYz + v.z * AZz + Tz 
} 

編輯:上面的代碼是錯誤的,現在修復它。

我寫了這個函數來強調一個事實,無論您使用哪種慣例,矢量*矩陣乘法的結果只是矢量的輸入座標和座標系的軸座標AX,AY和AZ(不管你使用的符號是什麼,也不管你將它們存儲在內存中的方式)。

如果你使用:

float m[16] = { AXx, AXy, AXz, 0, AYx, AYy, AYz, 0, AZx, AZy, AZz, 0, Tx, Ty, Tz, 1}; 

你需要調用:

vecMatMult(v, m[0], m[1], m[2], m[12], m[4], m[5], m[6], m[13], ... 

如果你使用:

float m[16] = { AXx, AYx, AZx, Tx, AXy, AYy, AZy, Ty, AXz, AYz, AZz, Tz, 0, 0, 0, 1}; 

你需要調用:

vecMatMult(v, m[0], m[4], m[8], m[3], m[1], m[5], m[9], m[10], ... 

這是否告訴你使用哪種約定?不,你只需要在正確的地方調用正確的係數,當你做一個vec * mat乘法。這就是它的全部,就像它看起來令人不安。

當談到mat * mat乘法時,現在的情況稍有不同。你可以假設你乘以矩陣的順序是不一樣的。所以R * S * T與T * S * R不一樣。順序確實很重要。現在,如果再次使用 「行大」,那麼數學,你需要(用你的符號)來寫:

MT11 = ML11 * MR11 + ML12 * MR21 + M13 * M31 + ml14 * mr41

其中M1是左手矩陣和mr右手一:mt = ml * mr。不過請注意,我沒有使用括號[]作爲訪問索引,因爲我不想建議我們在這裏訪問存儲在一維數組中的元素。我們只是在談論矩陣的係數。如果你想用C++編寫它,那麼這完全取決於你如何將係數存儲在內存中,如上所述。

希望它有幫助。

+0

很棒的回答。謝謝。 –

+0

@Mark,vecMatMult函數有錯誤。希望你沒有被這個困惑。錯誤現在得到糾正。很高興幫助。 – user18490

2

矩陣是關聯的,這意味着

ABC = (AB)C = A(BC) 

所以它並不重要的矩陣,你居然乘在一起的第一(AB vs BC),只要你保持順序相同。前和後乘法是AB vs BA

這就是說,this question解釋瞭如果您希望您的轉換以您想要的方式出現,應該乘以縮放/旋轉/平移的順序。如果您使用行矩陣而不是列矩陣,則反轉順序並轉置每個元素。 Here's a slideshow給出了一個更好的解釋(跳轉到第19張幻燈片)。

嚴格在矩陣的方面,列矩陣排列Ť * ř * 小號,或翻譯*旋轉*比例。所以,如果你從身份I開始,後乘法(你在做什麼)是正確的。如果更改爲預乘的X通過升的轉變最終將成爲大號 * X,所以爲了得到牛逼 * [R * 小號你會翻轉您執行呼叫的順序。

+0

這是否意味着前和後乘法的唯一連鎖效應將會在着色器中出現?即頂點* MVP或PVM *頂點? –

+1

我不確定你的意思。一般情況下,'gl_Position = MVP * in_Position'和'MV = Scale * Rotation * Translation'如果這就是你要求的。 – GraphicsMuncher

+0

我已經爲我的問題添加了更多信息,包括着色器示例。 –

0

創建一個類,矩陣,以保存實數4x1大小的矩陣。將 保存在數組中。在主體中,創建兩個對象,A和B爲由用戶填充的 。創建另一個對象C. C實際上是A 和B的總和。您應該有一個函數add,它接收兩個矩陣對象並將它們的總和作爲單獨的矩陣返回。 matrix C = add(A,B); 在屏幕上顯示三個對象在適當format.in C++