3D数学 向量和矩阵
向量和矩陣
- 坐標系
- 左手系、右手系
- 向量常用公式
- 向量的模(長度)
- 標準化向量
- 點積
- 叉積
- 矩陣常用性質
- 逆矩陣性質
- 正交矩陣
- 齊次空間
- 坐標空間變換推到過程
- MVP矩陣
- 局部空間
- 世界空間
- 模型矩陣
- 觀察空間
- 觀察矩陣
- 裁剪空間
- 透視投影
- 正交投影
- 屏幕空間
- 容易讓人產生誤會的地方
- MVP后的透視除法
坐標系
左手系、右手系
也就是把拇指當作xxx軸,食指當作yyy軸,中指當作zzz軸,來考慮三維坐標。
向量常用公式
向量的模(長度)
公式:∣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的長度。
標準化向量
公式: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的單位向量。
點積
| 公式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?) | 計算兩個向量的夾角 |
| 公式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組成的面的法向量長度; |
矩陣常用性質
矩陣能夠表示線性變換,平常會用的有旋轉、縮放、平移、鏡像、仿射。
逆矩陣性質
公式:M(M?1)=IM(M^{-1})=IM(M?1)=I
幾何解釋:逆矩陣可以“撤銷”原變換。 (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是正交的,則當且僅當MMM與它的轉置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
正交矩陣必須滿足的條件:
齊次空間
齊次空間有以下意義:
坐標空間變換推到過程
轉自:知乎的一篇文章
MVP矩陣
移步:坐標系統
局部空間
局部空間是指物體所在的坐標空間,即對象最開始所在的地方。想象你在一個建模軟件(比如說Blender)中創建了一個立方體。你創建的立方體的原點有可能位于(0, 0, 0),即便它有可能最后在程序中處于完全不同的位置。甚至有可能你創建的所有模型都以(0, 0, 0)為初始位置(譯注:然而它們會最終出現在世界的不同位置)。所以,你的模型的所有頂點都是在局部空間中:它們相對于你的物體來說都是局部的。
我們一直使用的那個箱子的頂點是被設定在-0.5到0.5的坐標范圍中,(0, 0)是它的原點。這些都是局部坐標。
從數據結構的理解角度為:每個頂點都在局部空間中。
typedef struct Vectex {vec3 position;vec2 uv;vec3 normal; };// 記錄模型中每一個點的信息 typedef struct Mesh {Vectex *vectexs; // 頂點數組unsigned int *indices; // 索引數組 }世界空間
如果我們將我們所有的物體導入到程序當中,它們有可能會全擠在世界的原點(0, 0, 0)上,這并不是我們想要的結果。我們想為每一個物體定義一個位置,從而能在更大的世界當中放置它們。世界空間中的坐標正如其名:是指頂點相對于(游戲)世界的坐標。如果你希望將物體分散在世界上擺放(特別是非常真實的那樣),這就是你希望物體變換到的空間。物體的坐標將會從局部空間變換到世界空間;該變換是由模型矩陣(Model Matrix)實現的。
模型矩陣
在擁有了世界空間的前提條件下,我們能夠自然的求出模型矩陣。模型矩陣是一種變換矩陣,它能通過對物體進行位移、縮放、旋轉來將它置于它本應該在的位置或朝向。
假設世界空間的原點為(0, 0, 0),那么從數據結構上面看:每個獨立的GameObject都會有一個Transform信息。 其中modelMatrix * go.meshInfos[i].vertex => 局部空間 -> 世界空間。
class Transform {Matrix4x4 translation;Matrix4x4 rotate;Matrix4x4 scale; }class GameObject {Mesh *meshInfos; // 可能有多個網格Transform transform; // model矩陣// model矩陣的方法Matrix4x4 matrix() { return transform.translation * transform.rotate * transform.scale; } }void drawCall() {// 假設是一個模型了GameObject go;// 將transform輸入給頂點著色器shader.setUniformMatrix4fv("modelMatrix", go.transform.matrix());// 渲染shader.drawElements(go.meshInfos); }觀察空間
觀察空間經常被人們稱之OpenGL的攝像機(Camera)(所以有時也稱為攝像機空間(Camera Space)或視覺空間(Eye Space))。觀察空間是將世界空間坐標轉化為用戶視野前方的坐標而產生的結果。因此觀察空間就是從攝像機的視角所觀察到的空間。而這通常是由一系列的位移和旋轉的組合來完成,平移/旋轉場景從而使得特定的對象被變換到攝像機的前方。這些組合在一起的變換通常存儲在一個觀察矩陣(View Matrix)里,它被用來將世界坐標變換到觀察空間。
觀察矩陣
觀察矩陣是一種變換矩陣,一般有2種方式構造這個矩陣。
裁剪空間
在一個頂點著色器運行的最后,OpenGL期望所有的坐標都能落在一個特定的范圍內,且任何在這個范圍之外的點都應該被裁剪掉(Clipped)。被裁剪掉的坐標就會被忽略,所以剩下的坐標就將變為屏幕上可見的片段。這也就是裁剪空間(Clip Space)名字的由來。為了將頂點坐標從觀察變換到裁剪空間,我們需要定義一個投影矩陣(Projection Matrix)。
由投影矩陣創建的觀察箱(Viewing Box)被稱為平截頭體(Frustum),每個出現在平截頭體范圍內的坐標都會最終出現在用戶的屏幕上。將特定范圍內的坐標轉化到標準化設備坐標系NDC的過程(而且它很容易被映射到2D觀察空間坐標)被稱之為投影(Projection),因為使用投影矩陣能將3D坐標投影(Project)到很容易映射到2D的標準化設備坐標系中。
一旦所有頂點被變換到裁剪空間,最終的操作——透視除法(Perspective Division)將會執行,在這個過程中我們將位置向量的x,y,z分量分別除以向量的齊次w分量;透視除法是將4D裁剪空間坐標變換為3D標準化設備坐標的過程。這一步會在每一個頂點著色器運行的最后被自動執行。
透視投影
一個透視平截頭體可以被看作一個不均勻形狀的箱子,在這個箱子內部的每個坐標都會被映射到裁剪空間上的一個點。
API:perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);它的第一個參數定義了fov的值,它表示的是視野(Field of View),并且設置了觀察空間的大小。如果想要一個真實的觀察效果,它的值通常設置為45.0f,但想要一個末日風格的結果你可以將其設置一個更大的值。第二個參數設置了寬高比,由視口的寬除以高所得。第三和第四個參數設置了平截頭體的近和遠平面。我們通常設置近距離為0.1f,而遠距離設為100.0f。所有在近平面和遠平面內且處于平截頭體內的頂點都會被渲染。
正交投影
正交投影矩陣定義了一個類似立方體的平截頭箱,它定義了一個裁剪空間,在這空間之外的頂點都會被裁剪掉。創建一個正交投影矩陣需要指定可見平截頭體的寬、高和長度。在使用正交投影矩陣變換至裁剪空間之后,任何在這個平截頭體以外的坐標將會受到裁剪。
API:ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);前兩個參數指定了平截頭體的左右坐標,第三和第四參數指定了平截頭體的底部和頂部。通過這四個參數我們定義了近平面和遠平面的大小,然后第五和第六個參數則定義了近平面和遠平面的距離。這個投影矩陣會將處于這些x,y,z值范圍內的坐標變換為標準化設備坐標。
屏幕空間
在完成以上MVP矩陣運算后,映射到屏幕空間的事情將交給glViewport,由它來設置屏幕顯示區域。
容易讓人產生誤會的地方
MVP后的透視除法
1、 頂點著色器乘上MVP矩陣后,輸出的頂點為裁剪空間的點并不是NDC坐標空間的點,NDC轉換是由GPU自己去做的。
2、 片段著色器的輸入不是NDC坐標空間or裁剪空間的點,而是屏幕空間的點。
總結
以上是生活随笔為你收集整理的3D数学 向量和矩阵的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2020中国奢侈品消费者数字行为洞察报告
- 下一篇: 软件开发包(SDK)安全与合规报告(20