GPU精粹與Shader程式設計(一):關於基礎物理渲染

遊資網發表於2019-07-08
第一部分·主核心內容提煉總結

一、用物理模型進行高效的水模擬(Effective Water Simulation from Physical Models)

【內容概覽】

本章介紹了在GPU中模擬和渲染大型水體的一些方法,並且提出了改進反射的一些有用技巧。

文章由計算簡單的正弦函式之和來對水面進行近似模擬開始,逐步擴充套件到更復雜的函式如Gerstner波,也擴充套件到畫素著色器。主要思路是使用週期波的加和,來建立動態的平鋪(tiling)凹凸貼圖,從而獲得優質的水面細節。

這章也集中解釋了水體渲染與模擬系統中常用引數的物理意義,說明了用正弦波之和等方法來近似水面的要點。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖基於文中水體技術渲染的Uru:Ages Beyond Myst中的場景

【核心內容提煉】

1.1背景與範圍

《GPU Gems 1》出版於2004年,在這幾年間,實時渲染技術漸漸從離線渲染領域中分離,自成一派。

而《GPU Gems 1》中收錄的這篇文章問世期間,快速傅立葉變換(Fast Fourier

Transform,FFT)庫已經能用於頂點和畫素著色器中。同時,執行於GPU上的水體模擬的模型也得到了改進。Isidoro等人在2002年提出了在一個頂點著色器中加和4個正弦波以計算水面的高度和方位的思路。另外,Laeuchi在2002年也發表了一個使用3個Gerstner波計算水面高度的著色器。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖基於快速傅立葉變換的水體渲染

1.2水體渲染的思路

文中對水體渲染的思路,執行了兩個表面模擬:一個用於表面網格的幾何波動,另一個是網格上法線圖的擾動。這兩個模擬本質上是相同的。而水面高度由簡單的週期波疊加表示。

正弦函式疊加後得到了一個連續的函式,這個函式描述了水面上所有點的高度和方向。在處理頂點時,基於每個頂點的水平位置對函式取樣,使得網格細分形成連續水面。在幾何解析度之下,將該技術繼續應用於紋理空間。通過對近似正弦疊加的法線取樣,用簡單畫素著色器渲染到渲染目標紋理(render target texture),從而產生表面的法線圖。對每幀渲染法線圖,允許有限數量的正弦波組相互獨立地運動,這大大提高了渲染的逼真度。

而直接疊加正弦波產生的波浪有太多的“簸盪(roll)”,而真實的波峰比較尖,波谷比較寬。事實證明,正弦函式有一個簡單的變體,可以很好地控制這個效果。

1.2.1波的選擇

對於每個波的組成,有如下幾個引數需要選擇:

•波長Wavelength(L):世界空間中波峰到波峰之間的距離。波長L與角頻率ω的關係為

ω=2π/L。

•振幅Amplitude(A):從水平面到波峰的高度。

•速度Speed(S):每秒種波峰移動的距離。為了方便,把速度表示成相位常數φ=S x

2π/L。

•方向Direction(D):垂直於波峰沿波前進方向的水平向量。

波的狀態定義為水平位置(x,y)和時間(t)的函式:

GPU精粹與Shader程式設計(一):關於基礎物理渲染

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖單個波函式的引數

而包括所有的波i的總表面是:

GPU精粹與Shader程式設計(一):關於基礎物理渲染

為了提供場景動力學的變數,我們將在約束中隨機產生這些波的引數,隨著時間的變化,我們會不斷將某個波淡出,然後再以一組不同的引數將其淡入。且此過程的這些引數是相關聯的,必須仔細地產生一套完整的引數組,才能使各個波以可信的方式進行組合。


1.2.2法線與切線

因為我們的表面有定義明確的函式,所以可以直接計算任意給定點處的曲面方向,而不是依賴於有限差分技術。

副法線(Binormal)B和正切向量T分別是x和y方向上的偏導數。

對於2D水平面中的任意點(x,y),表面上的三維位置P為:

GPU精粹與Shader程式設計(一):關於基礎物理渲染

求副法線(Binormal)B方向,即對上式對x方向求偏導。而求正切向量T方向,即對上式對y方向求偏導。

而法線N由副法線B和切線T的叉積給出:

GPU精粹與Shader程式設計(一):關於基礎物理渲染

1.3波的幾何特徵

首先文中將幾何波限制為4個,因為新增更多的波並不能增加新的概念,只不過增加更多相同的頂點Shader處理指令和常數而已。

