1. 程式人生 > >【GPU精粹與Shader程式設計】(二) 《GPU Gems 1》全書核心內容提煉總結 · 上篇

【GPU精粹與Shader程式設計】(二) 《GPU Gems 1》全書核心內容提煉總結 · 上篇

                                    本文由出品,首發於知乎專欄,轉載請註明出處 

題圖背景來自《戰神4》。

系列文章前言

我們知道,《GPU Gems》1~3 、《GPU Pro》1~7 以及《GPU Zen》組成的饕餮盛宴,共11本書,合稱“GPU精粹三部曲“,是遊戲開發、計算機圖形學和渲染領域的業界頂尖大牛們一線經驗的合輯彙編,是江湖各大門派武林絕學經典招式的精華薈萃,是瞭解業界各種高階知識和技法Trick,將自己的遊戲開發、圖形學與渲染能力提升到下一個高度的捷徑。

本文將總結提煉“GPU精粹三部曲“11本書中的第一本《GPU Gems 1》全書的核心內容,是【GPU精粹與Shader程式設計】系列文章正篇的第一篇,全文共1萬5千餘字。

本文內容關鍵詞:

  • 真實感水體渲染(Realistic Water Rendering)
  • 真實感面板渲染(Realistic Skin Rendering)
  • 無盡草地的渲染(Rendering Countless Blades of Waving Grass)
  • 水焦散渲染(Rendering Water Caustics)
  • 面部表情模擬(Facial expression simulation)
  • Perlin噪聲(Perlin Noise)
  • 火焰的渲染(Fire Rendering)
  • 衍射的模擬(Simulating Diffraction)
  • 陰影的渲染(Shadow Rendering)
  • 電影級光照(Cinematic Lighting)
  • 陰影貼圖抗鋸齒(Shadow Map Antialiasing)
  • 全方位陰影貼圖(Omnidirectional Shadow Mapping)

為什麼要分成上下兩篇?

原本計劃,是將整本書的提煉總結一次性更新,總字數3萬字。但很遺憾,當我將整本書的提煉內容3萬字貼到文字編輯器中,內容排版都已經基本完成時,發現文字編輯器下方直接提示已超出1萬6千字....

這時我才發現,原來知乎專欄的單篇文章,其實也是有字數限制的,大概1萬多漢字的樣子。

所以,原本全文3萬字的整本書的提煉總結文章,將分為上下兩部分發布。

今天暫且釋出上半部分,而下半部分的內容,則可以經過進一步的優化提煉,在下次更新中釋出。

不過話說回來,上半部分也已經超過1萬5字,內容方面,應該完全夠看了。

GitHub上已更新完整的上下兩篇

GitHub上自然是不會像知乎專欄這樣有單篇文章1萬多字字數限制的,所以全部的3萬字已經更新,想提前看3萬字完整版的同學請戳GitHub連結:

系列文章風格說明

為了讓每篇文章的乾貨更足,內容更加詳實,本文與後續文章的的寫作風格與文章結構,將在系列文章開篇中的規劃的寫作風格的基礎上,做一些微調。

具體是將每篇需要提煉的章節內容分為兩大部分:

  • 主核心內容
  • 次核心內容

其中,主核心內容,將選取大家最感興趣、最有提煉價值的渲染相關的內容,會用更加詳細的篇幅進行提煉總結。每章主核心內容將包含五個部分:

  • 【章節概覽】
  • 【核心內容提煉】
  • 【核心要點總結】
  • 【本章配套程式碼彙總表】
  • 【關鍵詞提煉】

而次核心內容,則會用更加精煉的篇幅進行總結。每部分將包含:

  • 【章節概覽】
  • 【核心要點】
  • 【本章配套程式碼彙總表】
  • 【關鍵詞提煉】

目錄 · 核心內容導航Highlight

【說明】上文已經說明,因為知乎專欄的字數限制,導致原本3萬字的內容需要分成兩次釋出,下文目錄中加粗標題為本文將包括的內容,非加粗的標題將在下次的文章中釋出。

