Dapatkah seseorang menjelaskan (alasan untuk) implikasi dari colum vs baris utama dalam perkalian / penggabungan?


11

Saya mencoba belajar bagaimana membangun pandangan dan proyeksi matriks, dan terus mencapai kesulitan dalam implementasi saya karena kebingungan saya tentang dua standar untuk matriks.
Saya tahu cara melipatgandakan matriks, dan saya bisa melihat bahwa transposing sebelum perkalian akan benar-benar mengubah hasilnya, maka kebutuhan untuk melipatgandakan dalam urutan yang berbeda.

Apa yang saya tidak mengerti adalah apa yang dimaksud dengan hanya 'notasi convention' - dari artikel di sini dan di sini penulis tampaknya menegaskan bahwa tidak ada bedanya dengan bagaimana matriks disimpan, atau ditransfer ke GPU, tetapi pada yang kedua halaman yang matriksnya jelas tidak setara dengan bagaimana itu akan diletakkan di memori untuk baris-utama; dan jika saya melihat matriks yang terisi dalam program saya, saya melihat komponen terjemahan menempati elemen ke-4, ke-8 dan ke-12.

Mengingat bahwa:

"Pasca-mengalikan dengan matriks kolom-utama menghasilkan hasil yang sama seperti pra-mengalikan dengan matriks baris-utama."

Mengapa dalam cuplikan kode berikut:

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

Apakah r! = R2 dan mengapa pos3! = Pos untuk :

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

Apakah proses multiplikasi berubah tergantung pada apakah matriksnya adalah baris atau kolom utama , atau hanya urutannya (untuk efek yang setara?)

Satu hal yang tidak membantu ini menjadi lebih jelas, adalah bahwa ketika disediakan untuk DirectX, kolom utama saya matriks WVP digunakan dengan sukses untuk mengubah simpul dengan panggilan HLSL: mul (vektor, matriks) yang akan mengakibatkan vektor diperlakukan sebagai baris-utama , jadi bagaimana bisa matriks utama kolom yang disediakan oleh perpustakaan matematika saya berfungsi?



Jawaban:


11

jika saya melihat matriks yang terisi dalam program saya, saya melihat komponen terjemahan menempati elemen ke-4, ke-8 dan ke-12.

Sebelum saya mulai, penting untuk dipahami: ini berarti matriks Anda adalah baris utama . Karena itu, Anda menjawab pertanyaan ini:

matriks WVP utama kolom saya berhasil digunakan untuk mengubah simpul dengan panggilan HLSL: mul (vektor, matriks) yang seharusnya menghasilkan vektor yang diperlakukan sebagai baris-utama, jadi bagaimana matriks utama kolom yang disediakan oleh pustaka matematika saya berfungsi?

cukup sederhana: matriks Anda adalah baris-utama.

Begitu banyak orang menggunakan matriks baris-besar atau transposed, sehingga mereka lupa bahwa matriks tidak berorientasi seperti itu secara alami. Jadi mereka melihat matriks terjemahan sebagai berikut:

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

Ini adalah matriks terjemahan yang dialihkan . Bukan itu yang tampak seperti matriks terjemahan normal. Terjemahan berada di kolom 4 , bukan baris keempat. Kadang-kadang, Anda bahkan melihat ini di buku teks, yang merupakan sampah.

Sangat mudah untuk mengetahui apakah sebuah matriks dalam array adalah baris atau kolom-utama. Jika baris-utama, maka terjemahan disimpan dalam indeks 3, 7, dan 11. Jika itu kolom-utama, maka terjemahan disimpan dalam indeks 12, 13, dan 14. Indeks nol-basis tentu saja.

Kebingungan Anda berasal dari keyakinan bahwa Anda menggunakan matriks kolom-utama ketika Anda sebenarnya menggunakan yang utama-baris.

Pernyataan bahwa baris vs kolom utama adalah konvensi notasi saja sepenuhnya benar. Mekanika perkalian matriks dan perkalian matriks / vektor adalah sama tanpa memperhatikan konvensi.

Apa yang berubah adalah arti dari hasil.

Matriks 4x4 setelah semua hanyalah kotak angka 4x4. Itu tidak harus merujuk pada perubahan sistem koordinat. Namun, begitu Anda menetapkan makna ke matriks tertentu, Anda sekarang perlu tahu apa yang disimpan di dalamnya dan bagaimana menggunakannya.

