第3章-圖形處理單元-3.8-像素着色器

3.8 像素着色器

在頂點、曲面細分和幾何着色器執行它們的操作後,圖元被裁剪並設置爲光柵化,如前一章所述。管線的這一部分在其處理步驟中相對固定,即不可編程但有些可配置。遍歷每個三角形以確定它覆蓋哪些像素。光柵化器還可以粗略計算三角形覆蓋每個像素的單元格區域(第5.4.2節)。與三角形部分或完全重疊的像素區域稱爲片元。

三角形頂點處的值,包括z緩衝區中使用的z值,在三角形表面爲每個像素進行插值。這些值被傳遞給像素着色器,然後像素着色器處理片元。在OpenGL中,像素着色器被稱爲片元着色器,這可能是一個更好的名稱。我們在本書中使用“像素着色器”以保持一致性。沿管線發送的點和線圖元也會爲覆蓋的像素創建片元。

跨三角形執行的插值類型由像素着色器程序指定。通常我們使用透視校正插值,這樣像素表面位置之間的世界空間距離會隨着物體距離的縮小而增加。一個例子是渲染延伸到地*線的鐵軌。軌道距離越遠,鐵路枕木的間距就越*,因爲每個接*地*線的連續像素都行進了更多的距離。其他插值選項可用,例如屏幕空間插值,其中不考慮透視投影。DirectX 11 進一步控制何時以及如何執行插值[530]。

在編程方面,頂點着色器程序的輸出,在三角形(或線)上進行插值,有效地成爲像素着色器程序的輸入。隨着GPU的發展,其他輸入也暴露出來了。例如,片元的屏幕位置可用於着色器模型3.0及更高版本中的像素着色器。此外,三角形的哪一邊可見是輸入標誌。這一點對於在單個通道中,三角形的正面和背面渲染不同的材質很重要。

有了輸入,像素着色器通常會計算並輸出片元的顏色。它還可能產生不透明度值並可選擇修改其z深度。在合併階段,這些值用於修改存儲在像素中的內容。光柵化階段生成的深度值也可以通過像素着色器進行修改。模板緩衝區值通常不可修改,而是傳遞到合併階段。DirectX 11.3允許着色器更改此值。在SM 4.0[175]中,霧計算和alpha測試等操作已從合併操作轉變爲像素着色器計算。

像素着色器還具有丟棄傳入片元的獨特能力,即不生成輸出。圖3.14顯示瞭如何使用片元丟棄的一個示例。裁剪*面功能曾經是固定功能管線中的可配置元素,後來在頂點着色器中指定。隨着片元丟棄可用,此功能可以在像素着色器中以任何所需的方式實現,例如決定裁剪體的並和或操作。

Figure3.14

圖3.14. 用戶定義的剪裁*面。在左側,單個水*剪切*面對對象進行切片。在中間,嵌套的球體被三個*面裁剪。在右側,球體的表面僅在它們位於所有三個剪裁*面之外時纔會被剪裁。(來自Three.js示例webgl裁剪和webgl裁剪交集[218]。)

最初,像素着色器只能輸出到合併階段,以供最終顯示。像素着色器可以執行的指令數量隨着時間的推移而顯着增加。這種增加產生了多渲染目標 (MRT) 的想法。不是將像素着色器程序的結果僅發送到顏色和z緩衝區,而是可以爲每個片元生成多組值並將其保存到不同的緩衝區,每個緩衝區稱爲渲染目標。渲染目標通常具有相同的x和y維度;一些API允許不同的大小,但渲染區域將是其中最小的。某些架構要求渲染目標具有相同的位深度,甚至可能具有相同的數據格式。根據GPU的不同,可用的渲染目標數量爲四個或八個。

即使有這些限制,多渲染目標 (MRT) 功能仍然是更有效地執行渲染算法的有力助手。單個渲染通道可以在一個目標中生成彩色圖像,在另一個目標中生成對象標識符,在第三個中生成世界空間距離。這種能力還產生了一種不同類型的渲染管管線,稱爲延遲着色,其中可見性和着色在單獨的通道中完成。第一個通道存儲在每個像素處有關對象位置和材質的數據。接下來的通道可以有效地應用照明和其他效果。此類渲染方法在第20.1節中描述。

