Geometrie (řádkový a sloupcový vektor)

Geometrie

Dříve v této lekci jsme si vysvětlili, že vektory (neboli body) lze zapsat jako matice (jeden řádek, tři sloupce). Všimněte si však, že jsme je mohli zapsat také jako matice (tři řádky, jeden sloupec). Z technického hlediska jsou tyto dva způsoby vyjádření bodů a vektorů jako matic naprosto platné a volba jednoho nebo druhého způsobu je pouze otázkou konvence.

Vektor zapsaný jako matice: \( V=\begin{bmatrix}x & y & z\end{bmatrix}\)

Vektor zapsaný jako matice: \(V=\begin{bmatrix}x\y\z\end{bmatrix}\)

V prvním příkladu (matice) jsme vyjádřili náš vektor nebo bod v tzv. řádkovém větném uspořádání: vektor (nebo bod) je zapsán jako řádek tří čísel. Ve druhém příkladu říkáme, že tento bod nebo vektor jsme zapsali v pořadí sloupec-major: tři souřadnice vektoru nebo bodu jsme zapsali svisle, jako sloupec.

Pamatujte, že body a vektory vyjadřujeme jako matice, abychom je mohli násobit transformačními maticemi (pro zjednodušení budeme pracovat spíše s maticemi než s maticemi). Také jsme se naučili, že matice můžeme násobit pouze tehdy, když je počet sloupců z levé matice a počet řádků z pravé matice stejný. Jinými slovy matice a lze navzájem násobit, ale matice a nikoli. Všimněte si, že pokud vektor zapíšeme jako matici, můžeme jej vynásobit maticí (za předpokladu, že tato matice je uvnitř násobení vpravo), ale pokud tento vektor zapíšeme jako matici, pak jej maticí vynásobit nemůžeme. To ilustrují následující příklady. Vnitřní rozměry (3 a 3) matic zapojených do násobení jsou stejné (zeleně), takže toto násobení je platné (a výsledkem je transformovaný bod zapsaný ve tvaru matice):

$$* = \begin{bmatrix}x & y & z\end{bmatrix} * \begin{bmatrix}c_{00}&c_{01}&{c_{02}}\\c_{10}&c_{11}&{c_{12}}\\c_{20}&c_{21}&{c_{22}}\\\end{bmatrix} =\begin{bmatrix}x’&y’&z’\end{bmatrix}$$

The Vnitřní rozměry (1 a 3) matic zapojených do násobení nejsou stejné (červeně), takže toto násobení není možné:

$$* \rightarrow \begin{bmatrix}x\\ y\\z\end{bmatrix} * \begin{bmatrix} c_{00}&c_{01}&{c_{02}}\\ c_{10}&c_{11}&{c_{12}}\\ c_{20}&c_{21}&{c_{22}}\\ \end{bmatrix}$$

Tak co uděláme? Řešením tohoto problému není násobení vektoru nebo bodu maticí, ale matice M vektorem V. Jinými slovy, posuneme bod nebo vektor doprava dovnitř násobení:

$$* \rightarrow \begin{bmatrix} c_{00}&c_{01}&{c_{02}}\\ c_{10}&c_{11}&{c_{12}}\\ c_{20}&c_{21}&{c_{22}}\\ \end{bmatrix} * \begin{bmatrix}x\\y\z\end{bmatrix} = \begin{bmatrix}x’\\y’\\z’\end{bmatrix}$$

Všimněte si, že výsledkem této operace je transformovaný bod zapsaný ve tvaru matice. Takže na začátku dostaneme bod a na konci transformovaný bod, což je to, co chceme. Problém je vyřešen. Shrňme si, že když se podle konvence rozhodneme vyjádřit vektory nebo body v řádkově větším pořadí (), musíme bod umístit na levou stranu násobení a na pravou stranu dovnitř znaménka násobení. Tomu se v matematice říká levé nebo předmnožení. Pokud se rozhodneme místo toho zapsat vektory v pořadí sloupec-major (), je třeba matici umístit na levou stranu násobení a vektor nebo bod na pravou stranu. Tomu se říká pravé nebo následné násobení.