Ambil matriks terjemahan yang saya tunjukkan di atas. Itu matriks yang valid. Anda bisa menyimpan matriks itu di float[16]dalam salah satu dari dua cara:

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

Namun, saya mengatakan bahwa matriks terjemahan ini salah, karena terjemahannya ada di tempat yang salah. Saya secara khusus mengatakan bahwa itu dialihkan relatif terhadap konvensi standar untuk bagaimana membangun matriks terjemahan, yang seharusnya terlihat seperti ini:

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

Mari kita lihat bagaimana ini disimpan:

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

Perhatikan bahwa column_majoradalah persis sama seperti row_major_t. Jadi, jika kita mengambil matriks terjemahan yang tepat , dan menyimpannya sebagai kolom-utama, itu sama dengan mentransposasikan matriks itu dan menyimpannya sebagai baris-utama.

Itulah yang dimaksud dengan menjadi hanya konvensi notasi. Sebenarnya ada dua set konvensi: penyimpanan memori dan transposisi. Penyimpanan memori adalah kolom vs baris utama, sedangkan transposisi normal vs ditransformasikan.

Jika Anda memiliki matriks yang dihasilkan dalam urutan baris-utama, Anda bisa mendapatkan efek yang sama dengan mentransposisi setara kolom-utama dari matriks itu. Dan sebaliknya.

Penggandaan matriks hanya dapat dilakukan dengan satu cara: diberikan dua matriks, dalam urutan tertentu, Anda mengalikan nilai-nilai tertentu bersama-sama dan menyimpan hasilnya. Sekarang,, A*B != B*Atetapi kode sumber sebenarnya A*Bsama dengan kode untuk B*A. Keduanya menjalankan kode yang sama untuk menghitung output.

Kode perkalian matriks tidak peduli apakah matriks kebetulan disimpan dalam urutan kolom-utama atau baris-utama.

Hal yang sama tidak dapat dikatakan untuk perkalian vektor / matriks. Dan inilah alasannya.

Multiplikasi vektor / matriks adalah kepalsuan; itu tidak bisa dilakukan. Namun, Anda bisa mengalikan matriks dengan matriks lain. Jadi jika Anda berpura-pura vektor adalah matriks, maka Anda dapat secara efektif melakukan perkalian vektor / matriks, cukup dengan melakukan perkalian matriks / matriks.

Vektor 4D dapat dianggap sebagai vektor kolom atau vektor baris. Yaitu, vektor 4D dapat dianggap sebagai matriks 4x1 (ingat: dalam notasi matriks, jumlah baris lebih dulu) atau matriks 1x4.

Tapi ada satu hal: Diberikan dua matriks A dan B, A*Bhanya didefinisikan jika jumlah kolom A sama dengan jumlah baris B. Oleh karena itu, jika A adalah matriks 4x4 kami, B harus berupa matriks dengan 4 baris di dalamnya. Oleh karena itu, Anda tidak dapat melakukan A*x, di mana x adalah vektor baris . Demikian pula, Anda tidak dapat melakukan di x*Amana x adalah vektor-kolom.

Karena itu, sebagian besar perpustakaan matematika matriks membuat asumsi ini: jika Anda mengalikan vektor kali dengan matriks, Anda benar-benar bermaksud melakukan perkalian yang benar-benar berfungsi , bukan yang tidak masuk akal.

Mari kita tentukan, untuk setiap vektor 4D x, berikut ini. Cakan menjadi bentuk matriks kolom-vektor x, dan Rharus menjadi bentuk matriks baris-vektor dari x. Mengingat ini, untuk setiap matriks 4x4 A, A*Cmerepresentasikan matriks mengalikan A dengan vektor-kolom x. Dan R*Amerepresentasikan matriks yang mengalikan vektor baris xdengan A.

Tetapi jika kita melihat ini menggunakan matematika matriks ketat, kita melihat bahwa ini tidak setara . R*A tidak boleh sama dengan A*C. Ini karena vektor baris tidak sama dengan vektor kolom. Mereka bukan matriks yang sama, jadi mereka tidak menghasilkan hasil yang sama.

Namun, mereka terkait dalam satu cara. Memang benar itu R != C. Namun, juga benar bahwa , di mana T adalah operasi transpos. Dua matriks adalah transpos satu sama lain.R = CT

