Ai đó có thể giải thích (lý do cho) ý nghĩa của colum so với hàng chính trong phép nhân / ghép?


11

Tôi đang cố gắng học cách xây dựng các ma trận xem và chiếu, và tiếp tục gặp khó khăn trong quá trình thực hiện do sự nhầm lẫn của tôi về hai tiêu chuẩn cho ma trận.
Tôi biết cách nhân một ma trận và tôi có thể thấy rằng việc hoán vị trước khi nhân sẽ thay đổi hoàn toàn kết quả, do đó cần phải nhân theo một thứ tự khác.

Tuy nhiên, điều tôi không hiểu là "quy ước công chứng" nghĩa là gì - từ các bài báo ở đâyở đây, các tác giả dường như khẳng định rằng nó không có gì khác biệt đối với cách ma trận được lưu trữ hoặc chuyển sang GPU, nhưng về mặt thứ hai trang đó ma trận rõ ràng không tương đương với cách nó sẽ được trình bày trong bộ nhớ cho hàng chính; và nếu tôi nhìn vào một ma trận đông dân trong chương trình của mình, tôi thấy các thành phần dịch chiếm các phần tử thứ 4, 8 và 12.

Cho rằng:

"nhân sau với ma trận cột chính tạo ra kết quả tương tự như nhân trước với ma trận hàng lớn."

Tại sao trong đoạn mã sau:

        Matrix4 r = t3 * t2 * t1;
        Matrix4 r2 = t1.Transpose() * t2.Transpose() * t3.Transpose();

Liệu r! = R2tại sao pos3! = Pos cho :

        Vector4 pos = wvpM * new Vector4(0f, 15f, 15f, 1);
        Vector4 pos3 = wvpM.Transpose() * new Vector4(0f, 15f, 15f, 1);

Liệu quá trình nhân có thay đổi tùy thuộc vào việc ma trận là hàng hay cột chính hay chỉ là thứ tự (cho một hiệu ứng tương đương?)

Một điều không giúp điều này trở nên rõ ràng hơn, đó là khi được cung cấp cho DirectX, ma trận WVP chính của cột của tôi được sử dụng thành công để chuyển đổi các đỉnh với lệnh gọi HLSL: mul (vectơ, ma trận) sẽ dẫn đến vectơ được xử lý như hàng chính , vậy làm thế nào để ma trận chính cột được cung cấp bởi thư viện toán học của tôi hoạt động?



Câu trả lời:


11

Nếu tôi nhìn vào một ma trận đông dân trong chương trình của mình, tôi thấy các thành phần dịch chiếm các phần tử thứ 4, 8 và 12.

Trước khi tôi bắt đầu, điều quan trọng là phải hiểu: điều này có nghĩa là ma trận của bạn là hàng chính . Do đó, bạn trả lời cho câu hỏi này:

Ma trận WVP chính của cột của tôi được sử dụng thành công để biến đổi các đỉnh với lệnh gọi HLSL: mul (vectơ, ma trận) dẫn đến vectơ được coi là hàng chính, vậy ma trận chính của cột được cung cấp bởi thư viện toán học của tôi có thể hoạt động như thế nào?

là khá đơn giản: ma trận của bạn là hàng chính.

Vì vậy, nhiều người sử dụng ma trận hàng lớn hoặc ma trận chuyển tiếp, họ quên rằng ma trận không được định hướng theo cách tự nhiên. Vì vậy, họ thấy một ma trận dịch như sau:

1 0 0 0
0 1 0 0
0 0 1 0
x y z 1

Đây là một ma trận dịch chuyển . Đó không phải là một ma trận dịch bình thường trông như thế nào. Bản dịch đi trong cột thứ 4 , không phải hàng thứ tư. Đôi khi, bạn thậm chí nhìn thấy điều này trong sách giáo khoa, đó là rác hoàn toàn.

Thật dễ dàng để biết liệu một ma trận trong một mảng là hàng hay cột chính. Nếu là hàng chính, thì bản dịch được lưu trữ trong các chỉ số 3, 7 và 11. Nếu đó là cột chính, thì bản dịch được lưu trữ trong các chỉ số 12, 13 và 14. Tất nhiên các chỉ số cơ sở.

