如何開始用 C++ 寫一個光柵化渲染器?
題主有一些基本的編程技能(.c# ),但是對內存和底層的知識並不熟悉,其餘還有一些基本的圖形學知識,也遠遠沒有到GPU架構的那一層次,所以想利用業餘時間用C++做一個光柵渲染器,可能會花很多時間,但願意一點一點來,所以提問一下各位大神
1 在OSX 玩的不是很熟的情況下,是不是只能在windows的編程環境下去做這個事情?2 有哪些必看的書籍推薦?3 從哪裡開始著手?
瀉藥。我是 @偉大的春春 中提到的光柵化軟體渲染器SALVIA的作者。
下面是小廣告時間:
SALVIA的主頁: https://code.google.com/p/softart/這篇文章大概可以算是SALVIA從07年開始做到12年的一個整體回顧
開源光柵化渲染器SALVIA的漫長五年(准·乾貨)下面是針對你的情況做的一些補充:
- 首先你說你只有C#的基礎,對C++這種需要手工管理資源的方式不是很適應。所以首先你需要閱讀C++方面的書籍,尤其是C++11方面的內容,這會對你的開發大有助益。
- 掌握OpenGL或者D3D API的用法。你必須要知道你做的東西是用來幹什麼的,是怎麼用的。
- 「光柵化渲染器」的關鍵,其實並不完全在於「光柵化」演算法本身,更重要的是圍繞著光柵化而做出來的一系列結構。此篇內容會給你一個很好的概念。當然出於簡單起見,DX9級別的管線要更加簡單。
- 在這整個結構中,實際上光柵化、插值以及Pixel的處理都相對簡單,難點反倒是在Primitive的生成(也就是三角形的生成、變換、裁切)。光柵化演算法可以使用教科書上的經典演算法,就是先把三角形按照水平線切割成上下兩個部分,再來按照斜率來計算水平像素的範圍。比如說下圖這樣。這是我當時在ADSK做Team sharing的時候所講的內容。
- 我當年的用的唯一「圖書」,是OpenGL 2.0 Specification。這一版的複雜度和DX9對應,既有提綱挈領的架構描述,也會精細到每個演算法,非常適合拿來開發。比OpenGL 2.0 Specification更好的材料當然也有,比如Direct3D 10 Functional Specification和D3D Reference Rast,但是這些都是在微軟的NDA協議保護下,是很難見到的。
- 關於平台,你可以考慮先把場景渲染成圖片。
- 如果說著手的話,頂點變換,基本的光柵化演算法,再加上把覆蓋到的像素以黑色,覆蓋不到的像素以白色的方式,繪製成一個圖片,這應該是最最基本的功能。在你的程序能將空間三角形能變成2D的圖像後,你就可以慢慢往裡面增加光照紋理等功能了。
@Milo Yip
@空明流轉 開發過高大上的SALVIA, @Yong He則提到了Larrabee。我來提供另一些觀點。
首先,如果從學習角度出發,不必一開始完全根據現時GPU的架構及概念,來用軟體複製一遍。現時的GPU主要是基於三角形光柵化及z-buffer。
如果我們從圖形學的歷史進程來學習,可以這樣做練習:
2D部分:- 光柵化2D點(就是在二維數組上畫點,了解色彩基本原理,並解決影像輸出問題)
- 光柵化2D直線(布雷森漢姆直線演演算法、吳小林直線演算法等)
- 2D直線的剪切演算法(見Line clipping)
- 光柵化2D三角形(scan conversion)。避免重複光柵化相鄰三角形邊界的像素(edge equation)。
- 光柵化簡單/複雜多邊形
3D部分:
- 把頂點從三維世界空間變換至二維屏幕空間,繪畫頂點(如銀河星係數據),操控攝像機旋轉模型。
- 在剪切空間進行3D直線的剪切演算法,把頂點連線(如各種三維正多面體)光柵化成wire frame模型
- 以多邊形來定義三維模型。使用畫家演算法來光柵化那些多邊形。
- 改為使用深度緩衝。
- 實現簡單的紋理映射,先做屏幕空間的插值,然後實現簡單的perspective-correct texture mapping。
- 實現簡單的頂點光照,使用頂點顏色插值實現Gouraud shading。
- 通過頂點法線插值,實現Phong shading。
- 實現其他貼圖技術,如mipmapping(也可試Summed area table)、bilinear/trilinear filtering、bump mapping、normal mapping、environment mapping等。
我們還可以光柵化一些GPU不支持的primitive,例如Blinn大神在1979年就直接光柵化透示正確的球體來渲染太空CG:
可以參考[1]的1-3章。
話說,我想過寫一本關於這些的"科普讀物",不過總覺得受眾太少……
[1] Blinn, Jim. Jim Blinn"s corner: notation, notation, notation. Morgan Kaufmann, 2003.這是我正在寫的路徑追蹤器,C++11/14實現,Linux環境(Ubuntu 16.04),寫了有4個月了。很喜歡圖形學,感覺特別有意思。
UncP/Giraffe,它的中文名字是長頸鹿,與Graphics有那麼一點諧音。
在寫光線追蹤器之前寫過一個很小的渲染器,很不滿意自己的實現所以開始寫Giraffe這個光線追蹤器。渲染器!=光線追蹤器,但是渲染器和光線追蹤器有很多共通的地方,雖然一個面向物體,一個面向像素。渲染器裡面的Z-Buffer在光線追蹤器里其實是不斷更新的距離。其餘的坐標系變換、光照、紋理映射、相交檢測是一樣的。光線追蹤器實現起來更加優雅但缺點是運行速度稍慢。
走了不少彎路,看了很多資料,嘗試了很久,現在完成了
- 多種表面的BSDF
- 全局光照
- 路徑追蹤
- 蒙特卡洛積分
- Russian Roulette
- 紋理(Solid,Procedural,Cellular)
- 反走樣
- Depth of field
- 光源(點光源,方向光,區域光,紋理光)
- 物體(平面,三角形,球,四稜柱,圓柱,圓盤)
- 加速數據結構(BVH)
- Giraffe光線追蹤語言
這個光線追蹤器我會一直寫下去因為圖 形 學 太 好 玩 了 !
別看我列了這麼多但是如果你想深入學習離線渲染的話我建議先去接觸各種演算法——PT,LT,BPT,MLT,PM,SPPM,Radiosity…
以下是光線追蹤器的部分效果圖。
下面三張是最近跑的。
這張是長頸鹿(雖然不怎麼像,花紋使用了Worley Noise)
我覺得圖形學代碼寫起來非常舒服,模塊間的耦合度非常低,尤其是當把基本框架搭起來之後,每次實現新的特性完全不需要進行模塊測試,直接跑就行(這只是個人感覺,僅供參考)。
推薦一些參考資料(只有英文資料):
smallpt: Global Illumination in 99 lines of C++,這是一個只有99行的光線追蹤器。Scratchapixel,這是一個圖形學網站,每章都有兩三百行的C++11編寫可以直接編譯運行的圖形學代碼,非常適合入門。
第一第二個比較簡單,耐心點慢慢來,如果有一定基礎了一定要去下面這個CMU的課程主頁,真的很棒(從渲染到光線追蹤再到動畫模擬)!
http://15462.courses.cs.cmu.edu/fall2015/
15年CMU的15-462,別去16年的,16年質量比較低。Computer Graphics
15年MIT的6.837。這兩個圖形學課程的主頁上有課程PPT,參考書,還有很多輔助代碼(空間幾何,圖片I/O等等)。GitHub - mmp/pbrt-v3: Source code for pbrt, the renderer described in the third edition of amp;quot;Physically Based Rendering: From Theory To Implementationamp;quot;, by Matt Pharr, Wenzel Jakob, and Greg Humphreys.,著名的開源渲染器,是書《Physically Based Rendering》的具體實現。
《Realistic Ray Tracing》,一本講光線追蹤具體實現的書,每章後面都有代碼,這本書的作者就是《Fundamentals of Computer Graphics》的作者。
《Real Time Rendering》,這本書可以作為一本百科參考書,可以在具體實現某個模塊時去閱讀一下相關章節。
路徑追蹤器的地址:Giraffe: Distributed Monte Carlo Path Tracer
另外這個路徑追蹤器不再更新,已升級為一個離線渲染器,地址在這:UncP/Zebra
感謝邀請,這個問題還是要回答一下。我假設你已經知道了圖形學的一些基本原理。
首先,看 Michael Abrash 的經典文章Rasterization on Larrabee
這裡講解了如何實現robust和高效的軟體光柵化。但光柵化渲染器除了光柵化,還有很多東西。首先需要熟悉Direct3D或者OpenGL API, 這樣你就大致知道要實現哪些功能了。如果不追求性能,總體上還是容易的:按照渲染管線一步一步實現即可。首先拿到場景輸入(格式參見OpenGL,也即一個vertex buffer, index buffer),設定好FrameBuffer,然後按照index buffer讀三角形,查找頂點,運行Vertex Shader,Vertex Shading的結果可以用一個cache緩存起來以復用。運行完Vertex Shader就有了投影后的三角形坐標,這個時候進行裁剪(裁剪是為了讓光柵化過程能高效地進行),然後進行光柵化。
光柵化後,對每個三角形所覆蓋的所有2x2 pixel block,你可以計算出這個像素中心對應的三角形的重心坐標,用重心坐標插值得到pixel shader的輸入,然後運行pixel shader。同時對2x2的pixel block進行pixel shading可以保證你能夠得到屏幕空間的導數,這樣就能用來做紋理過濾了。最後就是進行z-test並且把像素顏色寫進FrameBuffer。對所有三角形執行上述步驟就完成了基本的渲染流程。
值得一提的是rasterization renderer有很多的優化。例如hi-Z, pre-Z, tiled rendering等。在把基本的流程打通一遍之後,可以再寫一遍然後試圖實現所有的這些優化。
另,實現渲染器跟操作系統沒太大關係,不過為了成就感,還是建議在熟悉的系統下搞個窗口應用程序吧。因為你實現的好的話,渲染器是可以實時渲染一些場景的,會更有樂趣。題主說熟悉C#,我最近剛好寫了一個C#版的,雖然沒有各位大神的高大上,但是比較簡單易懂,也放上來給題主參考一下吧~
原文鏈接:用C#實現一個簡易的軟體光柵化渲染器
這是一個用C#+winform實現的軟體光柵化渲染器,今天拿出來與大家分享一下,希望能起到拋磚引玉的作用,給新人一點啟發(結構比較簡單,注釋比較詳細^_^),也歡迎司機們拍磚指點和交流~
目的:
鞏固圖形編程知識,理解渲染流水線所做的事情。
實現功能:
1、將頂點數據進行一系列處理顯示到屏幕上(廢話。。。(?_?))
2、線框渲染模式、紋理渲染模式、頂點色模式
3、紋理uv坐標和頂點色等的透視校正插值
4、紋理雙線性過濾採樣
5、背面消隱
6、Cvv簡單裁剪
7、「基礎光照模型」(相當於D3D、OpenGL中的固定管線頂點光照)
截圖:
光照
頂點色
線框模式:
代碼放在Github上面:GitHub - aceyan/SimpleSoftwareRenderer: 簡單的軟體渲染by c# winform
下面我會將一些完成這個渲染器所需要的知識點以及相關資料的鏈接(多是博文)羅列一下,方便查閱。
零、準備階段想要實現渲染器首先我們得知道「給定視點、三維物體、光源、照明模式,和紋理等元素,如何繪製一幅二維圖像」,這就必須提到一個詞「圖像繪製管線」(也稱繪製流水線),我們得渲染器正是要以軟體的形式來模擬這條流水線的運作。
流水線:(轉)GPU圖形繪製管線
理解了繪製管線,我們發現其中充滿對矩陣向量等數學工具的運用,那麼再實現流水線之前,我們必須先實現這些數學工具,這些數學類網上已經有很多的資料了,但是有幾點需要特別說明:
1、本程序中使用行矩陣與行向量相關資料:D3d和openGl矩陣區別
矩陣和向量的乘法順序
2、本程序使用左手坐標系3、實現了color類來方便進行顏色的運算,因為進行光照的時候要使用顏色乘以顏色的計算方法,它跟向量的乘法不同,應該稱為「Modulate(調製)」,注意不能用向量乘法的計算公式哦。相關資料:
在 Cg 的逐像素著色的最後,每一個顏色值相加和每一個顏色相乘從數學角度有什麼差別? - Milo Yip 的回答
@Milo Yip 大神的博文裡面有一段講顏色計算的:
用JavaScript玩轉計算機圖形學(一)光線追蹤入門
4、矩陣求逆由於要進行光照,我們需要變換頂點法線,那麼就需要矩陣求逆和求轉置的運算。(為什麼需要逆轉置呢?請看:cg語言漫反射光照模型中的worldMatrix_IT是什麼意思?是世界變換矩陣的轉置的逆? - 遊戲開發)
其中矩陣求逆比較複雜,本程序使用的是伴隨矩陣的方法求矩陣的逆, 必須先求出矩陣的行列式和伴隨矩陣,這部分需要一定的線性代數基礎知識。
4.1矩陣求逆重要定理:逆矩陣的幾種求法與解析(很全很經典)_百度文庫
4.2求矩陣行列式本程序使用遞歸演算法求行列式,請查看矩陣行列式的遞歸定義:
3行列式(遞歸定義)_圖文
4.3伴隨矩陣:伴隨矩陣_百度文庫
實現了數學類,就可以正式進入流水線了:
一、幾何階段1、頂點從模型空間-----&>世界空間這一步要生成世界矩陣(簡稱m),m是一系列平移、選擇等變換的組合
如果開啟了光照,我們還需要把模型空間的法線信息變換到世界空間,對頂點進行光照計算並保存光照結果顏色,以便在光柵化的時候進行差值和顏色調製(Modulate)
這裡實現的「簡單光照模型」可以參見《Cg教程_可編程實時圖形權威指南》第五章光照
2、世界空間-----&>相機空間這一步需要生成視矩陣(簡稱v)
推導過程:@zdd的博客 View Transform(視圖變換)詳解
上面的推導中
寫錯了
應該是
rx ry rz
[ ux uy uz ]
dx dy dz
但是推導結果是對的
3、相機空間---&>齊次剪裁空間這裡要生成投影矩陣,簡稱p
Dx風格的投影矩陣推導:
透視投影詳解
本程序使用的投影矩陣:
視空間的頂點乘以這個矩陣之後被變換到齊次剪裁空間,並且w分量保存著視空間的z信息。
在齊次剪裁空間我們可以對頂點進行簡單的裁剪,既將不在
-w &<= x &<= w
-w&<= y &<= w
0 &<= z &<= w
這個範圍的頂點剔除掉,不進行渲染。
這裡採用的是最簡單粗暴的剪裁方法,更加複雜的剪裁還需要生成新的頂點,有很多可以深入挖掘的地方:)
Ps:為了方便後續的透視校正插值,程序中還將1/z 保存在頂點數據中。
4、進行圖元裝配,也就是將頂點以一定的順序組裝成三角形(Primitive Assembly Trianglesetup )本程序中使用了頂點索引的方式來組織頂點數據,並且約定以逆時針順序組織的三角形的法線朝向屏幕外,換句話說就是逆時針順序組織的三角形看起來是正面。
有了這個約定就可以進行背面消隱:
從零實現3D圖像引擎:(14)背面消隱的三大陷阱
5、透視除法對透視變換得到的含有深度信息(z)的齊次坐標做透視除法。所謂透視除法,
就是把透視變換後的齊次坐標除以(z)。由於透視變換矩陣已經構造好了,當 視錐體內部點經透視處理後的齊次坐標除以(z)後,使得頂點進入-1&<= x &<= 1
-1&<= y &<= 1
0 &<= z &<= 1 (CVV正方體中)。
6、映射到視口將cvv正方體的頂點根據屏幕大小或者視口大小轉換為屏幕坐標
二、光柵化階段1、Rasterization光柵化光柵化決定哪些像素被幾何圖元覆蓋的過程
1.1 如果選擇線框模式,本程序使用的畫線方法是Bresenham快速畫直線演算法相關資料:Bresenham快速畫直線演算法
1.2頂點色模式和紋理模式三角形光柵化演算法:從零實現3D圖像引擎:(15)三角形的光柵化
掃描線填充演算法:從零實現3D圖像引擎:(2)畫2D直線不簡單
光柵化過程當中,我們要對uv坐標,頂點顏色、我們保存在頂點信息中的1/z等信息進行透視校正插值:
深入探索透視紋理映射(下)
文章中的重要結論:
我們發現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 紋理操作,也就是根據像素的紋理坐標,查詢對應的紋理值紋理坐標的透視校正: 深入探索透視紋理映射(上)
紋理採樣,雙線性紋理過濾:紋理映射的雙線性插值濾波
DirectX (9) 紋理映射
2.3 Blending本程序暫時沒有對alpha的處理,後面應該會更新
2.4:Filtering相當於後處理,就是對即將寫入framebuff(幀緩衝)的像素進行一些處理,本程序暫時沒有添加。
經歷了上面的步驟,把要渲染的像素寫到framebuff中,再將framebuff渲染到屏幕上就完成了所有步驟(本程序簡單的生成了一個bitmap來作為framebuff),這時你應該可以在屏幕上看到你所渲染的東西。如果看起來不太對,那麼進入debug吧。一開始也許會覺得調試這樣一個繪製流水線會無從下手,因為其中任何一步的偏差都會使最後的渲染髮生錯誤。
請假了一些大神後找到了不錯的調試方法,下面給出一些tips:
1、調試數學庫,用筆算來驗證結果的正確性
2、調試流水線,可以建立一個簡單多邊形,比如一個quad,用筆算來驗證m、v、p三個矩陣的正確性。光柵化的階段由於要進行插值,計算量比較大,建議使用心算和單步跟蹤的方式來進行調試。
筆算的過程能讓你更加了解背後的數學奧秘:)
最後要感謝一下@cppyin,@zdd, @Milo Yip , @韋易笑 等大神的文章,給我很大幫助。
特別要推薦 @韋易笑 老師寫的mini3D:Skywind Inside,讓我受益匪淺。
教材的話推薦《3D遊戲編程大師技巧》,優點是從零開始構建,缺點(見仁見智)是用的是 C + Windows + DirectDraw(非常古老)
渲染器本身其實是平台無關、語言無關、API 無關的,作為起步階段的渲染器,你所需要的僅僅是- 輸入一組三角形的數組,vector&
scene; - 輸出一個二維數組, char buffer[800][600][3];
- 如果你用 javascript + html5 來做,可以直接寫到 canvas 上
- 在 Windows 上實時繪製結果可以用 GDI,也可以用 DirectDraw(參考推薦的教材),也可以用 OpenGL(僅僅繪製像素,不用三維加速),也可以用 WPF,設置可以用控制台界面(即 cmd.exe)
- 如果你沒有一個可視化的環境,那麼可以將二維數組寫到文件中以驗證結果,例如 saveImage(buffer, "buffer.bmp"),當然啦,saveImage 需要你自己來實現
想實現光線跟蹤渲染器的還可以看這個
smallpt: Global Illumination in 99 lines of C++「用c++做一個光柵化渲染器」?這事我剛剛做了!要不題主您受累過去看一眼?:)
我不知道題主這個問題是什麼時間開的?(知乎頁面上好像不顯示)如果你還對這事感興趣,或者還是弄,沒弄完,那麼不如來我這看看,一起切磋~
上面那些大神的回答都非常有理論指導意義,我弄得這個玩意可能有一點點實踐方面的參考價值?
GitHub - CallMeZhou/Puresoft3D他們都有漂亮的截圖,我也有~ 喜歡的話,賞個星星唄~做了個一個月玩具小項目從0 到軟渲 到遊戲引擎 到遊戲邏輯和美術的全過程,估計業餘時間做事沒問題的,軟渲部分只寫了兩個星期左右,所以只實現了一些原理性的功能。因為輸出是用 c艹控制台 ,所以畫面相對樣衰一點。在這裡我可以分享一點入門級的軟體渲染器的一點開發實踐經驗。接下來講得有點像科普= =...有那麼一點長,而且基本都是震撼靈魂引人深思的圖,慎點
--------------------------------------------------
現在主流兩種渲染方式就是光線跟蹤和光柵化了,現代大多數顯卡用的都是基於光柵化的渲染方式。所以我就模仿d3d9的風格去設計api,寫一條固定管線。之所以叫管線還是因為渲染3d物體的流程還是相對固定的,三維物體的頂點,網格,貼圖等數據要經過一系列的處理:
1,頂點的變換(至齊次裁剪空間),可以順便做個頂點光照
2,三角形的裁剪3,三角形的光柵化(矢量圖形變成像素)4,逐像素處理(如果做了頂點級光照就不做像素級光照了)5,輸出到屏幕------------------
一、頂點變換一般情況下,頂點是由3dmax maya等建模軟體導出的,每個物體都有自己的參考系,首先輸入就是原始的頂點和索引數據。然後我們就要把一大堆的物體通過平移和旋轉合併到一個坐標系下面,世界坐標系,也可以看做拿地球作為參考系。然後成像這個過程呢,最起碼需要物體,燈光,和攝像機(人眼之類的光感受器)。
攝像機一般由多個參數來抽象,相機(鏡頭)固有屬性至少要有縱向視場角,屏幕(底片)長寬比,視截體的近平面和遠平面深度。相機還需要姿態描述,也就是相機的位置和傾斜旋轉程度。我選擇的是一個位移向量和yaw-pitch-roll姿態角(就是 搖頭拒絕-點頭贊成-側著頭賣萌 三個角度的量化描述)。這是要完成最基本的透視成像需要的屬性。 然後已經都在世界坐標系的頂點,要進一步轉換到相機坐標系,就是以觀察者為中心的參考系。相機坐標系下的坐標一般都叫做view space里的坐標。 上圖那一個奇怪東西是眼睛( ̄_, ̄ ),然後這麼轉換,是因為投影這個操作,簡直就可以用每個頂點的x y坐標直接除以z坐標啊得到成像屏的坐標啊多爽!
當然了,z坐標不能隨便丟棄,z坐標是要保存下來的因為後面要用到。投影之後,好吧其實嚴格來講不能叫投影因為投影會降維,我們的坐標的xyz分量要全部被映射到[-1,1]區間內,這很明顯是一個邊長為2的正方體,這個空間也叫「齊次裁剪空間」(homogeneous clipping space) 。然後這裡有一點小坑,就是國際慣例來說,view space的z坐標是會被映射到非線性的[0,1]區間內,也就是1/z從0到1線性增長,這樣的話z坐標(浮點數)在靠近觀察者的地方精度會更高。我是覺得有點奇怪的,話說到現在都還沒有想懂,一定就要把z坐標映射到一個非線性的空間嗎,所以我自己實現的時候,寫的perspectiveProjection投影透視矩陣就跟d3dx生成的不太一樣,但是xyz依舊是要映射到[-1,1]的區間內。(我寫的是把z映射到線性分布的[0,1]區間中)對了,在頂點處理階段可以進行頂點光照。因為曲面不可能都用方程來描述,所以一般都是用三角網格這麼離散地去近似描述一個曲面。為了讓人類「看起來」這物體還是挺光滑的,於是就有了Gouraud著色法,先在頂點處計算好顏色,然後再在三角形內對顏色這個頂點屬性進行插值來填充。光照需要有光源信息和表面的材質信息來交互,但是具體還是看看書唄這裡就不講了。。= =
---------------------------------------------
二、三角形裁剪人眼只能看到前面的東西,(p~~)眼是看不到東西的,所以經過映射之後,我們要只留下z&>0的三角形,就是上圖灰色部分。一開始我以為,這個裁剪估計只是因為要加速和優化吧,畢竟看不見的肯定就趕緊裁掉。本來我是直接略過這個步驟的,後來發現好像完全不只是因為優化,不做這一步會產生錯誤的效果,特別是當三角形的三個頂點有些z小於0有些z大於0,會產生詭異的現象(然而我沒有定量地去分析這個的問題),例如三角形橫跨整個屏幕等等...總之呢,其實做這一步就要用到三角形和長方體的求交,而且求交完得到的多邊形要求三角化(對的,三角形和立方體求交可能會截出四邊形,但是必須三角化,以求圖元形式的統一)。其實這個三角形和正方形求交情況有點多,所以我因為項目需求不多就從簡,直接暴力地把三個頂點全部在範圍以外的三角形給剔除掉
= =。 -----------------------------三、光柵化好了,到這裡,我們已經得到了一堆有可能會被看到的三角形(之所以說是有可能,那是因為三角形間可以被互相阻擋),而且他們的xy坐標都被映射到[-1,1],z是[0,1]。現在還是矢量圖形,要把矢量圖形離散化成點陣,因為是寫軟體渲染,所以直接一個二維數組就當是圖片了。光柵化我也有點閉門造車的感覺,只知道有個方法叫「掃描線」法,我就按著這個名字實現了一個光柵化的演算法= =其實有點像是密閉圖形的填充演算法
首先,把齊次裁剪空間的坐標都換成像素坐標。於是現在我們一行一行像素來處理。現在先不光柵化,而是直線和三角形邊框求交。這樣必定可以交的0/1/2個頂點,如果可以求交到2個頂點,那麼我們在這一行就有了一個x坐標的區間(x1,x2),簡單點處理就把這一行的從[x1]到[x2]的像素全部當成三角形內部的像素([]為向下取整),於是掃描線上三角形的最上方掃到最下方,這樣整個三角形就被完全離散化為像素點了。當然橫線和三角形的求交,也是要分類討論和處理一些特殊情況的,這個就不展開講了。
到這裡,我們有了需要一堆被處理的像素以及他們的像素坐標,然後我們要逐像素求出他們的雙線性插值坐標(s,t),其中在三角形內,s+t≤1。
三角形的三個頂點像素坐標是我們知道的,所以只需要解二元一次方程即可解出(s,t)。然後就能用這個比例反過來對其他頂點屬性(如顏色,紋理坐標等)進行插值。但是注意,反過來插值不可以用普通的雙線性插值,
那是給2d圖形或者全平面深度一樣的三角形用的插值方法,3d渲染要做關於z坐標(深度)的補償插值,也叫透視校正插值(perspective correct interpolation)。為什麼要這麼干呢?因為表面的信息在三維空間中是均勻的話,投影到平面之後,逐像素看就不一定均勻了,因為有「遠大近小」的透視效應存在。
這個是沒校正的,直接對三角形進行雙線性插值,可以看到紋理有種奇怪的感覺是吧
(圖侵刪)
這個就要用雙線性插值公式來校正:(每個頂點屬性都可套用這公式,這裡的c1,c2是插值坐標)注意現在得到的一堆像素,不止有顏色信息,還有可能會有插值過的法線信息,紋理坐標信息等頂點屬性。
-------------
四、逐像素處理也就是pixel shader乾的事。我自己寫的這條管線選擇了使用頂點光照,於是就不做逐像素光照了。 用紋理坐標在圖片里對應位置採樣得顏色信息,然後和之前光照得的顏色進行混合,於是就實現了紋理貼圖。因為這個小項目需求不多,所以大概做到這些原理性的特性就沒了,題主大可一試。
可以參考一下我的源碼:GitHub - CHINA-JIGE/Shoot-The-Chicken-3D: 基於c++控制台的3d第一人稱射擊遊戲,包括軟體渲染器,遊戲引擎層,遊戲邏輯層首先感謝題主和各位答主,借著這個問題引發的興趣,再根據各位答主提供的資料,我也寫出了自己的軟體光柵化渲染器。
先放渲染截圖:從截圖可以看到的功能有:
*模型坐標轉換*紋理渲染模式*線框渲染模式*單個點光源實際上,上面說的這些其實就是我這個渲染器的所有功能了(大霧)。特地說明一下,我這個渲染器不支持交互,跑起來只能渲一張靜態圖,然後就退出了。
本來看了 @空明流轉 的答案,計劃是搞個和SALVIA類似的大工程,寫著寫著發現力不從心,然後就砍呀砍呀成了現在這個樣子。不過我的整個渲染器只有一個cpp文件喲,加空行加註釋一共也不到500行代碼。不能做大,就往小里做,條條大路通北京哈哈。回到主題,題主是問從哪裡開始著手,我就說一下我的開發過程。
1. 首先把本問題下所有答案都讀一遍,再讀一遍,讀三遍。2. @Milo Yip 的回答里提到了很多技術要點,查wiki查google進行深入了解。3. 然後發現被細節淹沒了,大流程還沒有概念,於是參考opengl的教學及手冊,了解大流程。4. 該知道的都知道了,把程序實現出來。5. 上知乎回答問題。大致的程序流程如下:創建renderer
創建frame/depth(z) buffer
設置攝像機 // projection/view matrix
創建model
創建vertex/index buffer // obj模型文件讀入
創建texture // bmp文件讀入
渲染model
遍歷model的所有三角形,對於每個三角形
對三個頂點分別調用vertex shader // 坐標系轉換
剔除測試 // backface culling
光柵化三角形
計算三角形的包圍盒,對於包圍盒內的每個像素 // triangle bounding box
像素測試 // triangle edge function
插值 // perspective correct intepolation
depth/z buffer測試
調用pixel shader // blinn-phong shading
frame/depth寫入 // texture bilinear filtering
線框渲染模式
2d畫線演算法 // bresenham"s line algorithm
frame/depth寫入
frame buffer導出為圖片 // bmp寫入
最後必須放上github啊,求提issue,各種corner case自己發現不了,渲染有錯自己也發現不了,大家多多關照。
GitHub - jintiao/cobra: software rasterizer in one file另外安利下這個教學網站 Scratchapixel ,我從裡面學到了好多。看PBRT先學基礎的數學和光學知識,然後你只要能夠用二進位拼一個bmp文件看結果就好了,完全跟操作系統沒關係,主要取決於你什麼工具用得熟。
我倒是很久沒有重點搞渲染器了,只有 @Yong He 和 @空明流轉 這兩位菊苣還奮鬥在GPU的一線工作中,值得大家學習。
一年前看到這個問題的時候,就想自己寫一個,可惜一直太懶沒有動手。兩個月前終於下定決心開始寫,斷斷續續寫到現在,目前勉強可以渲染一些東西。
語言選擇了JavaScript,所以速度感人(Chrome下比IE或Edge下要快很多,不知道為啥)。
在線運行:JS Rasterizer
源碼:delphifirst/js-rasterizer目前想做但還沒有做的功能:透視修正視口裁剪紋理濾波處理三角形接縫背面剔除1. 平台無關
2. 書籍:書籍到是不少,但是要求C++,實在想不出來別的。《3D遊戲編程大師技巧》算一個。 本來想推其他的,一想到Basic不滿足題主要求就算了。
開源項目:SALVIA Renderer (DX10-like Software Renderer)SoftRenderer3. 有點線性代數基礎,C++基礎,windows基礎就可以直接上手。正在參照以上大神的指導寫自己的軟體渲染器。決定將過程更新到這裡,幫助一些有需要的童鞋。
平台:Windows Store app
已完成:
1. 光柵化線段 三角形 - Bresenham"s line algorithm - Introduction to Software-based Rendering: Simple Line Drawing - Software Rasterization Algorithms for Filling Triangles - Z-buffer2. 透視投影3D物體 - Book Introduction to 3D Game Programming With Directx 11 Chapter 53. 紋理4. 光照 (Gouraud Shading)截圖:
可以看到,邊角處有突出,因為精度計算還有些問題,後面解決待完成:1. 載入模型2. ...代碼:ibosong/SoftwareEngine · GitHub當年看的是這個項目:The Mesa 3D Graphics Library是一個opengl的開源實現,代碼很清晰,也比較容易懂。我記得它是很多平台上opengl的最初實現。當時看的時候還不是很複雜的項目,記得還沒有硬體加速的部分,或者我特地下了個老版本也說不定。覺得新版本太龐大也可以找老版本看看。書應該是看這個書的吧:計算機圖形學 (豆瓣)貌似還有本OpenGL的Spec先寫矩陣計算實現頂點計算,再做光照,按照OpenGL上給的公式實現光照;再實現紋理和線性插值。再把這些組合成流水線;然後就是個能跑Nehe 教程1-6課的小玩具了。代碼非常簡單,不超過5000行應該。https://code.google.com/p/softgl/只能在windows上跑。藉此感嘆下青春- -b
還是得先了解opengl或者dx,再去寫自己的吧。不過有人推薦光線跟蹤,我覺得這個和光柵化渲染器差別有點大啊。。。。
想用C++實現一個軟體渲染器,類似DX和OpenGL,除了《3D遊戲編程大師技巧》,或者什麼網站推薦? - 李雪峰的回答 - 知乎
我用C實現的,2000行代碼左右,支持多平台
個人認為,最經典的是《3D遊戲編程大師》,不賣弄,純乾貨,從零教你寫軟3D引擎,當然包括光柵化。我的觀點一直是,當你邁出第一步,所有的東西都會水到渠成。
說到光柵化渲染器,必須提一下gameKnife大神的SoftRenderer。我自己做圖形學課程設計時寫的一篇文章,也收集了不少資料,可以參考一下。How to write a simple software rasterizer
圖形學課程作業,qt界面,光柵處理器(預計1~2md)和光線跟蹤(預計5md):https://github.com/maxint/zbuffer-cpuhttps://github.com/maxint/raytracer-cpu
最近剛寫完的軟渲染blog: 圖形學雜談 - 想做遊戲嗎?照著blog上的代碼一步步做結果就是你想要的.ps:光柵化用重心坐標法計算,&<3D遊戲編程大師技巧&>里的掃描線演算法已經過時了github地址: GitHub - zxx43/Software-Render: 軟體渲染器最終效果:
推薦閱讀:
※大家都在勸退材化環生,那麼除了cs或金融還有好的出路嗎?
※是否每局Windows紙牌遊戲(Solitaire)都是可以被解決的?也就是說每局都可以贏。
※軟體崩潰後,提示發出的「發送錯誤報告」到底有沒有用?
※圖靈機與λ演算是等價的,為什麼前者成為了普遍接受的計算機或計算理論的模型?
※量子計算機的發展對傳統微電子行業是否有衝擊?