Ini fakta yang lucu. Karena vektor diperlakukan sebagai matriks, mereka juga memiliki kolom vs pertanyaan penyimpanan baris-utama. Masalahnya adalah mereka berdua terlihat sama . Array float adalah sama, jadi Anda tidak bisa membedakan antara R dan C hanya dengan melihat data. Satu- satunya cara untuk mengetahui perbedaannya adalah dengan bagaimana mereka digunakan.

Jika Anda memiliki dua matriks A dan B, dan A disimpan sebagai baris-mayor dan B sebagai kolom-mayor, mengalikannya sama sekali tidak berarti . Anda mendapatkan omong kosong sebagai hasilnya. Yah, tidak juga. Secara matematis, apa yang Anda dapatkan setara dengan melakukan . Atau ; mereka identik secara matematis.AT*BA*BT

Oleh karena itu, perkalian matriks hanya masuk akal jika dua matriks (dan ingat: perkalian vektor / matriks hanyalah perkalian matriks) disimpan dalam urutan utama yang sama.

Jadi, apakah kolom vektor-utama atau baris-utama? Keduanya dan tidak sama seperti yang dinyatakan sebelumnya. Ini adalah kolom utama hanya ketika digunakan sebagai matriks kolom, dan itu adalah baris utama ketika digunakan sebagai matriks baris.

Oleh karena itu, jika Anda memiliki matriks A yang merupakan kolom utama, x*Aberarti ... tidak ada. Sekali lagi, artinya , tapi bukan itu yang benar-benar Anda inginkan. Demikian pula, apakah transkripsi multiplikasi jika baris-utama.x*ATA*xA

Oleh karena itu, urutan perkalian vektor / matriks tidak berubah, tergantung pada urutan utama data Anda (dan apakah Anda menggunakan matriks transposisi).

Mengapa dalam cuplikan kode berikut tidak r! = R2

Karena kode Anda rusak dan bermasalah. Secara matematis ,. Jika Anda tidak mendapatkan hasil ini, maka tes kesetaraan Anda salah (masalah presisi floating-point) atau kode multiplikasi matriks Anda rusak.A * (B * C) == (CT * BT) * AT

mengapa pos3! = pos untuk

Karena itu tidak masuk akal. Satu-satunya cara untuk menjadi kenyataan adalah jika . Dan itu hanya berlaku untuk matriks simetris.A * t == AT * tA == AT


@Nicol, Semuanya mulai klik sekarang. Ada kebingungan karena terputusnya hubungan antara apa yang saya lihat dan apa yang saya pikir seharusnya, karena perpustakaan saya (diambil dari Axiom) menyatakan kolom-mayor (dan semua perintah perkalian dll sesuai dengan ini) namun tata letak memori adalah baris -major (dilihat dari indeks terjemahan dan fakta HLSL bekerja dengan benar menggunakan matriks non-transposed); Namun saya melihat sekarang bagaimana ini tidak dalam konflik. Terima kasih banyak!
sebf

2
Saya hampir memberi Anda -1 untuk mengatakan hal-hal seperti "Bukan seperti itu terjemahan matriks normal seperti" dan "yang merupakan sampah total". Kemudian Anda melanjutkan dan menjelaskan dengan baik mengapa keduanya benar-benar setara dan oleh karena itu tidak ada yang lebih "alami" dari yang lain. Mengapa Anda tidak menghapus omong kosong kecil itu saja dari awal? Sisa jawaban Anda sebenarnya cukup baik. (Juga, untuk yang berminat: steve.hollasch.net/cgindex/math/matrix/column-vec.html )
imre

2
@ Imre: Karena itu bukan omong kosong. Konvensi penting karena membingungkan memiliki dua konvensi. Matematikawan menetap di konvensi untuk matriks sejak lama. "Transposed matrices" (dinamai karena dialihkan dari standar) adalah pelanggaran terhadap konvensi itu. Karena mereka setara, mereka tidak memberikan manfaat aktual kepada pengguna. Dan karena mereka berbeda dan dapat disalahgunakan, itu menciptakan kebingungan. Atau dengan kata lain, jika matriks yang dialihkan tidak ada, OP tidak akan pernah menanyakan hal ini. Dan karena itu, konvensi alternatif ini menciptakan kebingungan.
Nicol Bolas