1.3.1方向波或圓形波的選擇

需要對下圖所示的方向波或圓形波進行選擇。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖方向波和圓形波

對於兩種型別的波,視覺特性和複雜性都是由干涉條紋引起的。

方向波需要的頂點shader處理指令較少,但是究竟選擇何種波需要取決於模擬的場景。對於大的水體,方向波往往更好,因為它們是風吹動產生的波較好的模型。對於較小的池塘的水,產生波的原因不是由於風,而是諸如例如瀑布,水中的魚,圓形波則更好一些。對於方向波,波的方向是在風向的一定範圍內任意繪製的;對於圓形波,波中心是在某些限定的範圍內任意繪製的。

1.3.2 Gerstner波

正弦波看起來圓滑,用於渲染平靜的,田園詩般的池塘很合適。而對於粗獷的海洋,需要形成較尖的浪頭和較寬的浪槽,則可以選擇Gerstner波。

Gerstner波早在計算機圖形學出現之前就已經被研發了出來,用於物理學基礎上為海水建模。Gerstner波可以提供一些表面的微妙運動,雖然不是很明顯但是卻很可信(具體可見[Tessendorf 2001])。

另外,Gerstner波有一種經常被忽略的性質:它將頂點朝著每個浪頭頂部移動,從而形成更尖銳的波峰。因為波峰是我們水錶面上最銳利的(即最高頻率,最主要)特徵,所以我們正希望頂點可以集中在此處。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖Gerstner波

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖基於Gerstner渲染出的水面 Unreal Engine 4


1.3.3波長等引數的選擇

波長等引數的選擇方法:

•波長的選擇,要點是不要追求波在真實世界中的分佈,而是要使用現在的少數幾個波達到最大效果。對波長相似的波進行疊加可以突顯水面的活力。於是文中選擇中等的波長,然後從它的1/2至兩倍之間產生任意波長。

•波的速度,通過波長,基於公式即可計算得出。

•振幅方面,主要是在Shader中指定一個係數,由美術同學對波長指定對應的合適振幅。

•波的方向,運動方向與其他引數完全獨立,因此可以自由選擇。

1.4波的紋理特徵

加和到紋理中的波也像上文說到的頂點一樣需要引數化,但是其具有不同的約束條件。首先,在紋理中得到寬頻譜更為重要。其次,在紋理中更容易形成不像天然波紋的圖案。第三,對給定波長只有某些波方向能保證全部紋理的平鋪(tiling)。也就是說,不像在世界空間中僅僅需要注意距離,在紋素(texel)中要注意所有的量。

文中的思路是在2到4個通道中,使用15個頻率和方位不同的波進行處理。雖然4個通道聽起來有點多,但是它們是進行256 x 256解析度的渲染目標紋理的處理,而不是處理主幀的幀緩衝。實際上,生成法線貼圖的填充率所造成的影響小到可以忽略不計。

1.5關於深度

首先,把在頂點上的水深度作為一個輸入引數,這樣,在著色器碰到岸邊這樣的微妙區域時,便可以自動進行校正。

因為水的高度需要計算,所以頂點位置的z分量就沒什麼用了。雖然我們可以利用這點來壓縮頂點的資料量,但是選擇把水深度編碼在z分量中,是一個更好的選擇。

更確切地說,就是把水體底部的高度放在頂點的z分量中,作為常數帶入水的高度表中,這樣通過相減,即可得到水深度。而同樣,這裡假定了一個恆定高度的水位表(constant-height

water table)。

我們也使用水深度來控制水的不透明度、反射強度和幾何波振幅。簡單來說,即水淺的地方顏色淺,水深的地方顏色深。有了適當的水深度,也就可以去光的傳播效果進行更完善的建模。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖真實感水體渲染效果圖 @Unreal Engine 4

【核心要點總結】

文中提出的水體渲染方法,總結起來有三個要點:

1)使用週期波(正弦波、Gerstner波)的加和

2)建立動態的平鋪(tiling)貼圖

3)使用凹凸環境對映(Bump-Environment Mapping)

【本章配套程式碼彙總表】

文中並沒有貼出相關程式碼,但原書配套CD提供了完整的原始碼和專案工程,具體程式碼和工程可以檢視:

QianMo/GPU-Gems-CD-Contentgithub.com

【關鍵詞提煉】

水的模擬(Water Simulation)