Sự nhầm lẫn của bạn bắt nguồn từ việc tin rằng bạn đang sử dụng ma trận cột lớn khi thực tế bạn đang sử dụng các ma trận hàng chính.

Tuyên bố rằng hàng so với cột chính là một quy ước công chứng chỉ hoàn toàn đúng. Các cơ chế của phép nhân ma trận và phép nhân ma trận / vectơ là như nhau bất kể quy ước.

Những thay đổi là ý nghĩa của kết quả.

Một ma trận 4 x 4 sau tất cả chỉ là một lưới số 4 x 4. Nó không đề cập đến một sự thay đổi của hệ tọa độ. Tuy nhiên, một khi bạn gán ý nghĩa cho một ma trận cụ thể, bây giờ bạn cần biết những gì được lưu trữ trong nó và làm thế nào để sử dụng nó.

Lấy ma trận dịch tôi chỉ cho bạn ở trên. Đó là một ma trận hợp lệ. Bạn có thể lưu trữ ma trận đó theo một float[16]trong hai cách:

float row_major_t[16] =    {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1};
float column_major_t[16] = {1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1};

Tuy nhiên, tôi đã nói rằng ma trận dịch thuật này là sai, bởi vì bản dịch không đúng chỗ. Tôi đặc biệt nói rằng nó được hoán vị so với quy ước chuẩn về cách xây dựng ma trận dịch thuật, nó phải giống như thế này:

1 0 0 x
0 1 0 y
0 0 1 z
0 0 0 1

Hãy xem cách chúng được lưu trữ:

float row_major[16] =    {1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1};
float column_major[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1};

Lưu ý rằng column_majorgiống hệt như row_major_t. Vì vậy, nếu chúng ta lấy một ma trận dịch phù hợp và lưu trữ nó dưới dạng cột chính, thì cũng giống như hoán vị ma trận đó và lưu trữ nó dưới dạng hàng chính.

Đó là những gì có nghĩa là chỉ là một quy ước công chứng. Thực sự có hai bộ quy ước: lưu trữ bộ nhớ và chuyển vị. Bộ nhớ lưu trữ là cột so với hàng chính, trong khi chuyển vị là bình thường so với chuyển đổi.

Nếu bạn có một ma trận được tạo theo thứ tự hàng lớn, bạn có thể có được hiệu ứng tương tự bằng cách hoán đổi tương đương với cột chính của ma trận đó. Và ngược lại.

Phép nhân ma trận chỉ có thể được thực hiện theo một cách: đưa ra hai ma trận, theo một thứ tự cụ thể, bạn nhân các giá trị nhất định lại với nhau và lưu trữ kết quả. Bây giờ, A*B != B*Anhưng mã nguồn thực tế A*Bgiống như mã cho B*A. Cả hai đều chạy cùng một mã để tính toán đầu ra.

Mã nhân ma trận không quan tâm đến việc ma trận xảy ra được lưu trữ theo thứ tự cột chính hay hàng chính.

Điều tương tự không thể được nói cho phép nhân vectơ / ma trận. Và đây là lý do.

Phép nhân vectơ / ma trận là một sự giả dối; nó không thể được thực hiện Tuy nhiên, bạn có thể nhân một ma trận với một ma trận khác. Vì vậy, nếu bạn giả vờ một vectơ là một ma trận, thì bạn có thể thực hiện phép nhân vectơ / ma trận một cách hiệu quả, chỉ đơn giản bằng cách thực hiện phép nhân ma trận / ma trận.

Một vectơ 4D có thể được coi là một vectơ cột hoặc một vectơ hàng. Nghĩa là, một vectơ 4D có thể được coi là ma trận 4x1 (hãy nhớ: trong ký hiệu ma trận, số hàng xuất hiện trước) hoặc ma trận 1x4.