Musíme si dát pozor na to, jak se tyto pojmy vlastně používají. Například v dokumentaci k programu Maya se píše, že „matice se v programu Maya násobí dodatečně. Například pro transformaci bodu P z objektového prostoru do světového prostoru (P‘) byste museli dodatečně vynásobit maticí worldMatrix. (P‘ = P x WM)“, což je matoucí, protože ve skutečnosti jde o přednásobení, ale v tomto konkrétním případě se mluví o poloze matice vzhledem k bodu. To je vlastně nesprávné použití terminologie. Mělo být napsáno, že v Mayi jsou body a vektory vyjádřeny jako řádkové vektory a že jsou tedy před-násobeny (což znamená, že bod nebo vektor se v násobení objeví před maticí).

Následující tabulka shrnuje rozdíly mezi oběma konvencemi (kde P, V a M znamenají v tomto pořadí bod, vektor a matici).

Řádek-major

\(P/V=\begin{bmatrix}x & y & z\end{bmatrix}\)

Levý nebo před.násobení

P/V * M

Sloupec-hlavní pořadí

\(P/V=\begin{bmatrix}x \\ y \\ z\end{bmatrix}\)

Pravý nebo post-násobení

M * P/V

Teď, když jsme se seznámili s těmito dvěma konvencemi, se možná ptáte: „Není to jen o psaní věcí na papír?“. Víme, jak vypočítat součin dvou matic A a B: každý koeficient v aktuálním řádku A vynásobíme příslušnými prvky v aktuálním sloupci B a výsledek sečteme. Použijme tento vzorec s použitím obou konvencí a porovnejme výsledky:

Řádek-větší-pořadí

$${ \begin{bmatrix}x & y & z\end{bmatrix} * \begin{bmatrix}a & b & c \\d & e & f \\g & h & i\end{bmatrix} }$$

$${ \begin{array}{l}x‘ = x * a + y * d + z * g\\\y‘ = x * b + y * e + z * h\\z‘ = x * c + y * f + z * i\end{array} }$$

Sloupcový hlavní řád

$${ \begin{bmatrix} a & b & c \\d & e & f \\g & h & i \end{bmatrix} * \begin{bmatrix}x\\y\\z\end{bmatrix} }$$

$${\begin{array}{l}x‘ = a * x + b * y + c * z\\y‘ = d * x + e * y + f * z\z‘ = g * x + h * y + i * z\end{array} }$$

Násobení bodu nebo vektoru maticí by mělo dát stejný výsledek, ať už použijeme řádkový nebo sloupcový hlavní řád. Pokud použijete 3D aplikaci k otočení bodu o určitý úhel kolem osy z, očekáváte, že bod bude po otočení v určité poloze bez ohledu na to, jakou vnitřní konvenci vývojář použil pro reprezentaci bodů a vektorů. Jak však vidíte z pohledu na výše uvedenou tabulku, vynásobení řádkově velkého a sloupcově velkého bodu (nebo vektoru) stejnou maticí by nám zjevně nedalo stejný výsledek. Abychom se dostali zpět na nohy, museli bychom matici použitou při násobení sloupec-většina skutečně transponovat, abychom se ujistili, že x‘, y‘ a z‘ jsou stejné (pokud si potřebujete připomenout, co je transpozice matice, podívejte se do kapitoly Operace s maticemi). Zde je výsledek:

Řádek-major

$${ \begin{bmatrix}x & y & z\end{bmatrix} * \begin{bmatrix}a & b & c \\d & e & f \\g & h & i\end{bmatrix} }$$

$${\begin{array}{l}x‘ = x * a + y * d + z * g\\\y‘ = x * b + y * e + z * h\\z‘ = x * c + y * f + z * i\end{array} }$$

Sloupcový hlavní řád

$${ \begin{bmatrix} a & d & g \\b & e & h \\c & f & i\end{bmatrix} * \begin{bmatrix}x\\y\\z\end{bmatrix} }$$

$${\begin{array}{l}x‘ = a * x + d * y + g * z\\y‘ = b * x + e * y + h * z\\z‘ = c * x + f * y + i * z\end{array} }$$

