如何寫一個軟體渲染器?

發表於2015-08-21

實現個簡單的固定渲染管線軟渲染器不算複雜,差不多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 行,比這個要簡單一些。各位有興趣也可以嘗試一下,就是簡單渲染個立方體足夠了。

相關文章