做游戲開發的程序員很少有空去寫博客,最近離職后休息了一下,去盛大面試被問到切線空間,之前雖然大致理解了,但是當時又被難到了,
覺得還是要總結一下自己學的東西,講的清楚才算真正的理解。
最近看完了線性代數的本質的一套視頻,感覺以前在學校學到的都是渣。
看完后對這種矩陣變換有了個全新的基礎認知,據說3D引擎都是從一個軟渲染開始,學了部分向量基礎后就打算開始學習下這個玩意
軟渲染至少得有個向量,矩陣運算的數學庫,然后就是lookat函數了,這個函數的主要作用就是構建一個視圖矩陣(view矩陣)
view矩陣的目標是把世界坐標系的物體的所有頂點轉換到攝像機坐標系(注:這里涉及的矩陣知識點叫基變換)
基變換的推導基本如下,涉及的知識點挺多:(基向量,矩陣線性變換,逆矩陣,單位矩陣,向量張成空間,正交矩陣,矩陣轉置)
攝像機在2個坐標系有2個位置
第1個是世界坐標系基向量通過旋轉跟平移得到攝像機的3個基向量在世界坐標系的位置
第2個是攝像機坐標系中3個基向量相對自己原點的標准基向量
攝像機的3個基向量在世界坐標系中的張成空間是整個空間(注:就是表示空間中的任何一個向量都可以用攝像機在世界坐標系中的3個向量通過乘以某個系數來表示,就好像y=x可以表示2維坐標系里斜率為45度角的所有點)
那么我們這里也可以把世界坐標系中的某個向量表示成類似結果:世界坐標系中的某個向量 = 系數1 * 攝像機在世界坐標系的3個基向量
這個系數1就是這個向量在攝像機坐標系中的所表示的向量
我們來進一步拆分上面的公式,攝像機的3個基向量也有上面類似的結論,因為世界坐標系的3個基向量也是張成整個空間,也就是說攝像機的每個基向量也可以通過世界坐標的3個基向量乘以某個系數2表示
那么公式就可以寫成:世界坐標系中的某個向量 = 系數1 * 系數2 * 世界坐標系的基向量
如果是2D的數序,我們知道要去求系數1,就是除以系數2和世界坐標系的基向量
在線性代數里這個系數1和系數2都表示某種矩陣變換,除法的表示方法是兩邊各乘以一個矩陣的逆
那么最后的公式就是: 系數1 = 世界坐標系中的某個向量 * 系數2的逆 * 世界坐標系的基向量的逆
轉化下最后的意思是這樣:任意在視圖坐標系表示的坐標 = 這個坐標在世界坐標系的坐標 * 攝像機線性變換的逆 * 世界坐標系單位矩陣的逆
世界坐標系單位矩陣的逆就是自身,單位矩陣做為變換參與運算時,轉換結果不變(這個是很重要的性質)
那么公式就可以簡化為:任意在視圖坐標系表示的坐標 = 這個坐標在世界坐標系的坐標 * 攝像機線性變換的逆
就像開頭所說view矩陣的目標是把世界坐標系的物體的所有頂點轉換到攝像機坐標系,那么這個攝像機線性變換的逆就是我們需要的view矩陣
這樣我們就可以開始拷貝網上別人的一些圖跟代碼來表示下了。
看看lookat的 3個位置向量
1個是眼睛的位置 eyePos
1個是看的目標點位置 tarPos
剩下1個是攝像機頭頂方向upVec(0,1,0)
先求出攝像機的3個基向量(U,V,W),基向量是互相垂直的3個單位向量
視線方向是眼睛指向目標,所以是tarPos-eyePos得到viewDir.normalize(),這個就是W了,基向量必須單位化
根據頭頂方向upVec跟W可以通過叉積得到跟這2個向量組成的屏幕垂直的向量,U = W x upVec
upVec雖然向上,但不一定跟W,U垂直,所以重新求V,同上V = W x U
這樣就得到U,V,W 3個基向量了,也就是下面的C,
C可以拆分成平移和旋轉2個變換, ,其中T是平移變化,R是旋轉變化,他的逆變換就是
T的逆矩陣為:
opengl的矩陣每行可以拆分成 x,y,z,w ,w是平移的成分,x,y,z就是旋轉的成分了。
上面的U,V,W是只有旋轉沒有平移的R了。
所以就有了這個
注意:這里又有個重要性質,正交矩陣的逆矩陣就是就是它的轉置矩陣
R是正交矩陣,所以R的逆就是R的轉置矩陣 ,注意看,行列是互換的。
以上就是視圖矩陣推導完整過程