1
@Nicol: Matriks yang memiliki terjemahan dalam 12-13-14 masih bisa berupa baris-mayor - jika kita menggunakan vektor baris dengannya (dan dikalikan dengan vM). Lihat DirectX. ATAU itu dapat dilihat sebagai kolom-utama, digunakan dengan vektor kolom (Mv, OpenGL). Itu benar-benar sama. Sebaliknya, jika sebuah matriks memiliki terjemahan pada 3-7-11, maka dapat dilihat sebagai matriks baris-utama dengan vektor kolom, ATAU kolom-utama dengan vektor baris. Versi 12-13-14 memang lebih umum, tetapi menurut saya 1) itu sebenarnya bukan standar, dan 2) menyebutnya kolom-mayor bisa menyesatkan, karena tidak harus begitu.
imre

1
@ imre: Ini standar. Tanyakan kepada ahli matematika terlatih mana pun terjemahannya, dan mereka akan memberi tahu Anda bahwa itu ada di kolom keempat. Matematikawan menemukan matriks; merekalah yang menetapkan konvensi.
Nicol Bolas

3

Ada dua pilihan konvensi yang bekerja di sini. Salah satunya adalah apakah Anda menggunakan vektor baris atau vektor kolom, dan matriks untuk konvensi ini adalah transpos satu sama lain.

Yang lainnya adalah apakah Anda menyimpan matriks dalam memori dalam urutan baris-utama atau urutan kolom-utama. Perhatikan bahwa "baris-utama" dan "kolom-utama" bukan istilah yang tepat untuk mendiskusikan konvensi baris-vektor / kolom-vektor ... meskipun banyak orang menyalahgunakannya. Layout memori baris-utama dan kolom-utama berbeda dengan transpos juga.

OpenGL menggunakan konvensi vektor kolom dan urutan penyimpanan kolom-utama, dan D3D menggunakan konvensi vektor baris dan pesanan penyimpanan baris-utama (well - setidaknya D3DX, perpustakaan matematika, tidak), sehingga dua transpos dibatalkan dan ternyata tata letak memori yang sama berfungsi untuk OpenGL dan D3D. Artinya, daftar 16 float yang disimpan secara berurutan dalam memori akan bekerja dengan cara yang sama di kedua API.

Ini mungkin yang dimaksud oleh orang yang mengatakan bahwa "tidak ada bedanya dengan bagaimana matriks disimpan, atau ditransfer ke GPU".

Sedangkan untuk cuplikan kode Anda, r! = R2 karena aturan untuk transpos suatu produk adalah (ABC) ^ T = C ^ TB ^ TA ^ T. Transposisi mendistribusikan lebih dari multiplikasi dengan penghormatan atas pesanan. Jadi dalam kasus Anda, Anda harus mendapatkan r.Transpose () == r2, bukan r == r2.

Demikian juga, pos! = Pos3 karena Anda mengubah posisi tetapi tidak membalik urutan perkalian. Anda harus mendapatkan wpvM * localPos == localPos * wvpM.Tranpose (). Vektor secara otomatis ditafsirkan sebagai vektor baris ketika dikalikan di sebelah kiri matriks, dan sebagai vektor kolom ketika dikalikan di sisi kanan matriks. Selain itu, tidak ada perubahan dalam bagaimana perkalian dilakukan.

Akhirnya, ulang: "matriks WVP utama kolom saya berhasil digunakan untuk mengubah simpul dengan panggilan HLSL: mul (vektor, matriks)," Saya tidak yakin tentang hal ini, tapi mungkin kebingungan / bug telah menyebabkan matriks keluar dari perpustakaan matematika sudah dipindahkan.


1

Dalam grafik 3d Anda menggunakan matriks untuk mengubah vektor dan titik. Mempertimbangkan fakta bahwa Anda berbicara tentang matriks terjemahan, saya hanya akan berbicara tentang poin (Anda tidak dapat menerjemahkan vektor dengan matriks, atau, mengatakan lebih baik, Anda bisa tetapi Anda akan mendapatkan vektor yang sama).

Dalam perkalian matriks jumlah kolom dari matriks pertama harus sama dengan jumlah baris yang kedua (Anda dapat mengalikan matriks anxm untuk mxk).

Suatu titik (atau vektor) diwakili oleh 3 komponen (x, y, z) dan dapat dianggap sebagai baris atau kolom:

colum (dimensi 3 X 1):

| x |

| y |

| z |