水的渲染(Water Rendering)

正弦函式近似加和(Sum of Sines Approximation)

Gerstner波(Gerstner Waves)

凹凸環境對映(Bump Environment Mapping)

二、Dawn Demo中的皮膚渲染(Skin in the Dawn Demo)

十年技術變遷:NVIDIA Dawn Demo

最初的Dawn Demo由NVIDIA於2002年釋出,而十年之後的2012年,NVIDIA新發布了“A New Dawn”技術Demo。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖A New Dawn Demo截圖

以下是一張新老Demo的對比效果圖。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖Dawn Demo(2002年)

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖A New Dawn Demo(2012年)

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖技術指標的對比

【章節概覽】

這章詳細介紹了NVIDIA出品的Dawn Demo中對精靈人物的著色技術,主要是皮膚的著色技巧。在當時(2002年)NVIDIA創造的此demo的品質,已經成為照片級真實感渲染和實時渲染的代表。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖Dawn Demo截圖

【核心內容提煉】

2.1關於皮膚著色

基於多種原因,在計算機圖形中模擬皮膚十分困難。在當時,即使是在電影中用高階產品模擬出來的模擬角色,通常也經不起近距離的觀察。因為,人類可以從中獲得大量非語言來表達的資訊,如重心的移動,走動的特別習慣,面部的表情,甚至有些人的皮膚泛紅等等。

雖然很少有人能理解像“次表面散射(Subsurface Scattering)”、“輪廓照明(Rim

Lighting)”這些詞彙,但是當把它們渲染錯了的時候,幾乎任何人都可以指出來。而且除了著色問題外,有時人們會因為皮膚渲染的問題,說皮膚看起來像是塑料做的。

2.2皮膚如何對光進行響應

皮膚不像大多數在計算機渲染中建模的表面,因為它是由半透明的表皮、真皮和皮下組織等數層構成的。這可以用次表面散射來模擬。這種現象很普遍,當在太陽面前向上舉起手,就能看到穿過皮膚的桔紅色的光。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖次表面散射-穿過皮膚的桔紅色的光

皮膚下的散射在所有的角度上顯現皮膚形態,使它具有了柔軟的、與眾不同的特徵。

在這之前有一些小組嘗試使用多層紋理貼圖來模仿皮膚的複雜性,但一般而言,這個方法比較難管理,美術同學很難通過預想,混合出最終符合預期的效果。

相反,文中使用單張彩色貼圖,通過著色程式來增加色彩的變化。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖Dawn頭部的前半邊的漫反射貼圖

另外,皮膚具有一些極細微的變化,會影響其反射特性。這對皮膚外觀有微妙的影響,特別是當光線直接與相機位置相反時,皮膚的表現則是存在邊緣(Edge)與輪廓光照(Rim Lighting),這時,需要皮膚輪廓邊緣的光照,或給皮膚邊緣加上光暈。

真正的皮膚具有一些細微的特徵,比如汗毛和毛孔能捕捉光線。儘管這些細節用於顯式地建模是太不明顯了,但我們還是希望得到一個合適、整體更逼真的皮膚渲染外觀。在特寫時,可以增加凹凸貼圖,提供一些額外的細節,特別是一些小的皺紋。但需要注意,我們想要的是柔軟的皮膚外觀,而不是光閃閃的油膩的塑料。另外,凹凸貼圖通常只需靜距離特寫時才可見。

我們可以通過建模來近似這兩個著色屬性,建模可以是基於表面法線的簡單公式,或者是基於光線或視線向量的簡單公式。

通過認識,我們可以將上述兩種渲染特性(次表面散射和邊緣光照),建模為基於表面法線和照明或觀察向量的簡單公式,從而近似出兩種著色屬性。尤其是沿著Dawn的輪廓邊緣,對她身後的光線取樣,按照觀察向量的索引,讓“穿過”Dawn的光與她的基礎皮膚色調混合,從而建立次表面散射和邊緣光照的著色效果。尤其是背景圖中更加明亮的區域。如下圖。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖Dawn的頭部前面的切線空間法線貼圖(凹凸貼圖)

2.3場景的照明

Dawn Demo中場景的照明使用了基於影象的光照(Image Based Lighting,

IBL),建立高動態範圍(High-Dynamic Range,HDR))的全景,使用環境對映貼圖(Environment Maps)進行場景的照明。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖立方體環境反射貼圖