Nhưng đây là điều: Cho hai ma trận A và B, A*Bchỉ được xác định nếu số cột của A bằng với số hàng của B. Do đó, nếu A là ma trận 4 x 4 của chúng ta, B phải là một ma trận có 4 hàng trong đó. Do đó, bạn không thể thực hiện A*x, trong đó x là một vectơ hàng . Tương tự, bạn không thể thực hiện x*Atrong đó x là một vectơ cột.

Bởi vì điều này, hầu hết các thư viện toán học ma trận đều đưa ra giả định này: nếu bạn nhân một vectơ nhân với một ma trận, bạn thực sự có nghĩa là thực hiện phép nhân thực sự hoạt động , chứ không phải là vô nghĩa.

Hãy để chúng tôi xác định, cho bất kỳ vector 4D x, sau đây. Csẽ là dạng ma trận vectơ cột của xRsẽ là dạng ma trận vectơ hàng của x. Với điều này, đối với bất kỳ ma trận A 4 nào, A*Cbiểu thị ma trận nhân A với vectơ cột x. Và R*Abiểu diễn ma trận nhân vectơ hàng xvới A.

Nhưng nếu chúng ta xem xét điều này bằng toán học ma trận nghiêm ngặt, chúng ta sẽ thấy rằng những điều này không tương đương . R*A không thể giống như A*C. Điều này là do một vectơ hàng không giống với vectơ cột. Chúng không phải là cùng một ma trận, vì vậy chúng không tạo ra kết quả giống nhau.

Tuy nhiên, chúng có liên quan theo một cách. Đúng là như vậy R != C. Tuy nhiên, cũng đúng , trong đó T là hoạt động chuyển vị. Hai ma trận là hoán vị của nhau.R = CT

Đây là một sự thật buồn cười. Vì các vectơ được coi là ma trận, nên chúng cũng có một cột so với câu hỏi lưu trữ chính hàng. Vấn đề là cả hai đều trông giống nhau . Mảng phao là như nhau, vì vậy bạn không thể biết sự khác biệt giữa R và C chỉ bằng cách nhìn vào dữ liệu. Cách duy nhất để nói lên sự khác biệt là cách chúng được sử dụng.

Nếu bạn có bất kỳ hai ma trận A và B nào và A được lưu dưới dạng hàng chính và B là cột chính, việc nhân chúng là hoàn toàn vô nghĩa . Bạn nhận được vô nghĩa như là kết quả. Vâng, không thực sự. Về mặt toán học, những gì bạn nhận được là tương đương với việc làm . Hoặc ; chúng giống hệt nhau về mặt toán học.AT*BA*BT

Do đó, phép nhân ma trận chỉ có ý nghĩa nếu hai ma trận (và nhớ: phép nhân vectơ / ma trận chỉ là phép nhân ma trận) được lưu trữ theo cùng một thứ tự chính.

Vì vậy, là một cột vector-chính hay hàng-chính? Nó là cả và không, như đã nêu trước đây. Nó chỉ là cột chính khi nó được sử dụng như một ma trận cột và nó là hàng chính khi nó được sử dụng như một ma trận hàng.

Do đó, nếu bạn có một ma trận A là cột chính, x*Acó nghĩa là ... không có gì. Vâng, một lần nữa, nó có nghĩa , nhưng đó không phải là những gì bạn thực sự muốn. Tương tự, phép nhân chuyển đổi nếu là hàng chính.x*ATA*xA

Do đó, thứ tự nhân vectơ / ma trận sẽ thay đổi, tùy thuộc vào thứ tự chính của dữ liệu của bạn (và liệu bạn có đang sử dụng ma trận chuyển vị không).

Tại sao trong đoạn mã sau đây không r! = R2

Bởi vì mã của bạn bị hỏng và lỗi. Về mặt toán học , . Nếu bạn không nhận được kết quả này, thì kiểm tra đẳng thức của bạn là sai (các vấn đề chính xác của dấu phẩy động) hoặc mã nhân ma trận của bạn bị hỏng.A * (B * C) == (CT * BT) * AT

tại sao pos3! = pos cho

