投影方式- Unity3D遊戲開發培訓
投影方式- Unity3D遊戲開發培訓
作者:鄧家海
2018-02-12 20:33:13
摘 要
透視投影是3D渲染的基本概念,也是3D程序設計的基礎。掌握透視投影的原理對於深入理解其他3D渲染管線具有重要作用。本文詳細介紹了透視投影的原理和算法實現,包括透視投影的標準模型、一般模型和屏幕坐標變換等,並通過VC實現了一個演示程序。
在Unity3D裏面,投影方式決定了我們人眼看到的場景,投影方式一般分為透視投影和正交投影兩種,透視投影相當於我們人的眼睛看到的東西。越遠越小,越近越大。還會出現一個倒影。在物理學上面的小孔成像就是透視投影。正交投影是平行光源的投射,物體不會隨著距離的改變而改變。在計算機三維圖像裏面,投影是可以看作是一種將三維坐標變成二維坐標的方法。
相機設置
透視投影:與人的視覺系統相似,多用在三維平面中對三維世界的呈現。模型是由視點E和視平面P兩部分構成(要求E不在平面P上),視點就是觀察者的位置,也就是三維世界的角度,視平面就是渲染三維對象的二維平面圖,對於任意一點X,構造一條從E到X的射線R,R與平面P的交點X,p即是X點的透視投影結果。如圖所示:
圖 1-1
圖 1-2
正交投影:用於二維世界的呈現。
圖 1-3
無論遠近,大小是一樣的
3D默認是透視投影(Perspective),2D默認是正交投影(Orthographic)。
透視投影的實現
6.1 載入3D模型
使用Matt Fairfax實現的Model_3DS類支持3DS模型文件的載入,該類的實現非常簡單,而且很容易使用,具體可參考[7]。由於本文的DEMO只需要其中的模型載入功能,所以對源代碼進行了刪減,去掉了紋理加載(暫不需要)和渲染(我們自己實現)代碼,在析構函數中添加了資源釋放代碼。
6.2 視圖變換
為表示透視投影的一般模型,實現了KCamera類,除保存視點的位置和姿態,還保存視圖變換矩陣m_kmView,隨著視點位置和姿態的變化,視圖矩陣也不斷更新,更新算法詳見第4節。對於世界坐標系中的任何一點v(x, y, z),通過v = m_kmView*v將其變換到透視投影的標準模型坐標系,詳見KCamera::Transform函數。
6.3 透視變換
KFrustum類用來對透視投影的標準模型進行建模,其成員包括視平面的尺寸大小,以及近截面和遠截面的z軸坐標。KFrustum通過Project函數將視圖變換的結果變換為透視坐標。算法的原理見第3節,代碼實現如下:
1 void KFrustum::Project(KVector3& v) 2 3 { 4 5 // xp = x*n/z, yp = y*n/z, zp = n. 6 7 float fFactor = GetNear()/v.z; 8 9 v.x *= fFactor; 10 11 v.y *= fFactor; 12 13 v.z = GetNear(); 14 15 }
6.4 屏幕變換
屏幕變換的算法通過宏實現,代碼如下:
1 #define ToScreen(v, Ws, Hs) / 2 3 {/ 4 5 float x = (v.x/GetWidth()+0.5f)*(Ws-1);/ 6 7 float y = (v.y/GetHeight()+0.5f)*(Hs-1);/ 8 9 v.x = KMath::Round(x);/ 10 11 v.y = KMath::Round(y);/ 12 13 }
6.5 渲染
Demo中的渲染使用軟件實現,沒有使用任何第三方圖形庫,主代碼在KCamera::Render函數中,它接收兩次參數:Model_3DS和KSurface,對Model_3DS中的頂點進行透視投影,然後將結果繪制到Ksurface中。函數代碼如下:
1 bool KCamera::Render(Model_3DS& m3DS, KSurface& kSurface) 2 3 { 4 5 kSurface.Fill(RGB(0,0,0)); // 背景為黑色 6 7 COLORREF crPen = RGB(255,0,0); // 用紅色繪制模型 8 9 10 11 KMatrix4 m = m_kmView; 12 13 int Ws = kSurface.GetWidth(); 14 15 int Hs = kSurface.GetHeight(); 16 17 18 19 for(int i=0; i<m3DS.numObjects; i++) 20 21 { 22 23 Model_3DS::Object& obj = m3DS.Objects[i]; 24 25 26 27 for(int n=0; n<obj.numFaces; n+=3) 28 29 { 30 31 int index = obj.Faces[n]*3; 32 33 KVector4 v0(obj.Vertexes[index], obj.Vertexes[index+1], obj.Vertexes[index+2]); 34 35 index = obj.Faces[n+1]*3; 36 37 KVector4 v1(obj.Vertexes[index], obj.Vertexes[index+1], obj.Vertexes[index+2]); 38 39 index = obj.Faces[n+2]*3; 40 41 KVector4 v2(obj.Vertexes[index], obj.Vertexes[index+1], obj.Vertexes[index+2]); 42 43 44 45 Transform(v0, Ws, Hs); 46 47 Transform(v1, Ws, Hs); 48 49 Transform(v2, Ws, Hs); 50 51 52 53 // 繪制網線 54 55 kSurface.MoveTo(v0.x, v0.y); 56 57 kSurface.LineTo(v1.x, v1.y, crPen); 58 59 kSurface.LineTo(v2.x, v2.y, crPen); 60 61 kSurface.LineTo(v0.x, v0.y, crPen); 62 63 } 64 65 } 66 67 68 69 return true; 70 71 }
投影方式- Unity3D遊戲開發培訓