漫反射環境貼圖(Diffuse Environment Map)也是一個立方體對映貼圖,它使用網格表面的法線作為索引。每個畫素儲存了相應法線與入射光夾角的餘弦加權平均值。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖漫反射環境貼圖(Diffuse Environment Map)

鏡面高光環境貼圖(Specular Environment Map)同樣也是一個立方體對映貼圖,使用反射向量作為索引(類似於立方體對映)。把此鏡面高光環境貼圖基於粗糙因子進行模糊,目的是模擬對任何表面任何給定點上的法線的改變。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖鏡面高光環境貼圖(Specular Environment Map)

存在的問題是,漫反射環境貼圖(Diffuse Environment Map)和鏡面高光環境貼圖(Specular Environment Map)考慮了來自環境的入射光,但不包含由物體引起的陰影。

要解決這個問題,可以生成一個遮擋項,用來近似表達在每個頂點上半球輻射光中,有多大比率場景中其他物體所遮擋。

2.4實現

Dawn Demo中,毫無懸念地使用頂點著色器和畫素著色器進行光照處理,頂點shader的主要功能是將座標轉換到投影空間,並執行那些不能在畫素著色器中執行的數學運算。

採用單通道(one-pass)的光照解決方案,不需要另外其他的通道渲染,或alpha混合來建立皮膚表面。

文中提供了完整的頂點Shader和畫素Shader的原始碼,這裡因為篇幅原因不再贅述,具體可以參考原文(PS:上文有貼出Web版的英文全書原文的連結)。

【核心要點總結】

文中採用的皮膚渲染方法,總結起來有三個要點:

1)基於影象的光照(Image Based Lighting,IBL),採用高動態範圍(High-Dynamic-Range,HDR)光照環境對映貼圖

2)次表面散射(Subsurface Scattering)

3)對皮膚邊緣加上光暈,即輪廓照明/邊緣光照(Rim Lighting)

【本章配套程式碼彙總表】

Example 3-1.從CPU應用程式接收的每個頂點資料示例程式碼(The Per-Vertex Data

Received from the CPU Application)

Example 3-2.輸出頂點的資料結構示例程式碼(The Data Structure of the Output

Vertices)

Example 3-3.Dawn臉部的皮膚渲染頂點著色器示例程式碼(A Sample Vertex Shader for

Dawn's Face)

Example 3-4.Dawn臉部的皮膚渲染片元著色器程式碼(The Fragment Shader for Dawn's

Face)

【關鍵詞提煉】

皮膚渲染(Skin Rendering)

次表面散射(Subsurface Scattering)

輪廓照明(Rim Lighting)

基於影象的光照(Image Based Lighting,IBL)

高動態範圍(High-Dynamic-Range,HDR)

環境對映貼圖(Environment Maps)

三、無盡波動的草地葉片的渲染(Rendering Countless Blades of Waving Grass)

【章節概覽】

這章關於巨量自然元素的渲染,特別是對於無盡波動的草地葉片的渲染。作者對Codecreatures demo中首次成形的技術進行了擴充套件,使其能夠高效能的渲染,以更好地適應遊戲引擎的需要。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖Realistic Grass Field Giovanni Baer

【核心內容提煉】

3.1概述

首先,需要意識到,對單個草葉的細節建模意義不大,因為那樣大片草地需要的多邊形數目會太多。

所以,我們必須建立一個符合以下條件的簡單而有用的替代方案:

•許多草的葉片必須由少數多邊形表示。

•草地必須從不同的視線看起來顯得密集。

而要做到讓場景不依賴於攝像機的位置和方向,可以把一些草葉組合起來,表示在一個紋理中,並將多個紋理組合起來,且在結果中單個的多邊形不應該引起注意。當觀察者四處活動時,通過將草體加入混合操作或者移除混合操作,以在距離範圍內增加或刪去草體,來保證整個草地的渲染效果具有穩定的視覺質量。

3.2草的紋理

草的紋理,應該是一些一簇一簇聚集叢生的草,否則,會出現大片的透明區域。

需在透明的alpha通道上畫實體草莖。在彩色通道中,用深淺不同的綠色和黃色,來較好地區別各個單獨的葉片,也應該模擬不同情況的草葉:長得好的和長得差的、老的和嫩的,甚至區別葉片的前面與後面。

下圖是一個草地紋理的示例。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖草地紋理的示意圖

3.3草體

這一部分將探討總結如何對多邊形進行組合,並用上文提到的草地紋理進行對映,以模擬出茂密的草地,並且不凸顯個別多邊形。此技術也保證了單個多邊形不可見。

因為使用者能自由地在場景中游玩,下圖所示的結構便不能產生令人信服的效果。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖線性排布

對於線性排布,如果從垂直於多邊形的方向觀看場景,就會立刻穿幫,看出草地多邊形的結構是線性排布的。另外這種情況下草地會看起來非常稀疏。只有在攝像機自動導航,或者渲染無法到達的遠距離草地時,才會考慮這樣的排布。

為了保證獨立於當前視線的良好視覺質量,我們必須交叉地排布草地多邊形。已證明,使用星型結構是非常好的。下圖給出了“草體”可能的兩種變體。

他們由3個相交的方塊構成。我們必須禁用背面剔除來渲染多邊形,以保證雙面都可見。為了得到合適的照明度,應該讓所有頂點的法線方向與多邊形的垂直邊平行。這保證了位於斜坡上的所有草體都可以得到正確的光照,不會因為地形的亮度而出現差異。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖草體的交叉排布

如果把這些草地物體彼此相當靠近地設定在一個大的區域裡,如下圖。在執行期間把它們從後向前進行排序,使用alpha混合,並啟用Draw

Call中的z-testing/writing,那麼就會得到自然而茂密的草地渲染效果。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖草地的擴充套件


3.4草地的動畫

關於草地的動畫,基本思想是以三角函式(尤其是正弦和餘弦)為基礎進行計算,且計算應該考慮到移動的位置和當前時間、風向和強度。

以基本思想為基礎,實現起來有幾種方法:

1)每草叢草體的動畫(Animation per Cluster of Grass Objects)

