3D数学 向量和矩阵
向量和矩陣
- 坐標(biāo)系
- 左手系、右手系
- 向量常用公式
- 向量的模(長(zhǎng)度)
- 標(biāo)準(zhǔn)化向量
- 點(diǎn)積
- 叉積
- 矩陣常用性質(zhì)
- 逆矩陣性質(zhì)
- 正交矩陣
- 齊次空間
- 坐標(biāo)空間變換推到過(guò)程
- MVP矩陣
- 局部空間
- 世界空間
- 模型矩陣
- 觀(guān)察空間
- 觀(guān)察矩陣
- 裁剪空間
- 透視投影
- 正交投影
- 屏幕空間
- 容易讓人產(chǎn)生誤會(huì)的地方
- MVP后的透視除法
坐標(biāo)系
左手系、右手系
也就是把拇指當(dāng)作xxx軸,食指當(dāng)作yyy軸,中指當(dāng)作zzz軸,來(lái)考慮三維坐標(biāo)。
向量常用公式
向量的模(長(zhǎng)度)
公式:∣v?∣=vx?vx+vy?vy+vz?vz|\vec{v}| = \sqrt{v_x*v_x+v_y*v_y+v_z*v_z}∣v∣=vx??vx?+vy??vy?+vz??vz??
代碼:float length(vec3 v) { return sqrt(v.x*v.x+v.y*v.y+v.z*v.z); }
幾何意義:得到v?\vec{v}v的長(zhǎng)度。
標(biāo)準(zhǔn)化向量
公式:v?norm=v?∣v?∣\vec{v}_{norm} = \frac{ \vec{v} }{ |\vec{v}| }vnorm?=∣v∣v?
代碼:vec3 normal(vec3 v) { return v / length(v); }
幾何意義:得到v?\vec{v}v的單位向量。
點(diǎn)積
| 公式A:a??b?=b??a?=axbx+ayby+azbz\vec{a} \cdot \vec{b} = \vec{b} \cdot \vec{a} = a_x b_x+a_yb_y+a_zb_za?b=b?a=ax?bx?+ay?by?+az?bz? | |
| 公式B1:a??b?=∣a?∣∣b?∣cos(θ)\vec{a} \cdot \vec{b} = \vert \vec{a} \vert \vert \vec{b} \vert cos{(\theta)}a?b=∣a∣∣b∣cos(θ) 公式B2:θ=arccos?(a??b?∣a?∣∣b?∣)\theta = \arccos{(\frac{\vec{a} \cdot \vec{b}}{\vert \vec{a} \vert \vert \vec{b} \vert})}θ=arccos(∣a∣∣b∣a?b?) | 計(jì)算兩個(gè)向量的夾角 |
| 公式C:v?p=n?v??n?∣n∣2\vec{v}_p = \vec{n}\frac{\vec{v} \cdot \vec{n}}{{\vert n \vert}^2}vp?=n∣n∣2v?n? | v?\vec{v}v在n?\vec{n}n上的投影v?p\vec{v}_pvp? |
公式A代碼:float dot(vec3 a, vec3 b) { return a.x*b.x+a.y*b.y+a.z*b.z; }
公式B1代碼:float dot(vec3 a, vec3 b) { return length(a)*length(b)*angle(a, b); }
公式B2代碼:float angle(vec3 a, vec3 b) { return acos(dot(a, b)/(length(a)*length(b))); }
公式C代碼:vec3 vec_projection(vec3 v, vec3 n) { return n*(dot(v, n)/pow(length(n), 2)); }
叉積
| 公式A:a?×b?=[aybz?azbyazbx?axbzaxby?aybx]\vec{a}\times\vec{b} = \begin{bmatrix} a_yb_z-a_zb_y & a_zb_x-a_xb_z & a_xb_y-a_yb_x \end{bmatrix}a×b=[ay?bz??az?by??az?bx??ax?bz??ax?by??ay?bx??] | 由a?\vec{a}a、b?\vec{b}b組成的面的法向量; |
| 公式B:S=∣a?×b?∣=∣a?∣∣b?∣sin?(θ)S=\vert \vec{a}\times\vec{b} \vert = \vert \vec{a} \vert \vert \vec{b} \vert \sin{(\theta)}S=∣a×b∣=∣a∣∣b∣sin(θ) | 由a?\vec{a}a、b?\vec{b}b組成的平行四邊形的面積; 也是由a?\vec{a}a、b?\vec{b}b組成的面的法向量長(zhǎng)度; |
矩陣常用性質(zhì)
矩陣能夠表示線(xiàn)性變換,平常會(huì)用的有旋轉(zhuǎn)、縮放、平移、鏡像、仿射。
逆矩陣性質(zhì)
公式:M(M?1)=IM(M^{-1})=IM(M?1)=I
幾何解釋:逆矩陣可以“撤銷(xiāo)”原變換。 (v?M)M?1=v?(MM?1)=v?I=v?(\vec{v}M)M^{-1}=\vec{v}(MM^{-1})=\vec{v}I=\vec{v}(vM)M?1=v(MM?1)=vI=v
逆矩陣:如果矩陣是非奇異矩陣,那么該矩陣可逆。
非奇異矩陣:非奇異矩陣的行列式不為零。
可逆矩陣求逆方法:
正交矩陣
定義:若方陣MMM是正交的,則當(dāng)且僅當(dāng)MMM與它的轉(zhuǎn)置MTM^{T}MT的乘積等于單位矩陣。
公式:M正交?MMT=IM_{正交} \Longleftrightarrow MM^{T}=IM正交??MMT=I
推到公式:M正交?MT=M?1M_{正交} \Longleftrightarrow M^{T} = M^{-1}M正交??MT=M?1
正交矩陣必須滿(mǎn)足的條件:
齊次空間
齊次空間有以下意義:
坐標(biāo)空間變換推到過(guò)程
轉(zhuǎn)自:知乎的一篇文章
MVP矩陣
移步:坐標(biāo)系統(tǒng)
局部空間
局部空間是指物體所在的坐標(biāo)空間,即對(duì)象最開(kāi)始所在的地方。想象你在一個(gè)建模軟件(比如說(shuō)Blender)中創(chuàng)建了一個(gè)立方體。你創(chuàng)建的立方體的原點(diǎn)有可能位于(0, 0, 0),即便它有可能最后在程序中處于完全不同的位置。甚至有可能你創(chuàng)建的所有模型都以(0, 0, 0)為初始位置(譯注:然而它們會(huì)最終出現(xiàn)在世界的不同位置)。所以,你的模型的所有頂點(diǎn)都是在局部空間中:它們相對(duì)于你的物體來(lái)說(shuō)都是局部的。
我們一直使用的那個(gè)箱子的頂點(diǎn)是被設(shè)定在-0.5到0.5的坐標(biāo)范圍中,(0, 0)是它的原點(diǎn)。這些都是局部坐標(biāo)。
從數(shù)據(jù)結(jié)構(gòu)的理解角度為:每個(gè)頂點(diǎn)都在局部空間中。
typedef struct Vectex {vec3 position;vec2 uv;vec3 normal; };// 記錄模型中每一個(gè)點(diǎn)的信息 typedef struct Mesh {Vectex *vectexs; // 頂點(diǎn)數(shù)組unsigned int *indices; // 索引數(shù)組 }世界空間
如果我們將我們所有的物體導(dǎo)入到程序當(dāng)中,它們有可能會(huì)全擠在世界的原點(diǎn)(0, 0, 0)上,這并不是我們想要的結(jié)果。我們想為每一個(gè)物體定義一個(gè)位置,從而能在更大的世界當(dāng)中放置它們。世界空間中的坐標(biāo)正如其名:是指頂點(diǎn)相對(duì)于(游戲)世界的坐標(biāo)。如果你希望將物體分散在世界上擺放(特別是非常真實(shí)的那樣),這就是你希望物體變換到的空間。物體的坐標(biāo)將會(huì)從局部空間變換到世界空間;該變換是由模型矩陣(Model Matrix)實(shí)現(xiàn)的。
模型矩陣
在擁有了世界空間的前提條件下,我們能夠自然的求出模型矩陣。模型矩陣是一種變換矩陣,它能通過(guò)對(duì)物體進(jìn)行位移、縮放、旋轉(zhuǎn)來(lái)將它置于它本應(yīng)該在的位置或朝向。
假設(shè)世界空間的原點(diǎn)為(0, 0, 0),那么從數(shù)據(jù)結(jié)構(gòu)上面看:每個(gè)獨(dú)立的GameObject都會(huì)有一個(gè)Transform信息。 其中modelMatrix * go.meshInfos[i].vertex => 局部空間 -> 世界空間。
class Transform {Matrix4x4 translation;Matrix4x4 rotate;Matrix4x4 scale; }class GameObject {Mesh *meshInfos; // 可能有多個(gè)網(wǎng)格Transform transform; // model矩陣// model矩陣的方法Matrix4x4 matrix() { return transform.translation * transform.rotate * transform.scale; } }void drawCall() {// 假設(shè)是一個(gè)模型了GameObject go;// 將transform輸入給頂點(diǎn)著色器shader.setUniformMatrix4fv("modelMatrix", go.transform.matrix());// 渲染shader.drawElements(go.meshInfos); }觀(guān)察空間
觀(guān)察空間經(jīng)常被人們稱(chēng)之OpenGL的攝像機(jī)(Camera)(所以有時(shí)也稱(chēng)為攝像機(jī)空間(Camera Space)或視覺(jué)空間(Eye Space))。觀(guān)察空間是將世界空間坐標(biāo)轉(zhuǎn)化為用戶(hù)視野前方的坐標(biāo)而產(chǎn)生的結(jié)果。因此觀(guān)察空間就是從攝像機(jī)的視角所觀(guān)察到的空間。而這通常是由一系列的位移和旋轉(zhuǎn)的組合來(lái)完成,平移/旋轉(zhuǎn)場(chǎng)景從而使得特定的對(duì)象被變換到攝像機(jī)的前方。這些組合在一起的變換通常存儲(chǔ)在一個(gè)觀(guān)察矩陣(View Matrix)里,它被用來(lái)將世界坐標(biāo)變換到觀(guān)察空間。
觀(guān)察矩陣
觀(guān)察矩陣是一種變換矩陣,一般有2種方式構(gòu)造這個(gè)矩陣。
裁剪空間
在一個(gè)頂點(diǎn)著色器運(yùn)行的最后,OpenGL期望所有的坐標(biāo)都能落在一個(gè)特定的范圍內(nèi),且任何在這個(gè)范圍之外的點(diǎn)都應(yīng)該被裁剪掉(Clipped)。被裁剪掉的坐標(biāo)就會(huì)被忽略,所以剩下的坐標(biāo)就將變?yōu)槠聊簧峡梢?jiàn)的片段。這也就是裁剪空間(Clip Space)名字的由來(lái)。為了將頂點(diǎn)坐標(biāo)從觀(guān)察變換到裁剪空間,我們需要定義一個(gè)投影矩陣(Projection Matrix)。
由投影矩陣創(chuàng)建的觀(guān)察箱(Viewing Box)被稱(chēng)為平截頭體(Frustum),每個(gè)出現(xiàn)在平截頭體范圍內(nèi)的坐標(biāo)都會(huì)最終出現(xiàn)在用戶(hù)的屏幕上。將特定范圍內(nèi)的坐標(biāo)轉(zhuǎn)化到標(biāo)準(zhǔn)化設(shè)備坐標(biāo)系NDC的過(guò)程(而且它很容易被映射到2D觀(guān)察空間坐標(biāo))被稱(chēng)之為投影(Projection),因?yàn)槭褂猛队熬仃嚹軐?D坐標(biāo)投影(Project)到很容易映射到2D的標(biāo)準(zhǔn)化設(shè)備坐標(biāo)系中。
一旦所有頂點(diǎn)被變換到裁剪空間,最終的操作——透視除法(Perspective Division)將會(huì)執(zhí)行,在這個(gè)過(guò)程中我們將位置向量的x,y,z分量分別除以向量的齊次w分量;透視除法是將4D裁剪空間坐標(biāo)變換為3D標(biāo)準(zhǔn)化設(shè)備坐標(biāo)的過(guò)程。這一步會(huì)在每一個(gè)頂點(diǎn)著色器運(yùn)行的最后被自動(dòng)執(zhí)行。
透視投影
一個(gè)透視平截頭體可以被看作一個(gè)不均勻形狀的箱子,在這個(gè)箱子內(nèi)部的每個(gè)坐標(biāo)都會(huì)被映射到裁剪空間上的一個(gè)點(diǎn)。
API:perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);它的第一個(gè)參數(shù)定義了fov的值,它表示的是視野(Field of View),并且設(shè)置了觀(guān)察空間的大小。如果想要一個(gè)真實(shí)的觀(guān)察效果,它的值通常設(shè)置為45.0f,但想要一個(gè)末日風(fēng)格的結(jié)果你可以將其設(shè)置一個(gè)更大的值。第二個(gè)參數(shù)設(shè)置了寬高比,由視口的寬除以高所得。第三和第四個(gè)參數(shù)設(shè)置了平截頭體的近和遠(yuǎn)平面。我們通常設(shè)置近距離為0.1f,而遠(yuǎn)距離設(shè)為100.0f。所有在近平面和遠(yuǎn)平面內(nèi)且處于平截頭體內(nèi)的頂點(diǎn)都會(huì)被渲染。
正交投影
正交投影矩陣定義了一個(gè)類(lèi)似立方體的平截頭箱,它定義了一個(gè)裁剪空間,在這空間之外的頂點(diǎn)都會(huì)被裁剪掉。創(chuàng)建一個(gè)正交投影矩陣需要指定可見(jiàn)平截頭體的寬、高和長(zhǎng)度。在使用正交投影矩陣變換至裁剪空間之后,任何在這個(gè)平截頭體以外的坐標(biāo)將會(huì)受到裁剪。
API:ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);前兩個(gè)參數(shù)指定了平截頭體的左右坐標(biāo),第三和第四參數(shù)指定了平截頭體的底部和頂部。通過(guò)這四個(gè)參數(shù)我們定義了近平面和遠(yuǎn)平面的大小,然后第五和第六個(gè)參數(shù)則定義了近平面和遠(yuǎn)平面的距離。這個(gè)投影矩陣會(huì)將處于這些x,y,z值范圍內(nèi)的坐標(biāo)變換為標(biāo)準(zhǔn)化設(shè)備坐標(biāo)。
屏幕空間
在完成以上MVP矩陣運(yùn)算后,映射到屏幕空間的事情將交給glViewport,由它來(lái)設(shè)置屏幕顯示區(qū)域。
容易讓人產(chǎn)生誤會(huì)的地方
MVP后的透視除法
1、 頂點(diǎn)著色器乘上MVP矩陣后,輸出的頂點(diǎn)為裁剪空間的點(diǎn)并不是NDC坐標(biāo)空間的點(diǎn),NDC轉(zhuǎn)換是由GPU自己去做的。
2、 片段著色器的輸入不是NDC坐標(biāo)空間or裁剪空間的點(diǎn),而是屏幕空間的點(diǎn)。
總結(jié)
以上是生活随笔為你收集整理的3D数学 向量和矩阵的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2020中国奢侈品消费者数字行为洞察报告
- 下一篇: 软件开发包(SDK)安全与合规报告(20