Bởi vì điều đó không có ý nghĩa. Cách duy nhất để trở thành sự thật sẽ là nếu . Và điều đó chỉ đúng với ma trận đối xứng.A * t == AT * tA == AT


@Nicol, Mọi thứ đang bắt đầu nhấp vào bây giờ. Có sự nhầm lẫn do sự mất kết nối giữa những gì tôi đang thấy và những gì tôi nghĩ tôi nên có, vì thư viện của tôi (lấy từ Axiom) tuyên bố chính cột (và tất cả các lệnh nhân v.v ... phù hợp với điều này) nhưng bố cục bộ nhớ là hàng -major (đánh giá bởi các chỉ số dịch thuật và thực tế HLSL hoạt động chính xác bằng cách sử dụng ma trận không chuyển đổi); Tôi thấy bây giờ tuy nhiên làm thế nào điều này không xung đột. Cảm ơn rât nhiều!
sebf

2
Tôi gần như đã cho bạn -1 khi nói những điều như "Đó không phải là ma trận dịch thông thường" và "đó là rác hoàn toàn". Sau đó, bạn tiếp tục và giải thích độc đáo tại sao chúng hoàn toàn tương đương và do đó không phải là "tự nhiên" hơn thì khác. Tại sao bạn không loại bỏ điều vô nghĩa đó ngay từ đầu? Phần còn lại của câu trả lời của bạn là trong thực tế khá tốt. (Ngoài ra, đối với những người quan tâm: steve.hollasch.net/cgindex/math/matrix/column-vec.html )
imre

2
@imre: Bởi vì nó không vô nghĩa. Các quy ước rất quan trọng vì nó khó hiểu khi có hai quy ước. Các nhà toán học giải quyết các quy ước cho ma trận từ lâu . "Ma trận chuyển vị" (được đặt tên bởi vì chúng được hoán vị từ tiêu chuẩn) là vi phạm quy ước đó. Vì chúng tương đương nhau, chúng không mang lại lợi ích thực sự cho người dùng. Và vì chúng khác nhau và có thể bị lạm dụng, nó tạo ra sự nhầm lẫn. Hoặc nói cách khác, nếu ma trận chuyển tiếp không tồn tại, OP sẽ không bao giờ hỏi điều này. Và do đó, quy ước thay thế này tạo ra sự nhầm lẫn.
Nicol Bolas

1
@Nicol: Một ma trận có bản dịch trong 12-13-14 vẫn có thể là hàng chính - nếu sau đó chúng ta sử dụng vectơ hàng với nó (và nhân với vM). Xem DirectX. HOẶC nó có thể được xem như là cột chính, được sử dụng với các vectơ cột (Mv, OpenGL). Nó thực sự giống nhau. Ngược lại, nếu một ma trận có bản dịch trong 3-7-11, thì nó có thể được xem như là một ma trận hàng chính với các vectơ cột, HOẶC cột chính với các vectơ hàng. Phiên bản 12-13-14 thực sự phổ biến hơn, nhưng theo tôi 1) nó không thực sự là một tiêu chuẩn và 2) gọi nó là chuyên ngành cột có thể gây hiểu lầm, vì nó không nhất thiết phải như vậy.
imre

1
@imre: Đó là tiêu chuẩn. Hỏi bất kỳ nhà toán học được đào tạo thực tế nơi bản dịch đi, và họ sẽ cho bạn biết rằng nó đi trong cột thứ tư. Các nhà toán học đã phát minh ra ma trận; họ là những người đặt ra các quy ước.
Nicol Bolas

3

Có hai sự lựa chọn khác nhau về quy ước tại nơi làm việc. Một là liệu bạn có sử dụng vectơ hàng hay vectơ cột hay không, và ma trận cho các quy ước này là hoán vị của nhau.

Khác là cho dù bạn lưu trữ ma trận trong bộ nhớ theo thứ tự hàng lớn hay thứ tự chính cột. Lưu ý rằng "hàng chính" và "chuyên ngành cột" không phải là thuật ngữ chính xác để thảo luận về quy ước hàng-vectơ / cột-vectơ ... mặc dù nhiều người sử dụng sai chúng như vậy. Bố cục bộ nhớ chính hàng và cột chính cũng khác nhau bởi một chuyển vị, quá.