Závěrem lze říci, že při přechodu z řádkového na sloupcový řád je třeba nejen prohodit bod nebo vektor a matici při násobení, ale také transponovat matici, aby bylo zaručeno, že obě konvence dávají stejný výsledek (a naopak).

Z těchto pozorování vyplývá, že jakoukoli řadu transformací aplikovaných na bod nebo vektor při použití řádkově-majoritní konvence lze zapsat v pořadí za sebou (nebo v pořadí čtení). Představte si například, že chcete bod P přeložit pomocí matice T, pak jej otočit kolem osy z pomocí Rz a poté kolem osy y pomocí Ry. Můžete zapsat:

$$P’=P * T * R_z * R_y$$

Pokud byste použili sloupcový zápis, museli byste transformaci volat v opačném pořadí (což by někomu mohlo připadat neintuitivní):

$$P’=R_y * R_z * T * P$$

Můžete si tedy pomyslet, že „musí existovat důvod, proč dát přednost jednomu systému před druhým“. Ve skutečnosti jsou obě konvence správné a dávají nám stejný výsledek, ale z určitých technických důvodů se v textech matematiky a fyziky s vektory obecně zachází jako se sloupcovými vektory.

Pořadí transformace při použití sloupcových matic je v matematice podobnější způsobu, jakým zapisujeme vyhodnocování a skládání funkcí.

Řádková konvence však usnadňuje výuku matic, což je důvod, proč ji používáme pro Scratchapixel (stejně jako Maya, DirectX. Jsou také definovány jako standard ve specifikacích RenderManu). Některá 3D API, například OpenGL, však používají konvenci sloupec-větší.

Implikace v kódování:

Je tu ještě jeden potenciálně velmi důležitý aspekt, který je třeba vzít v úvahu, pokud se potřebujete rozhodnout mezi row-major a column-major, ale ten ve skutečnosti nemá nic společného se samotnými konvencemi a s tím, jak praktická je jedna oproti druhé. Spíše to souvisí s počítačem a způsobem, jakým pracuje. Nezapomeňte, že budeme pracovat s maticemi. Typicky vypadá implementace matice v C++ takto:

třída Matrix44{ … float m;};

Jak vidíte, 16 koeficientů matice je uloženo ve dvourozměrném poli floatů (nebo double v závislosti na požadované přesnosti. Naše třída Matrix v C++ je šablona). Což znamená, že v paměti bude 16 koeficientů rozloženo následujícím způsobem: c00, c01, c02, c03, c10, c11, c12, c13, c20, c21, c22, c23, c30, c31, c32, c33. Jinými slovy, jsou v paměti rozloženy souvisle. Nyní se podívejme, jak se k těmto koeficientům přistupuje při násobení vektorů maticemi, kde jsou vektory zapsány v pořadí řádek-major:

// row-major orderx‘ = x * c00 + y * c10 + z * c20y‘ = x * c01 + y * c11 + z * c21z‘ = x * c02 + y * c12 + z * c22

