實現個簡單的固定渲染管線軟渲染器不算複雜,差不多700行程式碼就可以搞定了。之所以很多人用 D3D用的很熟,寫軟渲染卻坑坑窪窪,主要是現在大部分講圖形的書,講到透視投影時就是分析一下透視變換矩陣如何生成,頂點如何計算就跳到其他講模型或者光照的部分了。
因為今天基本上是直接用 D3D 或者 OGL,真正光柵化的部分不瞭解也不影響使用,所以大部分教材都直接跳過了一大段,攝像機座標系如何轉換?三角形如何生成?CVV邊緣如何檢測?四維座標如何裁剪?邊緣及步長如何計算?掃描線該如何繪製?透視紋理對映具體程式碼該怎麼寫?framebuffer zbuffer 到底該怎麼用?z-test 到底是該 test z 還是 w 還是 1/z 還是 1/w ?這些都沒講。
早年培訓學生時候,我花兩天時間寫的一個 DEMO,今天拿出來重新調整註釋一下,效能和功能當然比不過高大上的軟體渲染器。但一般來講,工程類專案程式碼不容易閱讀,太多邊界情況和太多細節優化容易讓初學者迷失,這個 mini3d 的專案不做任何優化,主要目的就是為了突出主幹:
原始碼:skywind3000/mini3d · GitHub
可執行:http://www.skywind.me/mw/images/c/c8/Mini3d.7z
操作方式:左右鍵旋轉,前後鍵前進後退,空格鍵切換模式,ESC退出。
特性介紹:
- 單個檔案:原始碼只有一個 mini3d.c,單個檔案實現所有內容,閱讀容易。
- 獨立編譯:沒有任何第三方庫依賴,沒有複雜的工程目錄。
- 模型標準:標準 D3D 座標模型,左手系 + WORLD/VIEW/PROJECTION 三矩陣
- 實現裁剪:簡單 CVV 裁剪
- 紋理支援:最大支援 1024 x 1024 的紋理
- 深度快取:使用深度快取判斷影象前後
- 邊緣計算:精確的多邊形邊緣覆蓋計算
- 透視貼圖:透視紋理對映以及透視色彩填充
- 實現精簡:渲染部分只有 700行, 模組清晰,主幹突出。
- 詳細註釋:主要程式碼詳細註釋
截圖效果
顏色填充
透視紋理對映
線框圖
增加光照和二次線性插值(朋友給 Mini3D加的平行光源)效果還行:
閱讀要求:
- 看過並瞭解 D3D / OGL的矩陣變換。
- 用 D3D / OGL 完成過簡單程式。
實現說明:
- transform:實現座標變換,和書本手冊同
- vertex: 如何定義頂點?如何定義邊?如何定義掃描線?如何定義渲染主體(trapezoid)?
- device: 裝置,如何 projection,如何裁剪和歸一化,如何切分三角形,如何頂點排序?
- trapezoid:如何生成 trape,如何生成邊,如何計算步長,如何計算掃描線
- scanline:如何繪製掃描線,如何透視糾正,如何使用深度快取,如何繪製
基礎練習:先前給學生的作業
- 增加背面剔
- 增加簡單光照
- 提供更多渲染模式
- 實現二次線性差值的紋理讀取
擴充套件練習:給有餘力的學生
推導並證明程式中用到的所有幾何知識
優化頂點計算效能
優化 draw_scanline 效能
從 BMP/TGA 檔案載入紋理
載入 BSP 場景並實現漫遊
其他內容
當年還用不了 D3D 和 OGL ,開發遊戲,做圖形實現軟體渲染是必備技能,當年機型差,連浮點數都用不了,要用定點數來計算,矩陣稍不注意就越界了。計算透視糾正還是一個比較昂貴的工作,更多遊戲使用仿射紋理繪製,只是把離螢幕近的多邊形切割成更小的三角形,讓人看起來沒有那麼明顯。即便到了 Quake 年代,計算 1/z 的除法也只是四個點才算一次(經過精確計算CPU週期,繪製四個點時下一個點的 1/z剛好算完),Quake 的四個點內也還是仿射紋理繪製……
那時顯示卡沒普及,光軟體渲染器的優化就是一個無底洞,今天有了 OGL/D3D和顯示卡,人的精力才能充分集中在更高層次的場景組織、層次細節、動態光照等功能上。然而有空的時候,花個一週時間坐下來了解一下這部分的大概原理,推導所用到的數學模型,也能幫助大家更好的理解底層執行機制,寫出更加優化的程式碼來。
PS:光線跟蹤版本的軟體渲染,考慮光照的話,簡單實現起來差不多 500 行,比這個要簡單一些。各位有興趣也可以嘗試一下,就是簡單渲染個立方體足夠了。