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?
sumber
Jawaban:
Sebelum saya mulai, penting untuk dipahami: ini berarti matriks Anda adalah baris utama . Karena itu, Anda menjawab pertanyaan ini:
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:
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: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:
Mari kita lihat bagaimana ini disimpan:
Perhatikan bahwa
column_major
adalah persis sama sepertirow_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*A
tetapi kode sumber sebenarnyaA*B
sama dengan kode untukB*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*B
hanya 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 melakukanA*x
, di mana x adalah vektor baris . Demikian pula, Anda tidak dapat melakukan dix*A
mana 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.
C
akan menjadi bentuk matriks kolom-vektorx
, danR
harus menjadi bentuk matriks baris-vektor darix
. Mengingat ini, untuk setiap matriks 4x4 A,A*C
merepresentasikan matriks mengalikan A dengan vektor-kolomx
. DanR*A
merepresentasikan matriks yang mengalikan vektor barisx
dengan A.Tetapi jika kita melihat ini menggunakan matematika matriks ketat, kita melihat bahwa ini tidak setara .
R*A
tidak boleh sama denganA*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*B
A*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*A
berarti ... tidak ada. Sekali lagi, artinya , tapi bukan itu yang benar-benar Anda inginkan. Demikian pula, apakah transkripsi multiplikasi jika baris-utama.x*AT
A*x
A
Oleh karena itu, urutan perkalian vektor / matriks tidak berubah, tergantung pada urutan utama data Anda (dan apakah Anda menggunakan matriks transposisi).
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
Karena itu tidak masuk akal. Satu-satunya cara untuk menjadi kenyataan adalah jika . Dan itu hanya berlaku untuk matriks simetris.
A * t == AT * t
A == AT
sumber
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.
sumber
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:
atau
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:
jika tidak:
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:
sumber
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:
Matriks transformasi utama kolom terlihat seperti ini:
Matriks utama baris ditentukan turun baris .
Mendeklarasikan matriks utama baris di atas sebagai array linier, saya akan menulis:
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:
Perhatikan ini sepenuhnya kontra-intuitif !! Matriks utama kolom memiliki entri yang ditentukan di kolom saat menginisialisasi array linier, jadi baris pertama
Menentukan kolom pertama dari matriks:
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.
sumber
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.
sumber