2)每頂點的動畫(Animation per Vertex)

3)每草體的動畫(Animation per Grass Object)

三種方法各有優缺點,而文中都給出了具體演算法步驟和實現的Shader原始碼,這裡因為篇幅原因,便不展開分析了,具體可以參閱原文。

最終可以實現的渲染效果。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖Realistic Grass

【核心要點總結】

1)草的紋理,應選取一簇一簇聚集叢生的草。在透明的alpha通道上畫實體草莖。在彩色通道中,用深淺不同的綠色和黃色,區別各個單獨的葉片。

2)草體的渲染,適合進行交叉排布,從後向前進行排序,使用alpha混合,並啟用Draw

Call中的z-testing/writing,便能得到自然而茂密的草地渲染效果。

3)草地的動畫,以三角函式(尤其是正弦和餘弦)為基礎,且應該考慮到移動的位置和當前時間、風向和強度。實現起來有三種方法:

1.每草叢草體的動畫(Animation per Cluster of Grass Objects)

2.每頂點的動畫(Animation per Vertex)

3.每草體的動畫(Animation per Grass Object)

【本章配套程式碼彙總表】

Example 7-1.頂點著色器框架(Framework in the Vertex Shader)

Example 7-2.對每草叢草體的動畫的實現Shader程式碼(Code for Animation per Cluster

of Grass Objects)

Example 7-3.每頂點動畫實現Shader程式碼(Code for Animation per Vertex)

Example 7-4.每草體的動畫實現Shader程式碼(Code for Animation per Grass Object)

【關鍵詞提煉】

草地渲染(Grass Rendering)

草地動畫(Grass Animation)

草體(Grass Objects)

本文節選自GPU精粹三部曲“11本書中的第一本《GPU Gems 1》

《GPU Gems 1》其書

《GPU Gems 1》英文原版出版於2004年4月,中文版《GPU精粹1》出版於2006年1月。需要說明的是,書中很多內容放到今天,並不過時,仍然很有研究、學習、運用、實踐的價值。尤其是水體渲染,皮膚渲染,次表面散射、陰影渲染、後處理相關的章節。

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖《GPU Gems 1》封面

GPU精粹與Shader程式設計(一):關於基礎物理渲染
圖全書內容概覽圖

•原書配套原始碼、工程與資源下載:

http://http.download.nvidia.com/ ... CD_Image/Index.html

我維護了的一個名為“GPU-Gems-CD-Content”的GitHub倉庫,以備份這些珍貴的資源,也方便直接在GitHub Web端檢視大牛們寫的程式碼:https://link.zhihu.com/?target=h ... GPU-Gems-CD-Content

作者:毛星雲編譯
專欄地址:https://zhuanlan.zhihu.com/p/35974789

相關文章