標籤:
恢(zheng)複(shi)更(kai)新(shi)後的第一天。
唔…不如來說說圖形管線和著色器的事吧。趁著剛給別人講完。(說來還是一段慘痛的經曆…TAT)
?
(請自行腦補分割線)
?
著色器,也就是平時各種地方說的shader,是開發人員為了開發需求而為GPU(圖形處理器)編寫的一個小程式。GPU有著高度並行化的特點,但運算的能力就沒有CPU那麼厲害,無法完成較為複雜的運算。但同時,它往往擁有CPU無法企及的龐大的計算單元數量(Iris 540:48,GTX 980Ti : 2560),這些單元都可以獨立完成相關的計算,想想如果有幾百個這樣的單元在同時計算,計算的效率勢必飛漲。不過這樣的計算所能完成的工作也是有限的,但是由於圖形計算高度的分離性,這種經過特殊設計的硬體成為了"大殺器"。如果把CPU比成一組學識淵博的教授的話,GPU可能更像一操場的中學生 —— 但應付常見的圖形計算,老教授們淵博的學識…並沒有什麼卯月。
我們可以為這樣的硬體編寫短小的程式,也就是shader。這些程式一般進行的是純粹的數學計算和簡單的邏輯處理,所以通常行數不會超過三位元,但行行是精華,可能某一行的一個算式就是幾十年的研究結果。
Unity中支援好幾種不同類型的shader,它們被稱之為"Surface shader"、"Unlit shader"一類。Unlit shader是其中最"本質"性的一個,它和OpenGL/DirectX/…這類底層圖形API所使用的shader沒有什麼本質性的區別。
(Unity中shader支援的語言是Cg和HLSL)
不想在這裡涉及太多具體實現的細節。那麼就讓我們開始吧,從冷冰冰的資料如何變成螢幕上的一幀幀映像開始。
(一)圖形渲染管線
(簡化了的圖形渲染管線模型)
"圖形渲染管線"(Graphics rendering pipeline)指的是從資料被輸入GPU開始,到最後渲染成為映像所經曆的一系列有序過程的總稱。
但首先,我們要能夠告訴GPU,我們要畫些什麼在螢幕上。現在,人們採用頂點+面的方式來描述。我們把一個個複雜的幾何體表示為一組面的集合,就像這樣:
儘管這樣不能完美的表示一個曲面,但對於騙過我們自己的眼睛來講這足夠了。這個茶壺成為了被一個個四邊形包圍起來的物體,如果我們存下這些四邊形的頂點資訊,我們就能夠根據這些資訊畫出一個一模一樣的"茶壺"。
由於空間中三點確定一個平面,我們不使用四邊形,而使用三角形來"包圍"一個物體。有了它們各個頂點的資料,我們就可以根據這些資料去繪製我們需要的圖形。這些資料被傳入GPU,再由GPU進行計算,變成映像呈現給我們。在這些資料中,大多數情況下不單包含頂點的位置資訊,往往還有頂點的法向量("朝向",這在計算光照時十分有用。單有位置不能判斷出某個頂點的朝向,進而無法判斷出這個面上某個點對應螢幕上的像素究竟該不該被照向某個方向的光打亮),紋理座標(用來給幾何體貼圖,詳見後續)等。
?
有了這些資訊,我們需要去把它計算成對應於螢幕上的座標。在這裡需要用到空間上的變換,把一個個座標點換成相對於眼睛位置、朝向的座標,並經過透視變換,變成對應於螢幕的座標點。
(物體的變換過程,注意透視變換產生的"展開")
?
在這之後,我們把這些點組裝成一個個面,再計算出每個面對應於螢幕上的那些像素。然後根據傳入的頂點資料給這些像素分配顏色,最終呈現在螢幕上(或儲存在映像中)。一般情況下,這一整套流程要在一秒鐘內執行60次以上 —— 通常有萬計的頂點;這便是GPU高度並行化可怕的計算效率。
?
(二)頂點著色器(Vertex shader)
在資料被傳入後,經過簡單的處理,第一個階段(stage)便是頂點著色器。上文提到"著色器"即shader,是我們為GPU編寫的獨立的小程式。它分為幾種不同的類型,分別用於整個渲染流程的不同階段。在這一階段,我們這一段小程式的目的是把輸入的頂點位置轉化為相對於螢幕的頂點位置(經過透視變換),並可處理資料向後續階段傳遞 —— 我們可以很輕鬆的在後續階段的shader中拿到之前階段shader產生的資料。
圖形渲染管線與shader(更新中)