atau

baris (dimensi 1 X 3):

| x, y, z |

Anda dapat memilih konvensi yang disukai, itu hanya konvensi. Sebut saja T matriks terjemahan. Jika Anda memilih konvensi pertama, untuk mengalikan titik p untuk matriks Anda perlu menggunakan perkalian posting:

T * v (dimensi 3x3 * 3x1)

jika tidak:

v * T (dimensi 1x3 * 3x3)

penulis tampaknya menegaskan bahwa tidak ada bedanya dengan bagaimana matriks disimpan, atau ditransfer ke GPU

Jika Anda menggunakan konvensi yang sama selalu tidak ada bedanya. Itu tidak berarti bahwa matriks dari konvensi yang berbeda akan memiliki representasi memori yang sama, tetapi bahwa mengubah suatu titik dengan 2 konvensi yang berbeda Anda akan memperoleh titik transformasi yang sama:

p2 = B * A * p1; // konvensi pertama

p3 = p1 * A * B; // konvensi kedua

p2 == p3;


1

Saya melihat komponen terjemahan menempati elemen ke-4, ke-8 dan ke-12 yang berarti matriks Anda "salah".

Komponen terjemahan selalu ditentukan sebagai entri # 13, # 14 dan # 15 dari matriks transformasi ( menghitung elemen pertama dari array sebagai elemen # 1 ).

Matriks transformasi utama baris terlihat seperti ini:

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

Matriks transformasi utama kolom terlihat seperti ini:

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

Matriks utama baris ditentukan turun baris .

Mendeklarasikan matriks utama baris di atas sebagai array linier, saya akan menulis:

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

Itu tampak sangat alami. Karena pemberitahuan, bahasa Inggris ditulis "baris-utama" - matriks muncul dalam teks di atas persis seperti yang akan ada dalam matematika.

Dan inilah titik kebingungannya.

Matriks utama kolom ditentukan turun kolom

Itu berarti untuk menentukan matriks transformasi kolom utama sebagai array linier dalam kode, Anda harus menulis:

    COLUMN_MAJOR = {R00, R10, R20, 0, // COLUMN # 1 // sangat kontra-intuitif
                     R01, R11, R21, 0,
                     R02, R12, R22, 0,
                     tx, ty, tz, 1};

Perhatikan ini sepenuhnya kontra-intuitif !! Matriks utama kolom memiliki entri yang ditentukan di kolom saat menginisialisasi array linier, jadi baris pertama

COLUMN_MAJOR = { R00, R10, R20, 0,

Menentukan kolom pertama dari matriks:

 R00
 R10
 R20
  0 

dan bukan baris pertama , karena tata letak teks yang sederhana akan membuat Anda percaya. Anda harus secara mental mengubah matriks utama kolom ketika Anda melihatnya dalam kode, karena 4 elemen pertama yang ditentukan sebenarnya menggambarkan kolom pertama. Saya kira ini sebabnya banyak orang lebih suka matriks baris-utama dalam kode (GO DIRECT3D !! cough.)

Jadi, komponen terjemahan selalu pada indeks array linier # 13, # 14, dan # 15 (di mana elemen pertama adalah # 1), terlepas dari apakah Anda menggunakan baris utama atau matriks utama kolom.

Apa yang terjadi dengan kode Anda dan mengapa itu bekerja?

Apa yang terjadi dalam kode Anda adalah, Anda memiliki kolom-matriks utama ya, tetapi Anda menempatkan komponen terjemahan di tempat yang salah. Ketika Anda mengubah urutan matriks, entri # 4 pergi ke entri # 13, entri # 8 ke # 13, dan entri # 12 ke # 15. Dan begitulah.


0

Sederhananya, alasan perbedaannya adalah bahwa perkalian matriks tidak komutatif . Dengan penggandaan angka yang teratur, jika A * B = C maka itu mengikuti bahwa B * A juga = C. Ini bukan kasus dengan matriks. Itu sebabnya memilih hal-hal utama baris atau kolom-utama.

Mengapa tidak masalah adalah bahwa, dalam API modern (dan saya secara khusus berbicara shader di sini), Anda dapat memilih konvensi Anda sendiri dan melipatgandakan matriks Anda dalam urutan yang benar untuk konvensi itu dalam kode shader Anda sendiri. API tidak lagi memberlakukan satu atau yang lain pada Anda.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.