用C#實現一個簡易的軟件光柵化渲染器和第一人稱相機

  主要參考博客:https://blog.csdn.net/aceyan0718/article/details/51659381 文本能力有限,沒錯,下面的話基本也是抄的。。

這是一個用C#+winform實現的軟件光柵化渲染器,今天拿出來與大家分享一下,希望能起到拋磚引玉的作用,給新人一點啓發(結構比較簡單,註釋比較詳細^_^)(新人+1,也給我很多啓發),也歡迎司機們拍磚指點和交流~

目的:

 鞏固圖形編程知識,理解渲染流水線所做的事情。

實現功能:

1、將頂點數據進行一系列處理顯示到屏幕上(廢話。。。(¬_¬))

2、線框渲染模式、紋理渲染模式、頂點色模式

3、紋理uv座標和頂點色等的透視校正插值

4、紋理雙線性過濾採樣

5、背面消隱

6、Cvv簡單裁剪(後面筆者(我)又加上了一個稍微複雜的裁剪,重構三角形,下面會詳細介紹)

7、“基礎光照模型”(相當於D3D、OpenGL中的固定管線頂點光照)
 

截圖

gif軟件的問題。。注意中間的模型就好

GitHub地址: https://github.com/TonyZgj/graphic_test

零、準備階段

強烈推薦如果想了解矩陣和空間變換的知識,參考三藍一棕,講的可以說非常好了

https://space.bilibili.com/88461692/channel/detail?cid=9450

想要實現渲染器首先我們得知道“給定視點、三維物體、光源、照明模式,和紋理等元素,如何繪製一幅二維圖像”,這就必須提到一個詞“圖像繪製管線”(也稱繪製流水線),我們得渲染器正是要以軟件的形式來模擬這條流水線的運作。

流水線:http://www.cnblogs.com/wonderKK/p/5022226.html

理解了繪製管線,我們發現其中充滿對矩陣向量等數學工具的運用,那麼再實現流水線之前,我們必須先實現這些數學工具,這些數學類網上已經有很多的資料了,但是有幾點需要特別說明:

1、本程序中使用行矩陣與行向量
相關資料:D3d和openGl矩陣區別

http://blog.csdn.net/Nightmare/article/details/3983724

2、本程序使用左手座標系
3、實現了color類來方便進行顏色的運算,因爲進行光照的時候要使用顏色乘以顏色的計算方法,它跟向量的乘法不同,應該稱爲“Modulate(調製)”,注意不能用向量乘法的計算公式哦。
相關資料:

https://www.zhihu.com/question/24026277/answer/26433842

@Milo Yip 大神的博文裏面有一段講顏色計算的:

http://www.cnblogs.com/miloyip/archive/2010/03/29/1698953.html#1794025

4、矩陣求逆

由於要進行光照,我們需要變換頂點法線,那麼就需要矩陣求逆和求轉置的運算。(爲什麼需要逆轉置呢?請看:https://www.zhihu.com/question/27739027

其中矩陣求逆比較複雜,本程序使用的是伴隨矩陣的方法求矩陣的逆, 必須先求出矩陣的行列式和伴隨矩陣,這部分需要一定的線性代數基礎知識。

4.1矩陣求逆重要定理:


http://wenku.baidu.com/link?url=dga27GT5oSK8SDwHaeqPGL_raHnMLNP54oad7q8wJnMOqEpuT4_UAC-BHAeTsL8vGpYkRuphnXng99TLfHOx8_Nn7WMVycFRlUiMgSS-2Ve

4.2求矩陣行列式


本程序使用遞歸算法求行列式,請查看矩陣行列式的遞歸定義:

http://wenku.baidu.com/link?url=9TSCuxJdfypmsqZLYegHNuKYP1TABFwGT22v-cJut5wenWM-Ll2Bp6zqgDEeIvd9ChV3Laj3czAbdWtjSwaulHHSkUZtrLNcgLqNw2xrjb7,

4.3伴隨矩陣:


http://wenku.baidu.com/link?url=BVbtO30v7eygbZUloybJtpFkCrpAwypYNyIx7XGX-OItxdWz0w4Y2hpzux729bq4MLKnx0sWwS4mJwm8Nrnf_3zxG9RqzD7LND3mklrwyDe

實現了數學類,就可以正式進入流水線了:
 

至於矩陣可以看我另外一篇博客,https://blog.csdn.net/qq_33413868/article/details/88182378,我覺得講的還是挺詳細的。。

 

一、幾何階段

1、頂點從模型空間----->世界空間


這一步要生成世界矩陣(簡稱m),m是一系列平移、選擇等變換的組合

如果開啓了光照,我們還需要把模型空間的法線信息變換到世界空間,對頂點進行光照計算並保存光照結果顏色,以便在光柵化的時候進行差值和顏色調製(Modulate)

這裏實現的“簡單光照模型”可以參見《Cg教程_可編程實時圖形權威指南》第五章光照

2、世界空間----->相機空間


這一步需要生成視矩陣(簡稱v)

推導過程:@zdd的博客 http://www.cnblogs.com/graphics/archive/2012/07/12/2476413.html

上面的推導中寫錯了

應該是 

  rxry rz

[ ux uy uz ]

  dx dy dz

但是推導結果是對的

3、相機空間--->齊次剪裁空間


這裏要生成投影矩陣,簡稱p

Dx風格的投影矩陣推導:

http://www.cnblogs.com/graphics/archive/2012/07/25/2582119.html

 

本程序使用的投影矩陣:

視空間的頂點乘以這個矩陣之後被變換到齊次剪裁空間,並且w分量保存着視空間的z信息。

在齊次剪裁空間我們可以對頂點進行簡單的裁剪,既將不在

-w <= x <= w

-w<= y <= w

0 <= z <= w

這個範圍的頂點剔除掉,不進行渲染。

我這裏採用的是生成新的頂點的較爲複雜的裁剪方法,但是是屏幕座標裁剪,在網上找了好久沒找到視錐體裁剪的教程,水平有限自己肝不出來,所以還有很多可以深入挖掘的地方:)

