GPU精粹與Shader程式設計(一):關於基礎物理渲染
第一部分·主核心內容提煉總結
一、用物理模型進行高效的水模擬(Effective Water Simulation from Physical Models)
【內容概覽】
本章介紹了在GPU中模擬和渲染大型水體的一些方法,並且提出了改進反射的一些有用技巧。
文章由計算簡單的正弦函式之和來對水面進行近似模擬開始,逐步擴充套件到更復雜的函式如Gerstner波,也擴充套件到畫素著色器。主要思路是使用週期波的加和,來建立動態的平鋪(tiling)凹凸貼圖,從而獲得優質的水面細節。
這章也集中解釋了水體渲染與模擬系統中常用引數的物理意義,說明了用正弦波之和等方法來近似水面的要點。
圖基於文中水體技術渲染的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波計算水面高度的著色器。
圖基於快速傅立葉變換的水體渲染
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)的函式:
圖單個波函式的引數
而包括所有的波i的總表面是:
為了提供場景動力學的變數,我們將在約束中隨機產生這些波的引數,隨著時間的變化,我們會不斷將某個波淡出,然後再以一組不同的引數將其淡入。且此過程的這些引數是相關聯的,必須仔細地產生一套完整的引數組,才能使各個波以可信的方式進行組合。
1.2.2法線與切線
因為我們的表面有定義明確的函式,所以可以直接計算任意給定點處的曲面方向,而不是依賴於有限差分技術。
副法線(Binormal)B和正切向量T分別是x和y方向上的偏導數。
對於2D水平面中的任意點(x,y),表面上的三維位置P為:
求副法線(Binormal)B方向,即對上式對x方向求偏導。而求正切向量T方向,即對上式對y方向求偏導。
而法線N由副法線B和切線T的叉積給出:
1.3波的幾何特徵
首先文中將幾何波限制為4個,因為新增更多的波並不能增加新的概念,只不過增加更多相同的頂點Shader處理指令和常數而已。
1.3.1方向波或圓形波的選擇
需要對下圖所示的方向波或圓形波進行選擇。
圖方向波和圓形波
對於兩種型別的波,視覺特性和複雜性都是由干涉條紋引起的。
方向波需要的頂點shader處理指令較少,但是究竟選擇何種波需要取決於模擬的場景。對於大的水體,方向波往往更好,因為它們是風吹動產生的波較好的模型。對於較小的池塘的水,產生波的原因不是由於風,而是諸如例如瀑布,水中的魚,圓形波則更好一些。對於方向波,波的方向是在風向的一定範圍內任意繪製的;對於圓形波,波中心是在某些限定的範圍內任意繪製的。
1.3.2 Gerstner波
正弦波看起來圓滑,用於渲染平靜的,田園詩般的池塘很合適。而對於粗獷的海洋,需要形成較尖的浪頭和較寬的浪槽,則可以選擇Gerstner波。
Gerstner波早在計算機圖形學出現之前就已經被研發了出來,用於物理學基礎上為海水建模。Gerstner波可以提供一些表面的微妙運動,雖然不是很明顯但是卻很可信(具體可見[Tessendorf 2001])。
另外,Gerstner波有一種經常被忽略的性質:它將頂點朝著每個浪頭頂部移動,從而形成更尖銳的波峰。因為波峰是我們水錶面上最銳利的(即最高頻率,最主要)特徵,所以我們正希望頂點可以集中在此處。
圖Gerstner波
圖基於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)。
我們也使用水深度來控制水的不透明度、反射強度和幾何波振幅。簡單來說,即水淺的地方顏色淺,水深的地方顏色深。有了適當的水深度,也就可以去光的傳播效果進行更完善的建模。
圖真實感水體渲染效果圖 @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。
圖A New Dawn Demo截圖
以下是一張新老Demo的對比效果圖。
圖Dawn Demo(2002年)
圖A New Dawn Demo(2012年)
圖技術指標的對比
【章節概覽】
這章詳細介紹了NVIDIA出品的Dawn Demo中對精靈人物的著色技術,主要是皮膚的著色技巧。在當時(2002年)NVIDIA創造的此demo的品質,已經成為照片級真實感渲染和實時渲染的代表。
圖Dawn Demo截圖
【核心內容提煉】
2.1關於皮膚著色
基於多種原因,在計算機圖形中模擬皮膚十分困難。在當時,即使是在電影中用高階產品模擬出來的模擬角色,通常也經不起近距離的觀察。因為,人類可以從中獲得大量非語言來表達的資訊,如重心的移動,走動的特別習慣,面部的表情,甚至有些人的皮膚泛紅等等。
雖然很少有人能理解像“次表面散射(Subsurface Scattering)”、“輪廓照明(Rim
Lighting)”這些詞彙,但是當把它們渲染錯了的時候,幾乎任何人都可以指出來。而且除了著色問題外,有時人們會因為皮膚渲染的問題,說皮膚看起來像是塑料做的。
2.2皮膚如何對光進行響應
皮膚不像大多數在計算機渲染中建模的表面,因為它是由半透明的表皮、真皮和皮下組織等數層構成的。這可以用次表面散射來模擬。這種現象很普遍,當在太陽面前向上舉起手,就能看到穿過皮膚的桔紅色的光。
圖次表面散射-穿過皮膚的桔紅色的光
皮膚下的散射在所有的角度上顯現皮膚形態,使它具有了柔軟的、與眾不同的特徵。
在這之前有一些小組嘗試使用多層紋理貼圖來模仿皮膚的複雜性,但一般而言,這個方法比較難管理,美術同學很難通過預想,混合出最終符合預期的效果。
相反,文中使用單張彩色貼圖,通過著色程式來增加色彩的變化。
圖Dawn頭部的前半邊的漫反射貼圖
另外,皮膚具有一些極細微的變化,會影響其反射特性。這對皮膚外觀有微妙的影響,特別是當光線直接與相機位置相反時,皮膚的表現則是存在邊緣(Edge)與輪廓光照(Rim Lighting),這時,需要皮膚輪廓邊緣的光照,或給皮膚邊緣加上光暈。
真正的皮膚具有一些細微的特徵,比如汗毛和毛孔能捕捉光線。儘管這些細節用於顯式地建模是太不明顯了,但我們還是希望得到一個合適、整體更逼真的皮膚渲染外觀。在特寫時,可以增加凹凸貼圖,提供一些額外的細節,特別是一些小的皺紋。但需要注意,我們想要的是柔軟的皮膚外觀,而不是光閃閃的油膩的塑料。另外,凹凸貼圖通常只需靜距離特寫時才可見。
我們可以通過建模來近似這兩個著色屬性,建模可以是基於表面法線的簡單公式,或者是基於光線或視線向量的簡單公式。
通過認識,我們可以將上述兩種渲染特性(次表面散射和邊緣光照),建模為基於表面法線和照明或觀察向量的簡單公式,從而近似出兩種著色屬性。尤其是沿著Dawn的輪廓邊緣,對她身後的光線取樣,按照觀察向量的索引,讓“穿過”Dawn的光與她的基礎皮膚色調混合,從而建立次表面散射和邊緣光照的著色效果。尤其是背景圖中更加明亮的區域。如下圖。
圖Dawn的頭部前面的切線空間法線貼圖(凹凸貼圖)
2.3場景的照明
Dawn Demo中場景的照明使用了基於影象的光照(Image Based Lighting,
IBL),建立高動態範圍(High-Dynamic Range,HDR))的全景,使用環境對映貼圖(Environment Maps)進行場景的照明。
圖立方體環境反射貼圖
漫反射環境貼圖(Diffuse Environment Map)也是一個立方體對映貼圖,它使用網格表面的法線作為索引。每個畫素儲存了相應法線與入射光夾角的餘弦加權平均值。
圖漫反射環境貼圖(Diffuse Environment Map)
鏡面高光環境貼圖(Specular Environment Map)同樣也是一個立方體對映貼圖,使用反射向量作為索引(類似於立方體對映)。把此鏡面高光環境貼圖基於粗糙因子進行模糊,目的是模擬對任何表面任何給定點上的法線的改變。
圖鏡面高光環境貼圖(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中首次成形的技術進行了擴充套件,使其能夠高效能的渲染,以更好地適應遊戲引擎的需要。
圖Realistic Grass Field Giovanni Baer
【核心內容提煉】
3.1概述
首先,需要意識到,對單個草葉的細節建模意義不大,因為那樣大片草地需要的多邊形數目會太多。
所以,我們必須建立一個符合以下條件的簡單而有用的替代方案:
•許多草的葉片必須由少數多邊形表示。
•草地必須從不同的視線看起來顯得密集。
而要做到讓場景不依賴於攝像機的位置和方向,可以把一些草葉組合起來,表示在一個紋理中,並將多個紋理組合起來,且在結果中單個的多邊形不應該引起注意。當觀察者四處活動時,通過將草體加入混合操作或者移除混合操作,以在距離範圍內增加或刪去草體,來保證整個草地的渲染效果具有穩定的視覺質量。
3.2草的紋理
草的紋理,應該是一些一簇一簇聚集叢生的草,否則,會出現大片的透明區域。
需在透明的alpha通道上畫實體草莖。在彩色通道中,用深淺不同的綠色和黃色,來較好地區別各個單獨的葉片,也應該模擬不同情況的草葉:長得好的和長得差的、老的和嫩的,甚至區別葉片的前面與後面。
下圖是一個草地紋理的示例。
圖草地紋理的示意圖
3.3草體
這一部分將探討總結如何對多邊形進行組合,並用上文提到的草地紋理進行對映,以模擬出茂密的草地,並且不凸顯個別多邊形。此技術也保證了單個多邊形不可見。
因為使用者能自由地在場景中游玩,下圖所示的結構便不能產生令人信服的效果。
圖線性排布
對於線性排布,如果從垂直於多邊形的方向觀看場景,就會立刻穿幫,看出草地多邊形的結構是線性排布的。另外這種情況下草地會看起來非常稀疏。只有在攝像機自動導航,或者渲染無法到達的遠距離草地時,才會考慮這樣的排布。
為了保證獨立於當前視線的良好視覺質量,我們必須交叉地排布草地多邊形。已證明,使用星型結構是非常好的。下圖給出了“草體”可能的兩種變體。
他們由3個相交的方塊構成。我們必須禁用背面剔除來渲染多邊形,以保證雙面都可見。為了得到合適的照明度,應該讓所有頂點的法線方向與多邊形的垂直邊平行。這保證了位於斜坡上的所有草體都可以得到正確的光照,不會因為地形的亮度而出現差異。
圖草體的交叉排布
如果把這些草地物體彼此相當靠近地設定在一個大的區域裡,如下圖。在執行期間把它們從後向前進行排序,使用alpha混合,並啟用Draw
Call中的z-testing/writing,那麼就會得到自然而茂密的草地渲染效果。
圖草地的擴充套件
3.4草地的動畫
關於草地的動畫,基本思想是以三角函式(尤其是正弦和餘弦)為基礎進行計算,且計算應該考慮到移動的位置和當前時間、風向和強度。
以基本思想為基礎,實現起來有幾種方法:
1)每草叢草體的動畫(Animation per Cluster of Grass Objects)
2)每頂點的動畫(Animation per Vertex)
3)每草體的動畫(Animation per Grass Object)
三種方法各有優缺點,而文中都給出了具體演算法步驟和實現的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 Gems 1》封面
圖全書內容概覽圖
•原書配套原始碼、工程與資源下載:
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
相關文章
- GPU精粹與Shader程式設計(四):真實感渲染GPU程式設計
- GPU精粹與Shader程式設計(二):次表面散射與環境光遮蔽GPU程式設計
- GPU精粹與Shader程式設計(三):實時輝光與透視陰影貼圖GPU程式設計
- shader程式設計基礎:畫線程式設計
- OpenGL shader 程式基礎
- 渲染中的光照著色方式:PBR(Physically Based Rendering,物理基礎渲染)與 傳統經驗渲染
- cuda程式設計與gpu平行計算(四):cuda程式設計模型程式設計GPU模型
- PBR(基於物理的渲染)學習筆記2筆記
- 基於物理的渲染(PBR)白皮書(二) PBR核心理論與渲染光學原理總結
- 併發程式設計——基礎概念(一)程式設計
- 併發程式設計基礎與原子操作程式設計
- 基於物理的渲染(PBR)白皮書(一):PBR核心知識體系總結與概覽
- GPU程式設計--CPU和GPU的設計區別GPU程式設計
- Socket程式設計基礎程式設計
- Go程式設計基礎Go程式設計
- Shell程式設計-基礎程式設計
- python程式設計基礎Python程式設計
- shell程式設計基礎程式設計
- Go 併發程式設計 - Goroutine 基礎 (一)Go程式設計
- 2024-2025-3-計算機基礎與程式設計計算機程式設計
- 基於UDP程式設計UDP程式設計
- Java 基礎02Java程式設計基礎Java程式設計
- Windows程式設計系列:圖形程式設計基礎Windows程式設計
- NOI Linux 基礎知識與程式設計環境Linux程式設計
- Java網路程式設計基礎學習與整理Java程式設計
- 【socket程式設計基礎模板】程式設計
- QML程式設計 基礎 小白程式設計
- 【程式設計基礎】輸出程式設計
- 程式設計基礎知識程式設計
- 網路程式設計基礎程式設計
- python 程式設計基礎案例Python程式設計
- shell程式設計基礎二程式設計
- PLC(一)可程式設計控制器基礎程式設計
- Java併發程式設計——基礎知識(一)Java程式設計
- Shader 繪製基礎圖形
- 設計模式-UML關係基礎設計模式
- 3D遊戲程式設計與設計6——物理系統與碰撞3D遊戲程式設計
- 2024-2025-1 20241325 《計算機基礎與程式設計》計算機程式設計