1.Unity渲染流水線——《Unity Shader入門精要》學習筆記

        首先膜拜本書的作者——大神candycat,雖然用了接近5年的Unity,但是在接觸了大神的博客後才發現,我之前會的東西都不過是九牛一毛╮(╯﹏╰)╭,革命尚未成功,同志還需努力ᕙ(⇀‸↼‶)ᕗ。從書中拆出部分相關概念方便以後隨時查閱:

    流水線:可以將一個複雜的流程拆分爲簡單的幾個步驟分別進行,由此可以提高單位時間的生產量;理想情況下,如果把一個非流水線系統分成n個流水線階段,且每個階段耗費時間相同的話,那麼會使整個系統得到n倍的速度提升。

    性能瓶頸:流水線系統中決定最後生產速度的是最慢的工序所需要的時間,即性能瓶頸。

    渲染流水線(渲染管線)

        目的:由一個三維場景出發,渲染(生成)一張二維圖像,即計算機需要從一些列的頂點數據、紋理等信息出發,把這些信息最終轉換成一張人眼可以看到的圖像。

        輸入:虛擬攝像機、光源、Shader、紋理等。

        工作所在模塊:CPU、GPU

        流程應用階段(Application Stage)、幾何階段(Geometry Stage)、光柵化階段(Rasterizer Stage);應用階段在CPU中進行,因此開發者擁有絕對控制權,爲了提高渲染性能而進行的遮罩剔除工作也是在此階段進行,最終輸出渲染圖元(點、線、三角面等)給幾何階段;幾何階段在GPU中進行,在此階段會把頂點座標變換到屏幕空間中,最終將屏幕空間的二維頂點座標、每個頂點對應的深度值、着色等信息傳遞給光柵化階段;光柵化階段在GPU中進行,在此階段會將從上一階段獲取到的信息進行插值運算再進行逐像素處理,此階段決定每個渲染圖元中最終能繪製在屏幕上的像素。        

    Shader:即着色器,屬於GPU流水線上一些可高度編程的階段,由Shader編譯出來的最終代碼運行在GPU上(對於固定管線的渲染來說,Shader有時等同於一些特定的渲染設置);

        類型:頂點着色器(Vertex Shader)、片元着色器(Frag們談Shader)等

        功能:可以通過着色器控制流水線中的渲染細節,例如用頂點着色器進行頂點變換、傳遞數據,用片元着色器進行逐像素的渲染。

    應用階段流水線

        1.把數據加載到顯存中:因爲顯卡對顯存訪問速度快且不存在訪問權限問題,故數據(頂點信息、法線、顏色、紋理、座標等)都需從硬盤中加載到系統內存,然後加載到顯存中,加載到顯存後內存中用不到的數據就可以移除了。

        2.設置渲染狀態:定義了場景中網格的狀態(着色器類型、光源屬性、材質等)。

        3.調用Draw Call:DC(Draw Call)是由CPU發起,由GPU執行的指令,這個指令僅僅指向一個需要被渲染的圖元列表,而不會再包含任何材質信息。當給定一個DC時,GPU就會根據給定的圖元列表按照GPU流水線進行計算,最終輸出形成屏幕上的像素。

    GPU流水線(幾何階段、光柵化階段)

        

      幾何階段流水線

        頂點着色器(Vertex Shader):完全可編程,通常用於實現頂點間的空間變換(對頂點座標進行某種變換,可以用於製作頂點動畫,比如模擬水面、布料)、頂點着色等功能。數據來自CPU,處理單位是頂點,頂點着色器本身不可創建或銷燬任何頂點,也無法得到頂點之間的關係,輸入進來的每個頂點都會調用一次頂點着色器。頂點着色器必須完成的一個工作是把頂點座標從模型空間轉換到齊次裁剪空間(常見的處理函數o.pos=mul(UNITY_MVP,v.position);),接着通常再由硬件做透視除法後,最終得到歸一化的設備座標(NDC)。最常見的輸出路徑是經光柵化後交給片元着色器進行處理,還可以把數據發送給曲面細分着色器或者幾何着色器。

        曲面細分着色器(Tessellation Shader):可選着色器,用於細分圖元。

        幾何着色器(Geometry Shader):可選着色器,可用於執行逐圖元的着色操作,或用於產生更多圖元。

        裁剪(Clipping):可配置階段,不可編程,用於將那些不在攝像機視野內的頂點裁減掉,並剔除某些三角圖元的面片。在此階段可以使用自定義的裁剪平面來配置裁剪區域,也可以通過指令控制裁剪三角圖元的正面還是背面。

        屏幕映射(Screen Mapping):不可配置和編程,此階段負責把每個圖元座標從三維座標系轉換到屏幕座標系中。屏幕座標系和z座標一期構成窗口座標系(Window Coordinates);屏幕映射得到的屏幕座標決定了這個點點對應屏幕上哪個像素以及距離這個像素多遠。OpenGL把屏幕的左下角當做原點,DirectX把屏幕左上角當做原點。

      光柵化階段流水線(最主要目標—計算每個圖元覆蓋哪些像素,併爲這些像素計算它們的顏色)

        三角形設置(Triangle Setup):固定函數階段,不可編程和配置。此階段會計算光柵化一個三角網格所需的信息,即計算三角網格每條邊(邊界)上的像素座標。

        三角形遍歷(Triangle Traversal,也稱爲 掃描變換,Scan Conversion):固定函數階段,不可編程和配置。此階段將會檢查每個像素是否被一個三角網格所覆蓋,若被覆蓋則會生成一個片元(Fragment,並不是真正意義上的像素,而是包含了很多狀態的集合,這些狀態用於計算每個像素的最終顏色,這些狀態包括但不限於它的屏幕座標、深度信息、頂點信息等)。然後使用三角網格3個頂點的頂點信息對整個覆蓋區域的像素進行插值,得到各像素的位置(尤其是可以得到深度)。

        片元着色器(Fragment Shader):完全可編程,用於實現逐片元的着色操作。在DirectX中,片元着色器被稱爲像素着色器(Pixel Shader),雖然此時的片元並不是真正意義上的像素。片元着色器的輸入時三角形遍歷對頂點信息插值得到的結果,輸出的是一個或多個顏色值。此階段可以完成很多重要的渲染技術,其中最重要之一是紋理採樣。爲了在片元着色器中進行紋理採樣,我們通常會在頂點着色器階段輸出每個頂點對應的紋理座標,然後經過光柵化階段對三角網格的3個頂點對應的紋理座標進行插值後就可以得到其覆蓋的片元的紋理座標了。片元着色器的侷限性在於它僅能影響單個片元,除此之外它僅能訪問到導數信息(Gradient或者說是Derivative)

        逐片元操作(Per-Fragment Operations):不可編程,但是具有很高的可配置性(即我們可以設置每一步的操作細節),負責執行很多重要操作如修改顏色、深度緩衝、進行混合等。在DirectX中此階段被稱爲輸出合併階段(Output-Merger)。此階段的主要任務是

            1.決定每個片元的可見性。此任務涉及很多測試工作,如深度測試、模板測試等。

            2.如果一個片元通過了所有的測試,就需要把這個片元的顏色值和已經存儲在顏色緩衝區中的顏色進行合併(混合)。

   模版測試(Stencil Test),具有很高的可配置性,通常用於限制渲染的區域,還可用於渲染陰影、輪廓渲染等,步驟如下:

        1.若開啓模板測試則繼續進行。

        2.GPU使用讀取掩碼讀取模版緩衝區中該片元位置的模板值,然後將該值與使用讀取掩碼讀取到的參考值(Reference Value)進行比較,此比較函數可以由開發者指定。

        3.若該片元未通過模板測試,該片元就會被捨棄。

        4.不管測試是否通過,都可以根據測試結果和深度測試結果來修改模板緩衝區,此修改操作也可由開發者指定。

    深度測試(Depth Test),具有很高的可配置性,步驟如下:

        1.若開啓深度測試則繼續進行。

        2.GPU讀取該片元的深度值與已經存在於深度緩衝區中的深度值進行比較。此比較函數也可由開發者設置,通常這個比較函數是小於等於的關係,即若這個片元的深度值大於當前深度緩衝區中的值,那麼就會捨棄它,用以實現只顯示離攝像機最近的物體的功能。

        3.若該片元未通過深度測試,該片元就會被捨棄。

        4.若未通過測試則改片元就沒有權利更改深度緩衝區中的值,否則開發者可以指定是否要用這個片元的深度值覆蓋掉原有的深度值,修改深度值需要通過開啓/關閉深度寫入來進行。

    合併:當片元通過所有測試而沒有被捨棄後進入合併階段,在此階段可以解決本次渲染是覆蓋之前顏色緩衝中的結果還是對之前結果進行其他處理;對於不透明物體,開發者可以關閉混合(Blend)操作。這樣片元着色器計算得到的顏色值就會直接覆蓋掉顏色緩衝區中的像素值;對於半透明物體,我們就需要使用混合操作來讓這個物體看起來是透明的。

    混合(Blend):具有很高的可配置性,開發者可以選擇開啓/關閉混合功能。若未開啓混合功能,就會直接使用片元的顏色覆蓋掉顏色緩衝區中的顏色;若開啓了混合功能,GPU會取出顏色緩衝區的顏色和片元的顏色,將兩種顏色進行混合。之後會使用一個混合函數來進行混合操作。這個混合函數通常和透明通道息息相關,例如根據透明通道的值進行相加、相減、相乘等。

    Early-Z技術:將深度測試在片元着色器之前執行的技術,可以提高GPU的性能,Unity中即採用此技術。使用此技術可能會與片元着色器中的一些操作衝突,例如在片元着色器進行透明度測試,但是此片元沒有通過,我們會在着色器中調用API將其手動捨棄,折舊導致GPU無法提前執行各種測試。因此現代的GPU會判斷片元着色器中的操作是否會與提前測試發生衝突,若存在衝突就會禁用提前測試,這將導致性能上的下降,因爲有更多片元需要被處理了,這也是透明度測試會導致性能下降的原因。

    我們的屏幕最終顯示的就是顏色緩衝區中的顏色值,但是爲了避免我們看到那些正在進行光柵化的圖元,GPU會使用雙重緩衝(Double Buffering)的策略。即對場景的渲染是在幕後發生的,即後置緩衝(Back Buffer)中,一旦場景已經被渲染到後置緩衝中,GPU就會交換後置緩衝和前置緩衝(Front Buffer)中的內容,二前置緩衝去是之前顯示在屏幕上的圖像。由此保證了我們看到的圖像是連續的。

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