1. 程式人生 > >OpenGL著色器程式解析--曲面細分基礎

OpenGL著色器程式解析--曲面細分基礎

轉載自:https://blog.csdn.net/cordova/article/details/80899013

背景

曲面細分技術是OpenGL 4.x加入的一個令人振奮的新特性。曲面細分處理的核心是3d模型的細節和多邊形數量等靜態屬性,例如當我們觀察一個複雜的模型,像人的臉部,當靠近看時我們應該使用一個高度精細的模型來展示小的細節(例如面板表面的小突起等)。而高度精細的模型意味著需要更多的三角形來實現以及需要更強的計算能力來進行處理。同一個模型,在遠處時我們希望用一個低精度的模型來代替展示,以省下更多的計算資源用於處理靠近鏡頭的物體。也就是要權衡計算資源的分配,將更多的計算用於處理靠近相機的物體時起細節更加清晰顯著。

一種實現方法是使用OpenGL已有的一個特性,即生成同一個模型的多個不同細節級別的模型資料(LOD技術)。例如,可以分成高精度,平均精度和低精度三個級別。然後可以根據離相機的距離選擇不同精度的模型來渲染。但這樣會需要更多的美術資源而且控制靈活度很低。我們需要的是匯入一個較低精度的模型,然後自動將每個三角形繼續細分成多個更小的三角形,這就是曲面細分。OpenGL 4.x提供的曲面細分管線就可以實現在GPU上動態的細分三角形並且可以選擇每個三角形的細分等級。

經過學術界和工業界多年的研究實踐後,曲面細分已經被定義和整合到了OpenGL的管線標準中,它的設計主要是受到數學背景、幾何表面和曲面理論、貝塞爾曲線和細分等理論的影響啟發。這裡我們主要分兩個階段來介紹曲面細分。這個教程中,我們主要關注為了實現曲面細分而用到的渲染管線的新機制,儘量避免涉及太多的數學知識。曲面細分技術本身是比較簡單的,只是它會牽扯到很多其他的技術和理論部分。下個教程我們會學習貝塞爾曲線及其在曲面細分中的應用。

現在先看一下曲面細分在圖形管線中是如何實現的。負責實現曲面細分的核心部分是兩個新的著色器階段,且在它們之間還有一個可以適當進行配置的固定功能的階段,但該階段不需要執行著色器。第一個著色階段叫做曲面細分控制著色(TCS,Tessellation Control Shader),固定功能階段叫做圖源生成(PG,Primitive Generator),第二個著色階段叫做曲面細分評估著色(TES,Tessellation Evaluation Shader)。下圖展示了幾個新階段在管線中所處的位置: 
http://ogldev.atspace.co.uk/www/tutorial30/pipeline.jpg

TCS作用於一組叫做控制點(CP,Control Points)的頂點組。控制點並不是被定義成像三角形、矩形、五邊形等多邊形形式,而是定義為一個幾何表面,這個表面通常由多項式來定義,而且移動其中一個控制點將會影響整個表面。這個通常在一些圖形軟體中,使用者可以通過移動一組控制點來隨意改變模型表面或者曲線形狀,一組控制點通常稱為一個Patch。下圖中的黃色表面就是通過一個16個控制點的patch來定義的: 
http://ogldev.atspace.co.uk/www/tutorial30/patch.jpg

TCS輸入一組patch並處理後輸出一組新的patch,開發者在shader中可以對控制點進行變換,也可以刪除或者新增控制點(類似於幾何著色器可以修改或增刪頂點)。另外,出了輸出patch,著色器還會計算輸出一組稱作曲面細分級別(Tessellation Levels,TL)的資料。TL決定了曲面細分的細節程度,即每組patch需要生成多少三角形。上面的操作都發生在著色器中,因此開發者可以使用任意的演算法來計算細分等級TL。例如,我們可以定義TL的值為3,如果光柵化三角形覆蓋的畫素數低於100,畫素數在101-500之間TL值為7,再多的TL的值就定義為12.5了(後面會介紹TL的值如何控制曲面細分的精細程度)。另外的演算法還有根據離相機的距離來計算細分程度的,都可以使得每組patch根據其自身的特點得到不同的TL值。

TCS結束後進入PG固定功能著色階段,進行真正的細分操作。這裡新手會很容易疑惑,PG並沒有真正的對TCS輸出的patch進行細分,事實上它甚至沒有訪問patch的許可權。相反,它根據TL的值在特定的空間中進行曲面細分,該空間可以是單位化的2維矩形或者是由三維質心座標定義的等邊三角形: 
http://ogldev.atspace.co.uk/www/tutorial30/domains.png