Jak vidíte, prvky matice pro x‘ nejsou přístupné postupně. Jinými slovy, k výpočtu x‘ potřebujeme 1., 5. a 9. float matice 16 floats array. Pro výpočet y‘ potřebujeme přístup k 2., 6. a 10. floatu této matice. A konečně pro výpočet z‘ potřebujeme 3., 7. a 11. float z tohoto pole. Ve světě výpočtů nemusí být přístup k prvkům pole v jiném než postupném pořadí nutně dobrý. Ve skutečnosti to potenciálně snižuje výkon cache procesoru. Nebudeme zde zabíhat do přílišných podrobností, ale řekněme si jen, že nejbližší paměť, ke které má procesor přístup, se nazývá cache. Přístup do této mezipaměti je velmi rychlý, ale lze do ní uložit jen velmi omezený počet dat. Když procesor potřebuje přistoupit k nějakým datům, nejprve zkontroluje, zda existují v mezipaměti. Pokud ano, procesor k těmto datům přistoupí ihned (cache hit), pokud ne (cache miss), musí pro ně nejprve vytvořit záznam v cache a poté na toto místo zkopírovat data z operační paměti. Tento proces je samozřejmě časově náročnější, než když data již v mezipaměti existují, takže v ideálním případě se chceme co nejvíce vyhnout chybění mezipaměti. Kromě kopírování konkrétních dat z hlavní paměti zkopíruje procesor také část dat, která se nachází hned vedle (například dalších 24 bajtů), protože hardwaroví inženýři usoudili, že pokud váš kód potřebuje přistoupit například k prvku pole, je pravděpodobné, že brzy poté přistoupí i k prvkům následujícím za ním. V programech skutečně často procházíme smyčkou prvky pole v sekvenčním pořadí, a tento předpoklad je tedy pravděpodobně pravdivý. Aplikováno na náš maticový problém, přístup ke koeficientům matice v jiném než sekvenčním pořadí proto může být problém. Za předpokladu, že procesor načte požadovaný float do cache a 3 floaty vedle něj, může naše současná implementace vést k mnoha chybám cache, protože koeficienty použité k výpočtu x‘ y‘ a z‘ jsou v poli vzdáleny 5 floatů. Na druhou stranu, pokud použijeme zápis v pořadí sloupec-major, výpočet x‘ například vyžaduje přístup k 1., 2. a 3. prvku matice.

// column-major orderx‘ = c00 * x + c01 * y + c02 * zy‘ = c10 * x + c11 * y + c12 * zz‘ = c20 * x + c21 * y + c22 * z

Ke koeficientům se přistupuje v sekvenčním pořadí, což také znamená, že dobře využijeme mechanismus mezipaměti procesoru (v našem příkladu pouze 3 chybění mezipaměti místo 9). Závěrem můžeme říci, že z programátorského hlediska může být implementace našeho bodového nebo vektorového násobení matic pomocí konvence colum-major order z hlediska výkonu lepší než verze používající konvenci row-major order. Prakticky se nám však nepodařilo prokázat, že by tomu tak skutečně bylo (při kompilaci programu s použitím optimalizačních příznaků -O, -O2 nebo -O3 může překladač udělat práci za vás tím, že optimalizuje smyčky nad vícerozměrnými poli) a my jsme úspěšně používali verzi s řádkovým větším pořadím bez ztráty výkonu ve srovnání s verzí téhož kódu používající implementaci s pořadím sloupec-větší.

