當前位置: 華文星空 > 知識

[GPU Pro5] 光照篇

2021-05-18知識

一、基於物理的面積光源

這裏介紹【殺戮地帶:暗影墜落】中的基於物理的燈光系統,支持多種能以3D或2D函式表示的形狀的面積光源。通常BRDF只支持點光源,對於一個光源只接收來自一個方向的光照。這裏對提供給BRDF的光照資訊進行重新建模,對面光源上所有點進行重要性采樣從而計算放射線度積分。光照資訊的單位都基於物理,比如光照強度單位是流明,距離單位是米等。

首先回顧光照模型

假設光源有均勻的光通量分布即Li為常數,並令BRDF由蘭伯特漫反射模型和Cook-Terrance的微表面鏡面模型組成,則

所以需要做的是計算兩個積分

  • 近似漫反射積分
    透過重要性采樣進行蒙地卡羅積分,即定義輸入範圍然後根據機率分布隨機生成輸入再進行函式計算並積分,其中重要性采樣可以透過空間啟發式或者一個額外初始蒙地卡羅的低采樣pass來估計重要性。這裏有兩個假設,假設光源形狀有統一的法線以及dA不為積分點而改變,則dAcosθo只需計算一次,此時積分函式只與θi和r有關。也就是說此時需要關註函式cosθi/(r^2),該函式的最大和最小值在光源形狀的邊界,因此必然存在一點用它來積分會使得誤差最小,但計算該點十分昂貴,所以這裏只是在最大值附近尋找近似點。

  • 為了找到該點,首先找到cosθi最大值:沿法線方向向光源平面發射射線,如果交點p‘不在光源內則離交點最近的有效光源邊緣點為cosθi最大點pc;然後找到r^2的最小值,將表面點投影到光源平面上,如果投影點p’‘不在光源內則離它最近的有效光源邊緣點為r^2最小點pr。則最重要采樣在pc和pr之間,這裏將pc和pr的半程向量的交點作為該采樣點pd。另一種更簡便快速但誤差更大的方法是不計算pc和pr而是直接將p’和p‘’的半程向量的交點作為pd。

  • 近似鏡面反射積分
    透過重要性采樣來近似,即透過機率分布函式PDF來找到重要性采樣的鏡面錐體從而找到積分最重要的點和區域。鏡面錐體圍繞視線的反射向量r,它的頂角α由光澤度g計算: α (g) = 2\sqrt\frac2{g + 2}. 找到錐體和面積光源相交的部份As從而得到積分的範圍,然後以類似上一節的方式找到一個最重要的點來近似表示這個積分,這裏使用As的幾何中心psc,由psc計算鏡面反射項,再將As面積作為立體角微分來對它進行歸一化得到結果
  • 之前都是假設放射線率Li為常數,如果改變這個假設而是假設光照強度I為常數,即光通量在立體角上分布恒定而不再是在立體角和球面面積上都分布恒定,此時可以對光源上變化的Li進行預積分再根據光源面積進行歸一化得到漫反射項因子和鏡面反射項因子,然後在像之前介紹的近似計算漫反射項和鏡面反射項的的采樣點時各自乘以對應因子來校正結果

    二、使用Epipolar Sampling實作的大氣散射光

    大氣的散射光效果包括藍天、日落黃昏的多彩地平線以及體積光等,這裏介紹一種高效能的基於物理的方法來實作,該方法結合了兩個關鍵技術:Epipolar Sampling(用來減少ray marching的采樣數)和1D的最小/最大二元樹(用來加速ray marching)。該方法的優勢包括預計算很少、支持晝夜變化、支持相機移動、可以和級聯陰影整合。

    主要的演算法步驟為:

    1. 生成epipolar采樣:為螢幕上的每條epipolar線計算入點和出點,並分布采樣;
    2. 選擇ray marching的采樣:沿著epipolar線稀疏定位初始的ray marching采樣,為每個采樣計算粗糙的內散射,在變化明顯的地方增加額外采樣;
    3. 為每個epipolar切片構建1D的最小/最大二元樹
    4. 執行ray marching
    5. 從ray marching的采樣中為剩下的采樣插值生成內散射的radiance
    6. 將散射從epipolar座標系轉換到螢幕空間,並和衰減的back buffer結合

    其中有以下技術要點:

  • 光照傳輸和散射積分
  • 光照和粒子的互動分為散射和吸收兩種型別,散射即改變光線方向,其中散射後光線射向相機的話則為內散射。而空氣分為空氣分子和氣溶膠兩種粒子,由空氣分子造成的散射稱為瑞立散射,由氣溶膠造成的散射稱為米氏散射,之前在大氣散射的章節中有過詳細介紹

  • epipolar采樣
  • epipolar采樣即在螢幕上沿著對極線來分布采樣從而進行ray marching,對極線是光源和螢幕邊緣的等距點的連線,然後在對極線上均勻分布采樣點,並每隔若幹個點取一個點作為ray marching的初始采樣點。

  • 1D最小/最大二元樹
  • 在選擇采樣後進行ray marching時,對於每個采樣發射射線並轉換到光照空間然後在shadow map中執行ray marching從而考慮陰影,但逐個遍歷陰影貼圖紋素代價較大,所以使用二元樹演算法。

    將光線和當前對極線形成的平面稱為對極切片,而該切片和光照投影平面會有一條相交的線,沿著這條線采樣陰影貼圖會得到一張一維的高度圖,這張高度圖對於該切片上所有的相機射線都相同,所以只需檢查視線中的當前點是否在高度圖上或下來判斷是否在陰影中。此時使用二元樹方法,即當前射線端點深度的最大值如果小於樹節點的最小深度值,則該射線線段被完全照亮(圖2.6的AB),如果最小值大於最大值則完全在陰影中(圖2.6的CD)

    三、體積光效果

    這裏介紹【殺戮地帶:暗影墜落】在延遲管線中實作體積光效果,解決的挑戰包括如何方便藝術家控制效果和如何處理透明物體。

    主要演算法是為每個光源渲染一個形狀來表示光照體積(點光源為球形,聚光燈為錐形,太陽光則為整個螢幕),然後計算視線和光照體積的相交線段,接下來在相交線段上進行ray marching,對於ray marching的每個采樣都把它當作朝向光源的白色漫射表面來計算光照(為了節省計算關閉陰影過濾),根據ray march的步進大小和散射因子來對光照結果進行縮放,最後將所有ray march的采樣結果疊加到一起渲染到螢幕上則為體積光效果。其中對於太陽光的ray marching由於是全螢幕所以是在級聯上進行。

    散射因子用來描述光在所有方向上的散射數量,用一個相位方程式來表示散射光在角度上的分布,從而決定進入相機的散射光,這裏用來模擬米氏散射

    此時需要解決兩個問題:

  • 控制散射數量:此時散射光效果過於統一和靜態,可以使用粒子效果來動態控制散射數量,將粒子渲染到3D紋理中,然後再執行時尋找紋理
  • 透明物體的處理:如果簡單對透明物體alpha混合的話則不能和體積光正常混合,處理透明物體的方法類似於上面處理散射數量的方法,但不是在深度層儲存散射數量,而是在另一個3D紋理的深度層中儲存累計體積光密度,然後在渲染透明物體時采樣該3D紋理來的值物體前面有多少體積光,從而得到透明物體上的體積光效果
  • 其中為了減少計算代價,使用了以下手段進行技術最佳化。

  • 低分辨率渲染:用一個半分辨率的buffer進行渲染然後進行雙邊升采樣
  • 抖動ray marching:減少ray march的step數,為了減少帶狀走樣,使用一個抖動圖案來對ray march的采樣位置進行偏移,來使得ray march采樣分布不均勻。然後使用雙邊高斯模糊來使得結果平滑。
  • 四、Hi-Z螢幕空間的錐體追蹤的反射

    這裏介紹一種動態場景中的新穎的計算反射方法,使用了Hi-Z技術和螢幕空間的錐體追蹤技術。Hi-Z即Hierarchical-Z緩存,是螢幕空間對齊的四邊形樹,儲存在Hi-Z紋理的MIP通道中,用來跳過空白區域來快速找到交點從而加速光線追蹤。螢幕空間的錐體追蹤技術用來近似粗糙表面來產生模糊的反射。此外,還對該演算法進行了一些最佳化,包括時間性過濾(temporal filtering),即利用之前幀的結果來穩定當前幀的輸出並模擬多次光線彈射。

    主要演算法包括五個步驟:1. Hi-Z pass;2. 預積分pass;3. 光線追蹤pass;4. 預摺積pass;5. 錐體追蹤pass。具體如下:

  • Hi-Z Pass
  • Hi-Z緩存是儲存初始z緩存的四個相鄰像素中最大或最小值的更小緩存,這裏使用最小值。該緩存透過在ray marching時切換不同的層次等級來跳過空白區域從而快速尋找到交點。這個pass則是用來對深度緩存或者Z-buffer構建Hi-Z緩存。

  • 預積分pass
  • 這個pass計算用於錐體追蹤的場景可見性。將構建的Hi-Z緩存用作輸入,已知mip0等級的每個深度像素是100%的可見性,往下遍歷緩存時更粗糙等級的緩存有著更小的可見性Visibility(n)≤Visibility(n−1),這裏的目標是計算粗糙等級的可見性,透過計算在四個像素的最小和最大深度形成的體積中有多少幾何體的比例得到,如下圖中MIP1:(1-0.5)*100%=50%,MIP2:(1-0.25)*50%=37.5%。該緩存用於在之後錐體追蹤時ray marching采樣可見性並累積,累積到100%時則停止追蹤。

  • 光線追蹤pass
  • 在螢幕空間的深度緩存值是線性的,所以在螢幕空間進行光線追蹤。用以下方式表示射線

    對反射射線在Hi-Z緩存中一邊ray marching一邊遍歷不同的層次來快速得到交點,即不相交時在更粗糙的等級上步進,相交時則在更細膩的等級上尋找。

  • 預摺積pass
  • 對初始場景顏色緩存進行不同的模糊,來得到不同粗糙度對應的反射。

  • 錐體追蹤pass
  • 在光線追蹤pass完成後執行,光線追蹤pass產生specular的完美反射,而這個pass用於產生glossy反射。在螢幕空間構建一個和反射光線分布角度對應的錐體,透過錐體的橫切圓來采樣預摺積的緩存來近似反射光線的積分。具體是先測試錐體是否和Hi-Z相交,相交的話則決定有多少相交並和之前預積分計算的可見性相乘,不斷向下遍歷Hi-Z並累加采樣結果直到可見性到達100%,由此得到的顏色結果為反射結果。

    結果如下:

    五、TressFX中的頭發渲染

    這裏介紹頭發如何按股來渲染,主要的處理包括幾何擴張(即將頭發從線條幾何擴張為網格)、光照和陰影(包括頭發的自陰影)、抗鋸齒、透明度處理(對逐像素的連結串列填充並排序來處理)以及寫入頭發深度(方便整合其它透明物體和景深效果等)。具體如下:

  • 幾何擴張
  • 一開始頭發由線段組成的股來表示,線段由連續的頂點表示,為了使其光柵化表示,需要在頂點著色器中將線段轉化為兩個三角形組成的方格,此分時為兩步來實作。第一步是在世界空間對線段沿著頭發半徑擴張,形成和視截面對齊的類似公告牌的方格;第二步是在螢幕空間將頭發方格投影後繼續擴張√2/2 ≈ 0.71個像素來保證一根頭發絲最少占一個像素

  • 光照
  • diffuse計算為K_d*sin(t, l),其中t為切線方向,l為光照方向。而specular計算為

    但實際上頭發有兩層高光,基礎高光向發尾偏移並且主要受燈光顏色影響,第二層高光向發根偏移並且受燈光顏色和頭發顏色影響,偏移透過將反射方向靠近或遠離切線方向來實作。

  • 陰影和自陰影
  • 自陰影透過儲存頭發深度的陰影貼圖來近似計算如下,其中depth range是要著色的頭發片元距陰影貼圖中對應深度的距離

  • 抗鋸齒
  • 抗鋸齒的策略是計算螢幕像素被發絲覆蓋的比例,即覆蓋率,使用覆蓋率來修改頭發的alpha值。在這裏使用的是一種影像空間的方法GPAA(geometric post-process antialiasing),即根據在多邊形邊緣的像素和實際邊緣的距離來評估覆蓋率

  • 透明度
  • 螢幕上的一個像素可以對應多層半透明的頭發,此時可以逐像素生成連結串列來處理,分為兩個pass來實作。第一個pass是A-Buffer填充,生成包含所有頭發片元的未排序連結串列,每個連結串列節點包含顏色、深度和下個節點指標;第二個pass是排序和繪制pass,即遍歷所有連結串列進行排序並根據排序結果對頭發進行混合。

    六、電線抗鋸齒

    這裏介紹電線或其他長且細的物體的抗鋸齒方法。由於電線寬度在螢幕上可能小於一個像素,所以顯示結果可能閃爍或不連續。解決的思路是保證電線的寬度大於等於一個像素然後使用MSAA來解決鋸齒。方案是執行時定義電線的半徑(需要保證半徑大於一個像素對應的半徑),然後根據半徑將頂點沿著法線進行位移,接下來透過計算覆蓋率然後進行alpha混合來修正結果。

    但此時只是消除了幾何走樣,還需要解決著色走樣。著色走樣由一個像素對應的電線部份可能有多個法線方向造成。最簡單的方法是使用半蘭伯特模型的光照模型來產生最少的走樣,也可以在半蘭伯特模型和其他光照模型之間混合,還有種方法是將電線上所有法線都朝向相機