像素着色器的侷限性在於它通常只能在交給它的片元位置寫入渲染目標,而不能從相鄰像素讀取當前結果。也就是說,當像素着色器程序執行時,它不能將其輸出直接發送到相鄰像素,也不能訪問其他人最*的更改。相反,它計算的結果隻影響它自己的像素。然而,這種限制並不像聽起來那麼嚴重。在一個通道中創建的輸出圖像可以讓像素着色器在以後的通道中訪問其任何數據。可以使用第12.1節中描述的圖像處理技術處理相鄰像素。

像素着色器無法知道或影響相鄰像素結果的規則也有例外。一是像素着色器可以在計算梯度或導數信息期間立即訪問相鄰片段的信息(儘管是間接的)。像素着色器提供了任何內插值沿x和y屏幕軸每個像素的變化量。這些值對於各種計算和紋理尋址很有用。這些梯度對於諸如紋理過濾(第6.2.2節)之類的操作特別重要,其過濾插值需要我們知道圖像覆蓋了多少像素。所有現代GPU通過以\(2×2\)爲一組處理片元(稱爲四邊形)來實現此功能。當像素着色器請求梯度值時,返回相鄰片段之間的差異。參見圖3.15。統一着色器核心具有訪問相鄰數據的能力——保存在同一warp的不同線程中——因此可以計算用於像素着色器的梯度。這種實現的一個結果是,在受動態流控制影響的着色器部分中無法訪問梯度信息(動態流控制指的是具有可變迭代次數的“if”語句或循環)。一組中的所有片元必須使用相同的指令集進行處理,以便所有四個像素的結果對於計算梯度都有意義。這是一個基本限制,即使在離線渲染系統中也存在[64]。

Figure3.15

圖3.15. 在左側,一個三角形被光柵化爲四邊形,一組\(2×2\)像素。用黑點標記的像素的梯度計算顯示在右側。對於四邊形中的四個像素位置中的每一個,都顯示了v的值。注意三個像素是如何沒有被三角形覆蓋的,但它們仍然由GPU處理,以便可以找到梯度。x和y屏幕方向的梯度是通過使用其兩個四邊形鄰居爲左下像素計算的。

DirectX 11引入了一種允許對任何位置進行寫訪問的緩衝區類型,即無序訪問視圖(UAV)。最初僅用於像素和計算着色器,對UAV的訪問擴展到DirectX 11.1 [146]中的所有着色器。OpenGL 4.3將此稱爲着色器存儲緩衝區對象 (SSBO)。這兩個名稱都以自己的方式描述。像素着色器以任意順序並行運行,並且該存儲緩衝區在它們之間共享。

通常需要某種機制來避免數據競爭條件(又名數據風險),其中兩個着色器程序都在“競爭”以影響相同的值,可能導致任意結果。例如,如果像素着色器的兩次調用試圖在大約同時添加到相同的檢索值,則可能會發生錯誤。兩者都會檢索原始值,都會在本地修改它,但是無論哪個調用最後寫入其結果都會消除另一個調用的貢獻——只會發生一個添加。GPU通過具有着色器可以訪問的專用原子單元來避免這個問題[530]。然而,原子操作意味着一些着色器可能會因爲等待訪問而停止,此時另一個着色器在讀取/修改/寫入相同的內存位置。

雖然原子可以避免數據風險,但許多算法需要特定的執行順序。例如,你可能希望在用紅色透明三角形覆蓋之前繪製一個更遠的透明藍色三角形,將紅色混合在藍色之上。一個像素可能有兩個像素着色器調用,每個三角形一個,以這樣一種方式執行,即紅色三角形的着色器在藍色的着色器之前完成。在標準管線中,片元結果被處理之前,會在合併階段進行排序。DirectX 11.3中引入了光柵化順序視圖(ROV)以強制執行順序。這些就像UAV一樣;它們可以由着色器以相同的方式讀取和寫入。關鍵區別在於ROV保證以正確的順序訪問數據。這大大增加了這些着色器可訪問緩衝區的有用性[327,328]。例如,ROV使像素着色器可以編寫自己的混合方法,因爲它可以直接訪問和寫入ROV中的任何位置,因此不需要合併階段[176]。代價是,如果檢測到無序訪問,像素着色器調用可能會停止,直到處理之前繪製的三角形。

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