šablona<typename T>třída Vec3{public: Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {} T x, y, z, w;}; template<typename T>class Matrix44{public: T m; Vec3<T> multVecMatrix(const Vec3<T> &v) {#ifdef ROWMAJOR return Vec3<T>( v.x * m + v.y * m + v.z * m, v.x * m + v.y * m + v.z * m, v.x * m + v.y * m + v.z * m);#else return Vec3<T>( v.x * m + v.y * m + v.z * m, v.x * m + v.y * m + v.z * m, v.x * m + v.y * m + v.z * m);#endif }}; #include <cmath>#include <cstdlib>#include <cstdio>#include <ctime> #define MAX_ITER 10e8 int main(int argc, char **argv){ clock_t start = clock(); Vec3<float> v(1, 2, 3); Matrix44<float> M; float *tmp = &M.m; for (int i = 0; i < 16; i++) *(tmp + i) = drand48(); for (int i = 0; i < MAX_ITER; ++i) { Vec3<float> vt = M.multVecMatrix(v); } fprintf(stderr, „Clock time %f\n“, (clock() – start) / float(CLOCKS_PER_SEC)); return 0;}

Pořadí řádek-větší a sloupec-větší ve výpočtech

Pro úplnost ještě uveďme, že termíny pořadí řádek-větší a sloupec-větší lze ve výpočtech použít také pro popis způsobu, jakým jsou prvky vícerozměrných polí rozloženy v paměti. Prvky vícerozměrného pole jsou řazeny za sebou zleva doprava, shora dolů. Tuto metodu používá jazyk C/C++. Například matici:

$$M = \begin{bmatrix}1&2&3\\4&5&6\end{bmatrix}$$

můžeme v jazyce C/C++ zapsat takto:

float m={{1, 2, 3}, {4, 5, 6}};

a prvky tohoto pole by byly v lineární paměti rozloženy souvisle jako:

1 2 3 4 5 6

V pořadí sloupec-větší, které používají jazyky jako FORTRAN a MATLAB, jsou prvky matice uloženy v paměti shora dolů, zleva doprava. Při použití stejného příkladu s maticí by byly prvky matice uloženy (a zpřístupněny) v paměti následujícím způsobem:

1 4 2 5 3 6

Znalost toho, jak jsou prvky matice rozloženy v paměti, je důležitá zejména tehdy, když se k nim snažíte přistupovat pomocí posunu ukazatele a optimalizace smyčky for (již dříve jsme v této kapitole vysvětlili, že to může ovlivnit výkon cache procesoru). Protože však budeme uvažovat pouze o programovacím jazyce C/C++, uspořádání sloupců (aplikované na výpočetní techniku) nás příliš nezajímá. Pouze se zmiňujeme o tom, co tyto pojmy znamenají ve výpočetní technice, abyste si uvědomili, že mohou popisovat dvě různé věci v závislosti na kontextu, ve kterém jsou použity. Měli byste si dát pozor, abyste je nezaměňovali. V kontextu matematiky popisují, zda s vektory (nebo body) zacházíte jako s řádky souřadnic nebo jako se sloupci a druhým, a v kontextu výpočetní techniky popisují způsob, jakým určitý programovací jazyk ukládá a přistupuje k prvkům vícerozměrného pole (což matice jsou) v paměti.

OpenGL je v tomto ohledu zajímavý případ. Když byl GL původně vytvořen, zvolili vývojáři konvenci řádkového vektoru. Vývojáři, kteří OpenGL rozšířili, si však mysleli, že by se měli vrátit ke sloupcově-většímu vektoru, což také udělali. Z důvodů kompatibility však nechtěli měnit kód pro násobení bodové matice a místo toho se rozhodli změnit pořadí, v jakém jsou koeficienty matice uloženy v paměti. Jinými slovy, OpenGL ukládá koeficienty v pořadí sloupec-major, což znamená, že koeficienty translace m03, m13 a m23 z matice používající vektor sloupec-major mají v poli float indexy 13, 14, 15, stejně jako koeficienty translace m30, m31 a m32 z matice používající vektor řádek-major.

Souhrn

Rozdíly mezi oběma konvencemi shrnuje následující tabulka:

.

Řadový-větší vektor (matematika) Sloupcový-.hlavní vektor (Matematika)

\(P/V=\begin{bmatrix}x & y & z\end{bmatrix}\)

\(P/V=\begin{bmatrix}x \\ y \\ z\end{bmatrix}\)

Pre-násobení \(vM\)

Po násobení \(Mv\)

Pořadí volání a pořadí použití transformací je stejné: „Vezmi P, transformuj podle T, transformuj podle Rz, transformuj podle Ry“ se zapíše jako \(P’=P*T*R_z*R_y\)

Pořadí volání je opačné než pořadí, v němž jsou transformace aplikovány: „vezmi P, transformuj podle T, transformuj podle Rz, transformuj podle Ry“ se zapíše jako \(P’=R_y*R_z*T*P\)

API:

API: Direct X, Maya

API:

Řádky matice představují základny (nebo osy) souřadnicového systému (červená: osa x, zelená: osa y, modrá:osa z)

$${\begin{bmatrix}}. \color{red}{c_{00}}& \color{red}{c_{01}}&\color{red}{c_{02}}&0\\ \color{green}{c_{10}}& \color{green}{c_{11}}&\color{green}{c_{12}}&0\\ \color{blue}{c_{20}}& \color{blue}{c_{21}}&\color{blue}{c_{22}}&0\\0&0&0&1 \end{bmatrix} } $$

Sloupce matice představují základny (nebo osy) souřadného systému (červená: osa x, zelená: osa y, modrá:osa z)

$${\begin{bmatrix} \color{red}{c_{00}}& \color{green}{c_{01}}&\color{blue}{c_{02}}&0\\ \color{red}{c_{10}}& \color{green}{c_{11}}&\color{blue}{c_{12}}&0\\ \color{red}{c_{20}}& \color{green}{c_{21}}&\color{blue}{c_{22}}&0\\0&0&0&1\end{bmatrix} }$$

Hodnoty překladu jsou uloženy v prvcích c30, c31 a c32.

$${\begin{bmatrix}1&0&0&0\\0&1&0&0\\0&0&1&0\\Tx&Ty&Tz&1\end{bmatrix} }$$

Hodnoty překladu jsou uloženy v prvcích c03, c13 a c23.

${\begin{bmatrix}1&0&0&0&Tx\0&1&0&Ty\0&0&1&Tz\0&0&0&1\end{bmatrix} }$$

Transponujte matici tak, abyste ji použili jako matici uspořádanou do sloupců

Transponujte matici tak, abyste ji použili jako matici uspořádanou do řádků.hlavní uspořádanou matici

Řádková hlavní matice (Výpočet) Sloupcová hlavní matice (Výpočet)

API: Direct X, Maya, PBRT

API:

Čtenář položil na Stackoverflow dotaz, podle kterého je výše uvedená tabulka matoucí. Téma je matoucí a navzdory naší snaze vnést do něj trochu světla je mnoho lidí stále zmateno. Řekli jsme si, že naše odpověď na Stackoverflow by snad mohla přinést další náhled na tuto otázku.

Máte teorii (to, co děláte v matematice s tužkou a papírem) a to, co děláte s implementací (C++). To jsou dva různé problémy.

Matematika: můžeš použít dva zápisy, buď sloupcový, nebo řádkový hlavní. S řádkovým větným vektorem musíte na papír zapsat násobení vektoru a matice vM, kde v je řádkový vektor (1×4) a M vaše matice 4×4. Proč? Protože matematicky můžete zapsat pouze *, a ne naopak. Podobně pokud použijete sloupec, pak je třeba vektor zapsat svisle dolů nebo v notaci (4 řádky, 1 sloupec). Násobení maticí lze tedy zapsat pouze takto: . Všimněte si, že matice je umístěna před vektorem: Mv. První zápis se nazývá levé nebo přednásobení (protože vektor je na levé straně součinu) a druhý (Mv) se nazývá pravé nebo následné násobení (protože vektor je na pravé straně součinu). Jak vidíte, termíny se odvíjejí od toho, zda je vektor na levé straně (před, neboli „pre“) nebo na pravé straně (za, neboli „post“) matice.

Pokud potřebujete transformovat vektor (nebo bod), pak musíte dávat pozor na pořadí násobení, když je zapisujete na papír. Pokud chcete něco převést pomocí matice T a pak otočit pomocí R a pak škálovat pomocí S, pak ve sloupcovém větném světě musíte zapsat v‘ = S * R * T * v. V řádkovém větném světě musíte zapsat v‘ = v * T * R * S.

To je pro teorii. Říkejme tomu řádková/sloupcová vektorová konvence.

Počítač: Pak přijde okamžik, kdy se to rozhodnete implementovat v C++, řekněme. Dobré na tom je, že C++ vám nic o ničem nevnucuje. Můžete si namapovat hodnoty koeficientů své matice v paměti, jak chcete, a můžete napsat kód pro provedení násobení matice jinou maticí, jak chcete. Stejně tak je zcela na vás, jakým způsobem budete přistupovat ke koeficientům pro násobení vektoru maticí. je třeba jasně rozlišovat mezi tím, jak mapujete své koeficienty v paměti, a tím, jaké konvence musíte z matematického hlediska používat pro reprezentaci svých vektorů. Jedná se o dva nezávislé problémy. Nazvěme tuto část rozložením řádek/sloupec-major.

Například můžete deklarovat třídu matice jako pole řekněme 16 sousedních floatů. To je v pořádku. Kde koeficienty m14, m24, m34 představují translační část matice (Tx, Ty, Tz), takže předpokládáte, že vaše „konvence“ je row-major, i když je vám řečeno, že máte použít konvenci matice OpenGL, která je prý column-major. Zde možný zmatek pramení ze skutečnosti, že mapování koeficientů v paměti se liší od mentální reprezentace, kterou si vytváříte pro matici „column-major“. Kódujete „řádek“, ale bylo řečeno, že používáte (z matematického hlediska) „sloupec“, z čehož vyplývají vaše potíže s pochopením, zda děláte věci správně, nebo špatně.

Důležité je vnímat matici jako reprezentaci souřadnicového systému definovaného třemi osami a translací. Kam a jak tato data uložíte do paměti, je zcela na vás. Předpokládejme, že tři vektory reprezentující tři osy souřadného systému jsou pojmenovány AX(x,y,z), AY(x,y,z), AZ(x,y,z) a vektor translace je označen (Tx, Ty, Tz), pak matematicky, pokud použijete sloupcový vektor, máte:

$$M = \begin{bmatrix} AXx & AYx & AZx & Tx\\\ AXy & AYy & AZy & Ty \\ AXz & AYz & AZz & Tz \\ 0 & 0 & 1 & 1\end{bmatrix}$

Osi soustavy souřadnic se zapisují vertikálně. Nyní máte-li při použití řádkové matice:

$$M = \begin{bmatrix}. AXx & AXy & AXz & 0\\ AYx & AYy & AYz & 0 \\ AZx & AZy & AZz & 0 \\ Tx & Ty & Tz & 1\end{bmatrix}$

Osi soustavy souřadnic jsou zapsány vodorovně. Problém, který nyní nastává ve světě počítačů, je, jak tyto koeficienty uložit do paměti. Můžete také udělat:

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

Říká vám to ale, jakou konvenci použijete? Ne. Můžete také napsat:

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

nebo:

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

Opět vám to nedává konkrétní informaci o tom, kterou „matematickou“ konvenci používáte. Prostě ukládáte 16 koeficientů do paměti různými způsoby a to je naprosto v pořádku, pokud víte, jaký způsob to je, abyste k nim mohli později vhodně přistupovat. Nyní mějte na paměti, že vektor vynásobený maticí by vám měl dát stejný vektor, ať už použijete řádkový nebo sloupcový matematický zápis. Důležité tedy skutečně je, abyste vynásobili souřadnice (x,y,z) svého vektoru správnými koeficienty z matice, což vyžaduje znalost toho, jak jste se „vy“ rozhodli uložit koeficient matice do paměti:

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 * AZz + v.z * AZz + Tz}