OpenGL sử dụng quy ước vectơ cột và thứ tự lưu trữ chính của cột và D3D sử dụng quy ước vectơ hàng và thứ tự lưu trữ chính hàng (tốt - ít nhất là D3DX, thư viện toán học), do đó, hai chuyển đổi hủy bỏ và nó biến mất cùng bố trí bộ nhớ hoạt động cho cả OpenGL và D3D. Nghĩa là, cùng một danh sách 16 số float được lưu tuần tự trong bộ nhớ sẽ hoạt động theo cùng một cách trong cả hai API.

Đây có thể là những gì có nghĩa là mọi người nói rằng "nó không khác biệt gì so với cách ma trận được lưu trữ hoặc chuyển sang GPU".

Đối với đoạn mã của bạn, r! = R2 vì quy tắc chuyển đổi sản phẩm là (ABC) ^ T = C ^ TB ^ TA ^ T. Chuyển vị phân phối trên phép nhân với sự hoàn nguyên của trật tự. Vì vậy, trong trường hợp của bạn, bạn sẽ nhận được r.Transpose () == r2, không phải r == r2.

Tương tự, pos! = Pos3 vì bạn đã chuyển đổi nhưng không đảo ngược thứ tự nhân. Bạn sẽ nhận được wpvM * localPos == localPos * wvpM.Tranpose (). Vectơ tự động được hiểu là một vectơ hàng khi được nhân ở bên trái của ma trận và như một vectơ cột khi được nhân ở bên phải của ma trận. Ngoài ra, không có thay đổi trong cách nhân lên.

Cuối cùng, re: "ma trận WVP chính của cột của tôi được sử dụng thành công để biến đổi các đỉnh với lệnh gọi HLSL: mul (vectơ, ma trận)," Tôi không chắc về điều này, nhưng có thể nhầm lẫn / một lỗi đã khiến ma trận xuất hiện từ thư viện toán học đã được chuyển đổi.


1

Trong đồ họa 3d, bạn sử dụng ma trận để biến đổi cả vectơ và điểm. Xem xét thực tế rằng bạn đang nói về ma trận dịch thuật, tôi sẽ chỉ nói về các điểm (bạn không thể dịch một vectơ với ma trận, hoặc nói tốt hơn, bạn có thể nhưng bạn sẽ có được cùng một vectơ).

Trong phép nhân ma trận , số lượng cột của ma trận thứ nhất phải bằng số lượng hàng của ma trận thứ hai (bạn có thể nhân ma trận lo lắng cho một mxk).

Một điểm (hoặc một vectơ) được biểu thị bằng 3 thành phần (x, y, z) và có thể được coi là cả hai như một hàng hoặc một cột:

colum (chiều 3 X 1):

| x |

| y |

| z |

hoặc là

hàng (kích thước 1 X 3):

| x, y, z |

Bạn có thể chọn quy ước ưa thích, nó chỉ là một quy ước. Hãy gọi nó là T ma trận dịch. Nếu bạn chọn quy ước đầu tiên, để nhân một điểm p cho ma trận, bạn cần sử dụng phép nhân bài:

T * v (kích thước 3x3 * 3x1)

nếu không thì:

v * T (kích thước 1x3 * 3x3)

các tác giả dường như khẳng định rằng nó không khác biệt gì so với cách lưu trữ ma trận hoặc chuyển sang GPU

Nếu bạn sử dụng luôn cùng một quy ước thì sẽ không có gì khác biệt. Điều đó không có nghĩa là ma trận của các quy ước khác nhau sẽ có cùng biểu diễn bộ nhớ, nhưng khi chuyển đổi một điểm với 2 quy ước khác nhau, bạn sẽ có được điểm chuyển đổi giống nhau:

p2 = B * A * p1; // quy ước đầu tiên

p3 = p1 * A * B; // quy ước thứ hai

p2 == p3;


1

Tôi thấy các thành phần dịch thuật chiếm các yếu tố thứ 4, 8 và 12 có nghĩa là ma trận của bạn "sai".