三角形的質心座標系是一個綜合三角形的三個頂點的權值來定義三角形內部位置的方法。三角形的頂點由U、V以及W三個分量確定。三角形中的某一個點的位置越靠近一個頂點,則這個頂點的權值就越大,相應的其他兩個頂點的權值就會減小。如果這個點正好位於一個頂點上,那麼對應這個頂點的權值為1,另外兩個頂點的權值都為0。例如質心座標系的U為(1,0,0),V為(0,1,0)、W為(0,0,1),此時三角形的中心用質心座標系表示就是(1/3,1/3,1/3)。質心座標系的一個有趣的特點是,如果將三角形內部一點的三個分量相加得到的結果將總是1。為了簡單,之後我們將專注於三角形空間。

PG根據TL的值在三角形內部生成一系列的點,每個點都是由這個三角形的質心座標系確定的。開發者可以選擇輸出的拓撲結構為點或者是三角形。如果選擇的是拓撲關係為點,那麼PG會直接將其傳入渲染管線的下一階段並按照點來進行光柵化。如果選擇的是三角形,PG會將所有頂點連起來這樣整個三角面就被細分成了多個小的三角面: 
http://ogldev.atspace.co.uk/www/tutorial30/subdivision.jpg

整體上TL會告訴PG三角形外邊緣上的分段的數量以及三角形邊到中心之間環的個數,從而進行三角形的構造。所以上面圖片中的這些小三角形與我們之前看到的patch有什麼關係呢?事實上這就主要取決於你想使用曲面細分技術去做什麼。其中一個非常簡單的用法(此教程中我們要用到的)就是跳過曲面的多項式表示,簡單說就是讓模型中的三角形面直接簡單地對映到patch上。那種情況下組成三角形的3個頂點就成了3個控制點,而原始的三角形既是TCS的輸入patch也是輸出patch。我們用PG來對三角形區域進行曲面細分並且生成由質心座標表示的“普通”三角形並對這些座標進行線性組合(例如將他們與原始三角形的屬性相乘)來對原始模型的三角形面進行細分。在下一節中我們我們將會介紹patch在幾何曲面上的實際應用。要牢記PG在意的不是TCS的輸入和輸出patch,而是每個patch的TL值。

至此PG完成了對三角形域的曲面細分,我們還需要使用細分的結果進行進一步的處理,畢竟PG自己無法訪問patch,它唯一的輸出就是質心座標和他們的連通性。進入TES著色器階段後,TES有許可權去訪問TCS中輸出的patch和PG生成的質心座標。PG對每一個質心座標都會執行TES著色器,而TES的功能就是為在PG中生成的每一個位於質心座標系下的頂點都生成一個真正的可用的頂點。因為可以訪問patch,TES可以從中獲取諸如位置、法線等資訊,並且通過這些資訊來生成頂點。在PG對一個“小”三角形的三個質心座標系下的頂點執行TES之後,由TES生成的這三個頂點會被傳遞到渲染管線的下一階段傳遞並把他們當做一個完整的三角形進行光柵化。

TES與頂點著色器十分相似,總是隻有一個輸入(質心座標)和一個輸出(頂點)。TES在每次呼叫過程中只能生成一個頂點,而且它不能丟棄頂點。OpenGL中曲面細分階段的TES著色器的主要目的就是藉助於PG階段生成的座標來生成曲面。簡單來說就是將質心座標變換到表示曲面的多項式中並計算出最終結果。結果就是新的頂點的位置,之後這些頂點就能與普通頂點一樣進行變換和投影了。如你所見,在處理幾何曲面的時候,如果我們選擇的TL值越高,我們獲得的區域位置就越多,而且通過在TES中對他們進行計算我們得到的頂點就會更多,這樣我們就能更好的表示精細的表面。在這一節中表面的計算公式我們簡單的使用一個線性組合公式來代替。

在TES著色器執行之後,產生的新的頂點會被作為三角形傳遞到渲染管線的下一階段。在TES之後接下來不管是GS還是光柵化階段,都和之前的一樣了。

總結一下整個渲染管線的過程:

  • patch中的每一個頂點都會執行頂點著色器,每個patch中都包含頂點快取中的多個控制點(CP)(控制點的最大值由驅動和GPU定義);
  • TCS著色器以頂點處理器處理之後的資料作為輸入並生成和輸出patch,除此之外它也會產生TLs;
  • 基於配置好的細分空間,通過獲取TCS著色器中的TL(細分層級)及其輸出的拓撲結構,PG會生成這個空間下的頂點位置和它們的連通性資訊;
  • 所有生成的位於細分空間下的位置都會經過TES著色器進行處理; 
    在第3步中生成的圖元會沿著渲染管線繼續傳遞,這些圖元的具體資料來自於TES著色器,然後流程會繼續推進到後面的GS階段和光柵化階段。