對《GPU Gems 1》全書核心內容,將進行重點提煉總結的主核心內容有:

  • 一、用物理模型進行高效的水模擬(Effective Water Simulation from Physical
    Models)
  • 二、Dawn Demo中的面板渲染(Skin in the Dawn Demo)
  • 三、無盡波動的草地葉片的渲染(Rendering Countless Blades of Waving Grass)
  • 四、次表面散射的實時近似(Real-Time Approximations to Subsurface
    Scattering)
  • 五、環境光遮蔽(Ambient Occlusion)

對《GPU Gems 1》全書核心內容,將進行提煉總結的次核心內容有:

  • 六、水焦散的渲染 (Rendering Water Caustics)
  • 七、 Dawn Demo中的動畫(Animation in the "Dawn" Demo)
  • 八、 改良的Perlin噪聲實現(Implementing Improved Perlin Noise)
  • 九、Vulcan Demo中的火焰渲染(Fire in the "Vulcan" Demo)
  • 十、衍射的模擬(Simulating Diffraction)
  • 十一、高效的陰影體渲染(Efficient Shadow Volume Rendering)
  • 十二、電影級光照(Cinematic Lighting)
  • 十三、陰影貼圖抗鋸齒(Shadow Map Antialiasing)
  • 十四、全方位陰影對映(Omnidirectional Shadow Mapping)
  • 十五、使用遮擋區間對映產生模糊的陰影(Generating Soft Shadows Using Occlusion
    Interval Maps)
  • 十六、透視陰影貼圖(Perspective Shadow Maps: Care and Feeding)
  • 十七、逐畫素光照的可見性管理(Managing Visibility for Per-Pixel Lighting)
  • 十八、空間BRDF(Spatial BRDFs)
  • 十九、基於影象的光照(Image-Based Lighting)
  • 二十、紋理爆炸(Texture Bombing)
  • 二十一、實時輝光(Real-Time Glow)
  • 二十二、顏色控制(Color Controls)
  • 二十三、景深 (Depth of Field)
  • 二十四、高品質的影象濾波(High-Quality Filtering)
  • 二十五、用紋理貼圖進行快速濾波寬度的計算(Fast Filter-Width Estimates with
    Texture Maps)
  • 二十六、OpenEXR影象檔案格式(The OpenEXR Image File Format)

《GPU Gems 1》其書

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

圖 《GPU Gems 1》封面

圖 全書內容概覽圖

《GPU Gems 1》隨書配套資源與原始碼下載

這一節提供了《GPU Gems 1》隨書的配套資源,以及原始碼的下載地址。

PS:配套的不少工程中不僅包含完整的原始碼,也直接包含經過編譯後的exe執行檔案,可以直接執行後檢視效果。

NVIDIA官方網站上<GPU Gems>1~3 Web版的url變更有幾次了,下面的連結,是目前最穩定版本。

  • 原書全文的Web版本:
  • 原書配套原始碼、工程與資源下載:

圖 配套資源的截圖

  • 另外,我維護了的一個名為“GPU-Gems-CD-Content”的GitHub倉庫,以備份這些珍貴的資源,也方便直接在GitHub Web端檢視大牛們寫的程式碼:

第一部分 · 主核心內容提煉總結