Tuto funkci jsme napsali proto, abychom zdůraznili skutečnost, že bez ohledu na to, jakou konvenci použijete, výsledkem násobení vektor * matice je pouze násobení a sčítání mezi vstupními souřadnicemi vektoru a souřadnicemi os souřadnicového systému AX, AY a AZ (bez ohledu na použitý zápis a bez ohledu na způsob jejich uložení v paměti). Pokud použijete:

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

musíte zavolat:

vecMatMult(v, m, m, m, m, m, m, m, m, ….

Pokud použijete:

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

musíte zavolat:

vecMatMult(v, m, m, m, m, m, m, m, m, m, m, m, m, ….

To vám říká, jakou konvenci použijete? Ne, jen musíte při násobení vec * mat volat správné koeficienty na správných místech. A to je vše, jakkoli se to může zdát nepříjemné.“ Nyní je situace trochu jiná, pokud jde o násobení mat * mat. Můžete předpokládat, že pořadí, v jakém násobíte matice, není stejné. Takže R * S * T není totéž jako T * S * R. Na pořadí skutečně záleží. Pokud nyní opět použijete „řádkový major“, pak matematicky musíte napsat:

mt11 = ml11 * mr11 + ml12 * mr21 + ml13 * mr31 + ml14 * mr41

kde ml je levá matice a mr pravá: mt = ml * mr. Všimněte si však, že jsme nepoužívali závorky pro přístupové indexy, protože nechceme naznačit, že zde přistupujeme k prvkům uloženým v 1D poli. Mluvíme pouze o koeficientech matic, jak jsou zapsány na papíře. Pokud to chcete zapsat v jazyce C++, pak vše závisí na tom, jak máte uloženy koeficienty v paměti, jak bylo naznačeno výše.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.