Các thành phần dịch thuật luôn được chỉ định là các mục # 13, # 14 và # 15 của ma trận biến đổi ( tính phần tử đầu tiên của mảng là phần tử # 1 ).

Một ma trận biến đổi chính hàng trông như thế này:

[ 2 2 2 1 ]   R00 R01 R02 0  
              R10 R11 R12 0 
              R20 R21 R22 0 
              t.x t.y t.z 1 

Một ma trận biến đổi chính của cột trông như thế này:

 R00 R01 R02 t.x   2  
 R10 R11 R12 t.y   2 
 R20 R21 R22 t.z   2 
  0   0   0   1    1 

Ma trận hàng lớn được chỉ định đi xuống các hàng .

Khai báo ma trận chính hàng ở trên dưới dạng một mảng tuyến tính, tôi sẽ viết:

ROW_MAJOR = { R00, R01, R02, 0,  // row 1 // very intuitive
              R10, R11, R12, 0,  // row 2
              R20, R21, R22, 0,  // row 3
              t.x, t.y, t.z, 1 } ; // row 4

Điều đó có vẻ rất tự nhiên. Bởi vì thông báo, tiếng Anh được viết là "hàng chính" - ma trận xuất hiện trong văn bản ở trên chính xác như trong toán học.

Và đây là điểm nhầm lẫn.

Ma trận chính của cột được chỉ định đi xuống các cột

Điều đó có nghĩa là chỉ định ma trận biến đổi chính của cột là một mảng tuyến tính trong mã, bạn phải viết:

    COLUMN_MAJOR = {R00, R10, R20, 0, // COLUMN # 1 // rất phản trực quan
                     R01, R11, R21, 0,
                     R02, R12, R22, 0,
                     tx, ty, tz, 1};

Lưu ý điều này là hoàn toàn phản trực quan !! Một ma trận chính của cột có các mục được chỉ định xuống các cột khi khởi tạo một mảng tuyến tính, vì vậy dòng đầu tiên

COLUMN_MAJOR = { R00, R10, R20, 0,

Chỉ định cột đầu tiên của ma trận:

 R00
 R10
 R20
  0 

và không phải là hàng đầu tiên , vì bố cục đơn giản của văn bản sẽ khiến bạn tin tưởng. Bạn phải chuyển đổi một ma trận chính của cột khi bạn nhìn thấy nó trong mã, bởi vì 4 phần tử đầu tiên được chỉ định thực sự mô tả cột đầu tiên. Tôi cho rằng đây là lý do tại sao nhiều người thích ma trận hàng lớn trong mã (GO DIRECT3D !! ho.)

Vì vậy, các thành phần dịch luôn nằm ở các chỉ số mảng tuyến tính # 13, # 14 và # 15 (trong đó phần tử đầu tiên là # 1), bất kể bạn đang sử dụng ma trận chính hàng hay cột chính.

Điều gì đã xảy ra với mã của bạn và tại sao nó hoạt động?

Điều đang xảy ra trong mã của bạn là, bạn có một ma trận chính cột có, nhưng bạn đặt các thành phần dịch sai ở vị trí sai. Khi bạn hoán đổi ma trận, mục số 4 chuyển đến mục số 13, mục số 8 đến số 13 và mục số 12 đến số 15. Và bạn có nó rồi đấy!


0

Nói một cách đơn giản, lý do cho sự khác biệt là phép nhân ma trận không giao hoán . Với phép nhân số thường xuyên, nếu A * B = C thì nó cũng theo B * A cũng = C. Đây không phải là trường hợp với ma trận. Đó là lý do tại sao chọn một trong hai vấn đề chính hàng hoặc chính.

Tại sao điều đó không quan trọng là, trong một API hiện đại (và tôi đặc biệt nói về các shader ở đây), bạn có thể chọn quy ước của riêng mình và nhân các ma trận của bạn theo đúng thứ tự cho quy ước đó trong mã shader của riêng bạn. API không còn thực thi cái này hay cái kia đối với bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.