一、 用物理模型進行高效的水模擬(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提供了完整的原始碼和專案工程,具體程式碼和工程可以檢視:

【關鍵詞提煉】

水的模擬(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)

第二部分 · 次核心內容提煉總結

六、水焦散的渲染 (Rendering Water Caustics)

【章節概覽】

這一章介紹了一種從美學角度出發(aesthetics-driven)來實時渲染水中焦散的方法。

圖 水的焦散效果

【核心要點】

水的焦散(Water Caustics)的定義:光從彎曲的表面反射或者折射,只聚焦在受光面的某些區域,於是就是產生焦散的現象。

圖 折射的計算(入射光線(E)從介質η1進入介質η2,發生折射,產生折射光線(T))

首先,從模擬的觀點出發,焦散其實可以通過正向或逆向光線追蹤計算。

正向光線追蹤中,要追蹤從光線射出並穿過場景的光線,累計其在不斷地區的貢獻。

而逆向光線追蹤,則以相反的過程工作,從海底開始,按照與入射相反的順序逆向根據光線,計算給定點的所有入射光線總和。

而該文章中,前一節介紹了逆向蒙特卡洛光線追蹤的一個簡化,並大膽假設一些光線對焦散有貢獻,並只計算到達海底光線的一個子集,因此,該方法計算消耗非常少,卻產生了儘管在物理上不正確但是非常逼真的焦散圖樣和效果。由於整個效果看起來非常令人信服,尤其是影象質量,使得這個方法非常值得實現。文中用HLSL和OpenGL都進行了實現,並按照慣例,提供了原始碼。

該演算法的虛擬碼如下:

1.Paint the ocean floor.
2.For each vertex in the fine mesh:
    a.Send a vertical ray.
    b.Collide the ray with the ocean's mesh.
    c.Compute the refracted ray using Snell's Law in reverse.
    d.Use the refracted ray to compute texture coordinates for the "Sun" map.
    e.Apply texture coordinates to vertices in the finer mesh.
3.Render the ocean surface.

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

Example 2-1. 關於波函式、波函式的梯度以及線平面截距方程的程式碼示例,(Code Sample for the Wave Function, the Gradient of the Wave Function, and the Line-Plane Intercept Equation)

Example 2-2. 最終渲染通道程式碼示例,展示了依賴紋理讀取操作 (Code Sample for the Final Render Pass, Showing the Dependent Texture Read Operations)

【關鍵詞提煉】

水焦散渲染(Rendering Water Caustics)

逆向蒙特卡洛光線追蹤(backward Monte Carlo ray tracing)

折射(Refraction)

七、 Dawn Demo中的動畫(Animation in the "Dawn" Demo)

【章節概覽】

這章主要講到程式設計人員如何幫助美術同學對混合形狀實行控制,從而建立不同的表情。主要是使用頂點Shader通過索引的蒙皮和變形網格物件(morph

target)來使一個高解析度網格變形,實現角色表情和動畫等效果。也討論了為實現實時動畫而考慮的各種折中方案。

圖 Dawn Demo的實時螢幕截圖

【核心要點】

使用變形目標(Morph Target)是表現複雜網格變形的常用方法。NVIDIA Demo團隊使用此技術建立的Zoltar等Demo從每秒插值30個網格開始,然後基於累計誤差方案(Accumulated Error Scheme)去除關鍵幀。使得我們能夠縮小檔案並減少儲存空間,最多可以將三分之二的原始關鍵幀,同時幾乎不會出現可見的失真。在這種型別的網格插值中,任何給定時間中只有兩個插值關鍵幀處於啟用狀態,而且他們是連續地執行的。另外,變形目標可以並行使用。

原文也中對變形目標(Morph Target)的具體實現進行了論述。

圖 表情的混合物件(混合形狀)

而蒙皮(Skinning)是一種網格變形方法,對網格中的每個頂點指定一組帶有權重的矩陣(權重最大可增加到1.0)。權重指明矩陣應該如何約束頂點。

為一個網格蒙皮做準備,通常要為網格建立一箇中間狀態,叫做繫結姿勢(Bind Pose),這個姿勢保持胳膊和腿略微分開,並且儘可能避免彎曲。

圖 Dawn的繫結姿勢(Bind Pose)

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

Example 4-1 以線性或連續樣式運用到變形目標的示例程式碼(Applying morph targets in

a linear or serial fashion sample code)

Example 4-2 單個"multiply-add"用於每個變形目標的示例程式碼(Single "multiply-add"

instruction for each morph target sample code)

Example 4-3 變形目標的實現示例程式碼(Morph Target Implementation sample code)

Example 4-4 使用四根骨頭蒙皮的示例程式碼(Application of four-bone skinning sample

code)

【關鍵詞提煉】

面部表情模擬(Facial Expression Simulation)

網格動畫(Mesh Animation)

變形目標(Morph Target)

蒙皮(Skinning)

八、 改良的Perlin噪聲實現(Implementing Improved Perlin Noise)

【章節概覽】

這章的作者是奧斯卡得主Ken Perlin。他提出的噪聲演算法(Perlin Noise)已在實時和離線計算機圖形學中得到多方面運用。這篇文章詳細闡述了最新進展,糾正了最初的兩個缺陷,也提供了有效及穩定的框架結構,用於在現代可程式設計硬體上執行噪聲運算。

【核心要點】

首先,噪聲函式的目的,是在三維空間中提供一種可以有效率地實現、可重複,偽隨機的訊號。其訊號的能帶有限(band-limited),大部分能量集中在一個空間頻率附近,而視覺上是各向同性(isotropic)的,統計上不隨旋轉變化。

一般的想法是建立某種訊號,類似於一個完全的隨機訊號(即白噪聲),通過低通濾波後,濾除了所有的空間高頻率而變得模糊。有如沙丘你緩慢上升的山包和下落的低谷。

圖 沙丘與噪聲

最初的噪聲在1983年實現,1985 (Perlin 1985)發表,思想是使用埃爾米特-樣條(Hermite

spline)插值法等方法實現,原文中對此方法的步驟進行了描述。

圖 使用埃爾米特-樣條(Hermite spline)插值法,從常規3D格點的八個樣本中插值

圖 通過噪聲函式產生的一個切片樣條

這篇文章從兩個方面對最初的噪聲方法的不足進行了改進:

插值的特性以及偽隨機斜率場(field of pseudo-random gradients)的特性。

圖 四種基於噪聲生成的紋理

而另外一個關於噪聲的思路是,用體積噪聲製造程式式紋理(Procedural texturing using volumetric noise),這樣可以不建立顯式的紋理影象,來得到自然的材質。這種方法在當年的大片《指環王》中,已經有了廣泛應用。

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

5-1 假設模型是單位半徑球體,實現凹凸模式的示例程式碼(Assuming the model is a unit-radius sphere, the expressions that implement these bump patterns sample Code)

【關鍵詞提煉】

Perlin噪聲(Perlin Noise)

噪聲函式(Noise function)

偽隨機斜率場(Field of Pseudo-Random Gradients)

體積噪聲( volumetric noise)

埃爾米特樣條(Hermite spline)

九、Vulcan Demo中的火焰渲染(Fire in the "Vulcan" Demo)

【章節概覽】

這章講述了GeForce FX 5900上市時的Demo“Vulcan”中的火焰渲染技術。其中的技術並非真正的物理模擬,而是對當時的工業標準電影《指環王》的離線技術的跟進。通過文中改進,突破了光柵化大量粒子時操作效能的限制,產生了真實可信的火焰影象。

圖 基於本章方法實現的"Vulcan" Demo的截圖

【核心要點】

首先文章嘗試了兩個方案:

完全程式化的火焰( fully procedural flames)和螢幕空間基於變形的二維火焰(screen-space 2D distortion-based flames.),經過試驗都未達預期。

於是改採用視訊紋理精靈(video-textured sprites ),最終達到預期,並實現出了逼真的火焰,且佔用很少的GPU資源。

圖 用於建立火焰效果的連續鏡頭

其中,煙的生成使用粒子系統建立一個煙霧生成器。而所需的光照可以採用不同的技術達到,如光線投射。

圖 程式式地產生煙

而火焰和煙的混合,比較常規地使用相加混合(additive blending)。

關於使火焰增加多樣性,文中使用了水平和垂直翻轉(沿著u和v軸)。而使用任意旋轉可以更加具表現力。

圖 由自定義紋理座標生成的變體

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

Example 6-1 最終的實現Shader程式碼(The Final Shader)

【關鍵詞提煉】

火焰渲染(Fire Rndering)

完全程式化的火焰(fully procedural flames)

螢幕空間基於變形的二維火焰(screen-space 2D distortion-based flames)

視訊紋理精靈(video-textured sprites )

十、衍射的模擬(Simulating Diffraction)

【章節概覽】

這章講述了簡化的Jos衍射光照模型(最初在SIGGRAPH 1999上發表),此模型以光的物理性質為基礎,將光當做波來進行建模,從而創建出多彩的干涉條紋。

【核心要點】

什麼是衍射(Diffraction)?

小尺度的表面細節引起反射波彼此干擾,這個現象就是衍射。

首先,計算機繪圖的大多數表面反射模型都忽略自然光的波動效果。當表面的細節比光的波長(約1um)大許多時,不存在問題。但對於小尺寸的細節,例如一個光碟的表面,波效應就不能忽略了。所以,對於小尺度的表面細節引起反射波彼此干擾的現象,即為衍射。

衍射使這些表面的反射光呈現五彩繽紛的圖案,由光碟的精細反射可以看到這一現象。

圖 光碟的衍射

衍射的實現,可以在Shader的頂點著色器上,也可以在片元著色器上,且實現可以在任何網格上進行,只需提供一個“切線向量”,和每頂點的法線及位置。而切線向量提供表面上窄條帶的區域性方向。對於一個光碟,其為軌道的方向,如下圖。

圖 光碟的切線向量

對應給定衍射波長的顏色,可以使用簡單近似的彩虹貼圖。貼圖從紫到紅排列,而且提供彩虹的大部分顏色,用三個理想凹凸函式(峰值分別在藍、綠和紅的區域)簡單混合而成。

圖 用於shader的彩虹彩色貼圖

而最終的衍射顏色是彩色的衍射圖案和各項異性高光的簡單相加的和。

圖 光碟衍射實時的3個快照


圖 用紋理對映各項異性主要方向表面的3個快照

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

Example 8-1. 衍射的頂點著色器程式碼(The Diffraction Shader Vertex Program)

【關鍵詞提煉】

衍射模擬(Simulating Diffraction)

各項異性(Anisotropy)

十一、高效的陰影體渲染(Efficient Shadow Volume Rendering)

【章節概覽】

這章全面講述了用於實時陰影渲染中常見兩種流派之一的陰影體(Shadow Volumes)技術,又稱模板陰影(Stencil Shadows)技術,重點是得到正確的角度的情形,減少幾何圖形和填充率的消耗。

【核心要點】

當時id software的《Doom 3》就是採用陰影體(Shadow Volumes)技術來對陰影進行的渲染。具體思想是在模板(stencil)緩衝標記陰影的畫素,把畫素分為陰影或照明兩種型別,接著調節負責光照的畫素程式,使陰影畫素的照明貢獻度為0。

陰影體技術可以為點光源、聚光燈和方向光源建立清晰的、逐畫素進度的陰影。單個物體可以被多個光源照亮,而且光有任意顏色和衰減度。陰影從三角形網格投射到深度緩衝區上。這意味著被遮擋的物體,可以是帶有深度緩衝區的網格、公告板、粒子系統或預先渲染的場景。

較其他運算相比,陰影體可以更好地處理許多製作困難的陰影場景,如一個插在萬聖節南瓜燈內部的光源。

圖 陰影體技術可以很好勝任的渲染場景

陰影體的缺點是對那些沒能正確表達自身形狀的網格的陰影表達效果並不理想。如一些帶透明區域的公告板,粒子系統,或帶alpha粗糙度的紋理網格(如一片樹葉)。這些投影體基於他們的真實網格產生陰影,陰影與物體的真實形狀並不匹配。而陰影體的另一個缺點是對帶裂縫的網格支援不太好。文中也表示,當時陰影體執行的理想場景是頂部俯視。

圖 模型上的裂縫會讓影子穿過空氣漏出來

總之,這篇文章對McGuire等人2003年提出的方法進行了很好的描述、分析與實踐。而在這篇文章發出之後的若干年,陰影體技術得到了各種進一步地優化與改進。

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

Example 9-1 程式結構虛擬碼(Program Structure Pseudocode)

Example 9-2 glFrustum風格的無限投影矩陣(An Infinite Projection Matrix in the Style of glFrustum)

Example 9-3 用於從示例光源中“擠”出 w=0頂點的頂點著色器程式碼(A Vertex Shader for Extruding w = 0 Vertices Away from the Example Light)

Example 9-4 (The markShadows Method)

Example 9-5 findBackfaces方法(The findBackfaces Method)

Example 9-6 renderShadowCaps方法(The renderShadowCaps Method)

Example 9-7 renderShadowSides方法(The renderShadowSides Method)

【關鍵詞提煉】

陰影渲染(Shadow Rendering)

陰影體(Shadow Volume)/ 模板陰影(Stencil Shadows)

多通道渲染(Multipass Rendering)

十二、電影級光照(Cinematic Lighting)

【章節概覽】

本章中介紹了一個的簡化的uberlight(可理解為“全能光照”)實現,此光照shader根據Ronen Barzel(1997,1999)提出的照明模型編寫而成。而該模型的超集已由Pixar動畫開發,並應用於《玩具總動員》、《怪物公司》、《海底總動員》等一系列的迪士尼電影中。

本章所對該光照模型的嘗試,旨在提供一套全面的光照控制引數,以涵蓋燈光美術師日常使用的大部分效果。

圖 《怪物公司》 中cookies對窗戶效果的貢獻

【核心要點】

首先,該章中呈現的Shader只模擬光照場景光源的形成和控制,不包括如何模擬表面細節和光反射行為的複雜性。

大體上,用於電影產品的照明模型會進行兩種操作,類似於顯示在這裡的虛擬碼:

color illuminationModel()
{
   Computer the surface characteristic
   For each light   {
        Compute the surface response(計算表面響應)
        Evaluate the light source (評估光源)   }
}

首先,通過這些方式計算表面著色資訊:執行各種紋理查詢(texture lookups),在網格上插值(interpolating values over the mesh),計算程式模式(computing procedural

patterns)等。然後在照明物體的每個光源上迴圈,計算出它的貢獻。我們通過對每個光線計算光的顏色,然後計算表面對照明的響應來進行上述操作。

原文中給出了一個Shader原始碼,該Shader用於計算塑料(plastic)材質在只有一個光源貢獻的反射模型。,可以很容易將它擴充套件為更通用的多光源和更多表面的解決方案。

該Shader為美術師提供了各個方面的照明控制:選擇(指定物體是否響應接受光照),顏色,形狀,陰影和紋理,而陰影選項中包括明暗度、色調、反射、陰影貼圖、陰影模糊等引數。

下面兩幅圖說明了uberlight 的使用效果。照明來自Pixar短片“Geri's Game”中的人物頭部。

圖 (a)Geri由一個光源照明;(b)改變光的權重,修改反射高光對比度;(c)改變陰影顏色,加強陰影;(d)改變穀倉形狀(類似窗戶一樣的遮擋物),建立更戲劇化的姿態;(e)使用一塊模糊的紋理cookie,豐富影象;(f)誇大透射的cookie的對比,建立像外星人一樣的效果

圖(a)常態 (b)黑色電影(noir)的高反差 (c)柔和的光線

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

10-1. The Vertex Program for an Uberlight-Like Shader

10-2. The Fragment Program for an Uberlight-Like Shader

【關鍵詞提煉】

電影級光照(Cinematic Lighting)

全能型光照(Uberlight)

照明模型(Lighting Model)

儲存於本地的光照資料(Light Cookies)

十三、陰影貼圖抗鋸齒(Shadow Map Antialiasing)

【章節概覽】

這章介紹瞭如何通過鄰近百分比過濾方法(Percentage-Closer Filtering , PCF)有效減少陰影貼圖的反走樣。

【核心要點】

陰影貼圖(Shadow Map,又譯作陰影對映)是渲染陰影的常見方法,也是渲染陰影領域的兩大流派之一,但是它存在走樣的問題。通常使用高分率的陰影貼圖和增加陰影貼圖的解析度來反走樣,也就是使用Stamminger和Drettakis 2002年提出的“透視陰影貼圖( perspective shadow maps)”技術。但是,當光與表面接近於平行的時候,使用“透視陰影貼圖”技術和增加陰影貼圖解析度就不起作用了,因為放大的倍數接近於無窮大。

高階渲染軟體使用“臨近的百分比過濾(Percentage-Closer Filtering , PCF)”技術解決走樣問題。最初的PCF演算法由Reeves等人1987年提出。其計算的是靠近光源表面的百分比,而不是在陰影中表面的百分比,具體是多次比較陰影貼圖的每個畫素,求其平均值。

且文中對傳統的PCF演算法做了改進,不再計算陰影貼圖空間中被遮擋的區域,只是簡單地在各處使用一個4 x 4個texel(紋素)的樣本塊。這個塊應該大到能夠有效地減少走樣,但是不能達到要求大量樣本和隨機取樣的程度。如下圖。

圖 (a)每畫素取1個樣本 (b)每畫素取4個樣本 (c)每畫素取16個樣本

可以看到3幅圖中的顯示效果區別很明顯,圖(c)中每畫素取16個樣本,效果最為出色,達到了反走樣的預期。

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

PS:原文中沒有對程式碼片段進行編號,這裡的編號為附加。

Example 11-1 暴風(Brute Force)演算法16取樣版本的片元程式實現程式碼

Example 11-2 陰影貼圖反走樣的4取樣實現版本程式碼

【關鍵詞提煉】

反走樣/抗鋸齒(Antialiasing)

鄰近百分比過濾(Percentage-Closer Filtering , PCF)

透視陰影貼圖( perspective shadow maps)

十四、全方位陰影貼圖(Omnidirectional Shadow Mapping)

【章節概覽】

在這章中,把陰影貼圖的思路擴充套件到正確處理全方位的(點)光源中,其中包括了實現細節,也涉及到基本硬體能力不足時的低效執行策略。

【核心要點】

首先,這篇文章也談到了在實時計算機圖形學中產生可見陰影的兩個流行方法是:

  • 模板陰影(stencil shadows)/ 陰影體(Shadow Volume)
  • 陰影貼圖(shadow mapping)

模板陰影(Stencil Shadows,也被稱Shadow Volume,陰影體)作在《Doom 3》中有所應用,優點是得到大量的GPU支援、獨立於光源的種類、產生的陰影質量很高。但缺點是嚴重依賴於CPU,只能產生清晰的影子,需要很高的填充率,而且不能與硬體(hardware-tessellated)的表面一起使用。

陰影貼圖(Shadow Mapping,也譯作陰影對映)由Lance Williams於1978年引入計算機圖形學,文章釋出當時多數好萊塢電影都在使用這個方法,包括計算機渲染和特效。為了計算陰影,陰影對映在場景幾何體上投射特殊的動態建立的紋理。它可以渲染清晰和模糊的影子,以及由不同型別的光源產生的陰影,它還可以與硬體鑲嵌的表面以及GPU動畫的網格(例如蒙皮網格)一起使用。

該文章主要介紹了全方位陰影貼圖(Omnidirectional Shadow Mapping)方法,該方法有兩個主要步驟:

  • 建立陰影貼圖
  • 進行陰影投射

在建立階段,對所有把陰影投射到陰影貼圖紋理上的物體,渲染它們到光源的距離的平方。而在投射結算,渲染所有接受陰影的物體,並比較所渲染的畫素到光源的距離的平方。以下為全方位陰影對映演算法的虛擬碼:

for (iLight = 0; iLight < NumberOfLights; iLight++) 
{     // Fill the shadow map.
     for (iObject = 0; iObject < NumberOfObjects; iObject++)
    {       RenderObjectToShadowMap(iLight, iObject);     }
     for (iObject = 0; iObject < NumberOfObjects; iObject++) 
     // Lighting and shadow mapping.     {       LightAndShadeObject (iLight, iObject);     }
}

圖 Omnidirectional Shadow Mapping @Merlin3d

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

Example 12-1 全方位陰影對映演算法的虛擬碼(Pseudocode for the Omnidirectional

Shadow-Mapping Algorithm)

Example 12-2 僅渲染深度(Depth-Only Rendering)

Example 12-3 產生一個軟陰影(Making a Softer Shadow)

【關鍵詞提煉】

陰影渲染(Shadow Rendering)

陰影貼圖(Shadow Mapping)

模板陰影(stencil shadows)/ 陰影體(Shadow volume)

全方位陰影對映(Omnidirectional Shadow Mapping)

The End.

下次更新,《GPU Gems 1》全書核心內容提煉總結 · 下篇,再見。

With best wishes.