Ps:爲了方便後續的透視校正插值,程序中還將1/z 保存在頂點數據中。

4、進行圖元裝配,也就是將頂點以一定的順序組裝成三角形(Primitive Assembly && Trianglesetup )


本程序中使用了頂點索引的方式來組織頂點數據,並且約定以逆時針順序組織的三角形的法線朝向屏幕外,換句話說就是逆時針順序組織的三角形看起來是正面。

有了這個約定就可以進行背面消隱:

http://blog.csdn.net/cppyin/article/details/6207206

5、透視除法


  對透視變換得到的含有深度信息(z)的齊次座標做透視除法。所謂透視除法,
  就是把透視變換後的齊次座標除以(z)。由於透視變換矩陣已經構造好了,當
  視錐體內部點經透視處理後的齊次座標除以(z)後,使得頂點進入
-1<= x <= 1

-1<= y <= 1

0 <= z <= 1  (CVV正方體中)。

6、映射到視口


將cvv正方體的頂點根據屏幕大小或者視口大小轉換爲屏幕座標
 

二、光柵化階段

1、Rasterization光柵化


光柵化決定哪些像素被幾何圖元覆蓋的過程

1.1 如果選擇線框模式,本程序使用的畫線方法是Bresenham快速畫直線算法


相關資料:http://www.cnblogs.com/gamesky/archive/2012/08/21/2648623.html

 

1.2頂點色模式和紋理模式


三角形光柵化算法:http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html 文章雖然是英文的,但是很好理解

光柵化過程當中,我們要對uv座標,頂點顏色、我們保存在頂點信息中的1/z等信息進行透視校正插值:

http://blog.csdn.net/popy007/article/details/5570803

文章中的重要結論:

我們發現s/z、t/z和x’、y’也是線性關係。而我們之前知道1/z和x’、y’是線性關係。則我們得出新的思路:對1/z關於x’、y’插值得到1/z’,然後對s/z、t/z關於x’、y’進行插值得到s’/z’、t’/z’,然後用s’/z’和t’/z’分別除以1/z’,就得到了s’和t’。

這就是爲什麼我們要保存1/z的原因。

2、Pixel Operation 像素操作


2.1消除遮擋面
根據zbuff  使用1/z來進行ztest,這樣就不用給zbuff初始化一個很大的值了。

2.2 Texture operation 紋理操作,也就是根據像素的紋理座標,查詢對應的紋理值
紋理座標的透視校正: http://blog.csdn.net/popy007/article/details/5556639

紋理採樣,雙線性紋理過濾:http://dev.gameres.com/Program/Visual/3D/Bilinear.htm

http://blog.csdn.net/i_dovelemon/article/details/27839279

三:相機的旋轉和移動

移動實際就是更改相機的eyePosition,旋轉更改其LookAt,需要配合點乘叉乘,其中要注意相機是使用的右手座標系,而我們的世界座標是使用的左手座標系,需要對應更改,這也是爲了符合unity的情況

 

經歷了上面的步驟,把要渲染的像素寫到framebuff中,再將framebuff渲染到屏幕上就完成了所有步驟(本程序簡單的生成了一個bitmap來作爲framebuff),這時你應該可以在屏幕上看到你所渲染的東西。如果看起來不太對,那麼進入debug吧。一開始也許會覺得調試這樣一個繪製流水線會無從下手,因爲其中任何一步的偏差都會使最後的渲染髮生錯誤。

請假了一些大神後找到了不錯的調試方法,下面給出一些tips:

1、調試數學庫,用筆算來驗證結果的正確性

2、調試流水線,可以建立一個簡單多邊形,比如一個quad,用筆算來驗證m、v、p三個矩陣的正確性。光柵化的階段由於要進行插值,計算量比較大,建議使用心算和單步跟蹤的方式來進行調試。

 

最後,再次感謝 https://blog.csdn.net/aceyan0718/article/details/51659381 大佬,我的代碼在其基礎上精簡和修改了一些,但大體思路還是跟其保持一致

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章