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


 

                                    本文由@淺墨_毛星雲 出品,首發於知乎專欄,轉載請註明出處 

                                    文章鏈接: https://zhuanlan.zhihu.com/p/35974789

 

 

 

題圖背景來自《戰神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鏈接:

QianMo/Game-Programmer-Study-Notes

 

 

系列文章風格說明

 

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

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

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

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

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

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

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

 

 

目錄 · 核心內容導航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版本:

              https://developer.nvidia.com/gpugems/GPUGems/gpugems_pref01.html

            

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

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

 

圖 配套資源的截圖

 

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

            【Github】QianMo/GPU-Gems-CD-Content

 

 

 

 

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

 

 

一、 用物理模型進行高效的水模擬(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提供了完整的源代碼和項目工程,具體代碼和工程可以查看:

【Github】QianMo/GPU-Gems-CD-Content

 

【關鍵詞提煉】

 

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

 

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