1. 程式人生 > 其它 >第5章-著色基礎-5.2-光源

第5章-著色基礎-5.2-光源

《實時渲染》第四版中翻,第5.2節。

5.2 光源

光照對我們示例著色模型的影響非常簡單;它為著色提供了一個主導方向。當然,現實世界中的照明可能非常複雜。可以有多個光源,每個光源都有自己的大小、形狀、顏色和強度;間接照明甚至增加了更多的變化。正如我們將在第9章中看到的,基於物理的、寫實的著色模型需要考慮所有這些引數。

相比之下,風格化的著色模型可能會以多種不同的方式使用照明,具體取決於應用程式的需求和視覺風格。一些高度風格化的模型可能根本沒有照明的概念,或者(如我們的 Gooch 著色示例)可能僅使用它來提供一些簡單的方向性。

照明覆雜性的下一步是使著色模型以二元的方式對光的存在或不存在做出反應。用這種模型著色的表面在被照亮時將具有一種外觀,而在不受光影響時具有不同的外觀。這意味著區分這兩種情況的一些標準:與光源的距離、陰影(將在第7章

中討論)、表面是否背對光源(即表面法線\(\textbf{n}\)與光向量\(\textbf{l}\)之間的夾角大於 \(90^{\circ}\)),或這些因素的某種組合。

從光的二元存在或不存在到光強度的連續尺度,這是一小步。這可以表示為不存在和完全存在之間的簡單插值,意味著強度的有界範圍,可能是0到1,或者表示為以其他方式影響著色的無界數量。後者的一個常見選項是將著色模型分解為有光照和無光照的部分,光照強度\(k_\textrm{light}\)線性縮放光照部分:

\[\textbf{c}_\textrm{shaded} = f_\textrm{unlit}(\textbf{n},\textbf{v}) + k_\textrm{light}f_\textrm{lit}(\textbf{l},\textbf{n},\textbf{v}) \tag{5.3} \]

這很容易擴充套件到RGB顏色\(\textbf{c}_\textrm{light}\)

:

\[\textbf{c}_\textrm{shaded} = f_\textrm{unlit}(\textbf{n},\textbf{v}) + \textbf{c}_\textrm{light}f_\textrm{lit}(\textbf{l},\textbf{n},\textbf{v}) \tag{5.4} \]

並且,對於對光源:

\[\textbf{c}_\textrm{shaded} = f_\textrm{unlit}(\textbf{n},\textbf{v}) + \sum_{i = 1}^{n}\textbf{c}_{\textrm{light}_{i}}f_\textrm{lit}(\textbf{l}_{i},\textbf{n},\textbf{v}) \tag{5.5} \]

無光照部分\(f_\textrm{unlit}(\textbf{n},\textbf{v})\)

對應於將光視為二元著色模型的“不受光照影響時的外觀”。它可以有多種形式,具體取決於所需的視覺風格和應用程式的需要。例如,\(f_\textrm{unlit}() = (0,0,0)\)將導致任何不受光源影響的表面變成純黑色。或者,無光照的部分可以為無光照物體表達某種形式的風格化外觀,類似於 Gooch模型的冷色表面遠離光線。通常,著色模型的這部分表示某種形式的照明,這些照明不是直接來自明確放置的光源,例如來自天空的光或從周圍物體反射的光。這些其他形式的照明將在第10章第11章中討論。

我們之前提到過,如果光的方向\(\textbf{l}\)與表面法線\(\textbf{n}\)的夾角超過90度,則光源不會影響表面點,實際上來自表面下方。這可以被認為是光的方向(相對於表面)與其對著色的影響之間更一般關係的一種特殊情況。儘管基於物理,但這種關係可以從簡單的幾何原理推匯出來,並且對於許多型別的非基於物理的風格化著色模型也很有用。

光在表面上的效果可以視覺化為一組光線,照射到表面的光線密度與用於表面著色目的的光強度相對應。參見圖5.4,它顯示了一個被照亮的表面的橫截面。沿該橫截面撞擊表面的光線之間的間距與\(\textbf{l}\)\(\textbf{n}\)之間的夾角的餘弦成反比。因此,入射到表面的光線的總密度與\(\textbf{l}\)\(\textbf{n}\)之間夾角的餘弦成正比,正如我們之前看到的,它等於這兩個單位長度向量之間的點積。在這裡我們看到為什麼定義與光行進方向相反的光向量\(\textbf{l}\)很方便;否則我們將不得不在執行點積之前取反它。

圖5.4. 上排的附圖顯示了表面上的光的橫截面圖。在左側,光線直接照射在表面上,在中心它們以一定角度照射表面,在右側,我們看到使用向量點積來計算角度餘弦。下圖顯示了與整個表面相關的橫截面平面(包括光和檢視向量)。

更準確地說,光線密度(以及光線對著色的貢獻)與點積為正時成正比。負值對應於來自表面後面的光線,沒有任何影響。所以,在將光的著色乘以光照點積之前,我們需要先將點積夾鉗(clamp)為0。使用1.2節中介紹的\(x^+\)表示法,這意味著將負值夾鉗為零,因此有:

\[\textbf{c}_\textrm{shaded} = f_\textrm{unlit}(\textbf{n},\textbf{v}) + \sum_{i = 1}^{n}({\textbf{l}_i}\cdot{\textbf{n}})^{+}\textbf{c}_{\textrm{light}_{i}}f_\textrm{lit}(\textbf{l}_{i},\textbf{n},\textbf{v}) \tag{5.6} \]

支援多個光源的著色模型通常會使用公式5.5中的一種結構,它更通用,或者公式 5.6,這是基於物理的模型所必需的。它對於風格化模型也很有用,因為它有助於確保照明的整體一致性,特別是對於背離燈光或有陰影的表面。然而,有些模型並不適合這種結構;此類模型將使用公式5.5中的結構。

函式\(f_\textrm{lit}()\)最簡單的可能選擇是使它成為一個恆定的顏色:

\[f_\textrm{lit}() = \textbf{c}_\textrm{surface} \tag{5.7} \]

這會形成下面的著色模型:

\[\textbf{c}_\textrm{shaded} = f_\textrm{unlit}(\textbf{n},\textbf{v}) + \sum_{i = 1}^{n}({\textbf{l}_i}\cdot{\textbf{n}})^{+}\textbf{c}_{\textrm{light}_{i}}\textbf{c}_\textrm{surface} \tag{5.8} \]

該模型的光照部分對應於Lambertian著色模型,以Johann Heinrich Lambert[967]命名,他在1760年就發表了它(令人驚歎)。該模型適用於理想的漫反射表面,即完美無光澤的表面。我們在此對Lambert模型進行稍微簡化的解釋,第9章將對其進行更嚴格的介紹。Lambertian模型可以單獨用於簡單的著色,它是許多著色模型中的關鍵構建塊。

我們可以從方程5.3-5.6中看到,光源通過兩個引數與著色模型互動:指向光的向量\(\textbf{l}\)和光的顏色\(\textbf{c}_\textrm{light}\)。有各種不同型別的光源,主要區別在於這兩個引數在場景中的變化方式。

我們接下來將討論幾種流行的光源型別,它們有一個共同點:在給定的表面位置,每個光源僅從一個方向\(\textbf{l}\)照亮表面。換句話說,從著色表面位置看到的光源是一個無限小的點。對於真實世界的燈光來說,嚴格來說並非如此,但大多數光源相對於它們與被照明表面的距離而言都很小,因此這是一個合理的近似值。 在第7.1.2第10.1節中,我們將討論從一系列方向照亮表面位置的光源,即“區域光”。

5.2.1 平行光

平行光是最簡單的光源模型。\(\textbf{l}\)\(\textbf{c}_\textrm{light}\)在場景中都是恆定的,除了\(\textbf{c}_\textrm{light}\)可能會因陰影而減弱。平行光沒有位置。當然,實際的光源在空間中確實有特定的位置。平行光是抽象的,當到光的距離相對於場景尺寸很大時,可以得到很好的效果。例如,可以將20英尺外的探照燈照亮一個小型桌面西洋鏡,這可以表示為平行光。另一個例子是被太陽照亮的場景,幾乎任何場景都能用到,除非所討論的場景是諸如太陽系內行星之類的場景。

平行光的概念可以稍微擴充套件,允許在光方向\(\textbf{l}\)保持不變的情況下改變\(\textbf{c}_\textrm{light}\)的值。出於效能或創造性的原因,這通常用於將燈光效果繫結到場景的特定部分。例如,一個區域可以用兩個巢狀的(一個在另一個內)盒形體來定義,其中外盒外的\(\textbf{c}_\textrm{light}\)等於(0,0,0)(純黑色),內盒內部等於某個常數值,兩個盒子之間的區域中在其極值之間平滑地插值。

5.2.2 精確光源

與平行光不同,精確光源不是按時完成約會的光,而是具有位置的光。與現實世界的光源不同,這種光也沒有尺寸,沒有形狀或大小。我們使用來自拉丁文punctus的術語“punctual”(意思是“點”),表示由源自單個區域性位置的所有照明源組成的類。我們使用術語“點光源”來表示一種特定型別的發射體,它向所有方向均等地發光。因此,點光源和聚光燈是兩種不同形式的點光源。光方向向量\(\textbf{l}\)根據當前著色表面點\(\textbf{p}_0\)相對於點光源的位置\(\textbf{p} _\textrm{light}\)的位置而變化:

\[\textbf{l} = \frac{\textbf{p} _\textrm{light} - \textbf{p}_0}{||\textbf{p} _\textrm{light}-\textbf{p}_0||} \tag{5.9} \]

此等式是向量歸一化的一個示例:將向量除以其長度以生成指向同一方向的單位長度向量。這是另一種常見的著色操作,和我們在上一節中看到的著色操作一樣,它是大多數著色語言中的內建函式。但是,有時需要此操作的中間結果,這需要使用更基本的操作在多個步驟中明確地執行規範化。將此應用於精確光源方向計算,我們得到以下結果:

\[\begin{aligned} \textbf{d} &= \textbf{p} _\textrm{light} - \textbf{p}_0 \\ r &= \sqrt{\textbf{d}\cdot\textbf{d}}\\ \textbf{l} &= \frac{\textbf{d}}{r} \\ \end{aligned} \tag{5.10} \]

由於兩個向量的點積等於兩個向量的長度與其夾角餘弦的乘積,0°的餘弦為1.0,因此向量與其自身的點積是其長度的平方。因此,要找到任何向量的長度,我們只需將其與自身求點積並取結果的平方根。

我們需要的中間值是\(r\),即精確光源與當前著色點之間的距離。除了用於對光向量進行歸一化之外,還需要使用\(r\)的值來計算距離函式中光強\(\textbf{c}_\textrm{light}\)的衰減(變暗)。這將在下一節中進一步討論。

點光源/泛光燈

向各個方向均勻發光的精確光源稱為點光源或泛光燈。對於點光源,\(\textbf{c}_\textrm{light}\)隨著距離\(r\)的函式變化而變化,其唯一的變化來源是上述提到的距離衰減。與圖5.4中使用餘弦因子演示類似的幾何推理類似,圖5.5顯示了為什麼會發生這種衰減。在給定的表面上,來自點光源的光線之間的間距與從表面到光源的距離成正比。與圖5.4中的餘弦因子不同,這種間距增加發生在表面的兩個維度上,因此光線密度(以及光強\(\textbf{c}_\textrm{light}\))與平方反比距離\(1/r^2\)成正比。這使我們能夠使用單個光屬性\(\textbf{c}_{\textrm{light}_0}\)指定\(\textbf{c}_\textrm{light}\)的空間變化,\(\textbf{c}_{\textrm{light}_0}\)定義為\(\textbf{c}_\textrm{light}\)在固定參考距離\(r_0\)處的值:

\[\textbf{c}_\textrm{light}(r) = \textbf{c}_{\textrm{light}_0}(\frac{r_0}{r})^2 \tag{5.11} \]

圖5.5. 來自點光源的光線之間的間距與距離\(r\)成比例地增加。由於間距增加發生在二維中,光線的密度(以及光強度)與\(1/r^2\)成比例地減小。

公式 5.11 通常稱為反平方光衰減。儘管從技術上講,這是是點光源的正確距離衰減,但存在一些問題使該方程不太適合實際著色使用。

第一個問題發生在相對較小的距離處。隨著\(r\)的值趨於0,\(\textbf{c}_\textrm{light}\)的值將無界增加。當\(r\)達到0時,我們將有一個被零除的奇點。為了解決這個問題,一個常見的修改是在分母[861]上新增一個小值\(\epsilon\)

\[\textbf{c}_\textrm{light}(r) = \textbf{c}_{\textrm{light}_0}\frac{r_0^2}{r^2+\epsilon} \tag{5.12} \]

使用的\(\epsilon\)的確切值取決於應用;例如,Unreal遊戲引擎使用\(\epsilon = 1\ \textrm{cm}\)[861]。

在CryEngine[1591]和Frostbite[960]遊戲引擎中,使用了另一種修改,將\(r\)限制為最小值\(r_\textrm{min}\)

\[\textbf{c}_\textrm{light}(r) = \textbf{c}_{\textrm{light}_0}(\frac{r_0}{\textrm{max}(r,r_\textrm{min})})^2 \tag{5.13} \]

與前一種方法中使用的有點任意的\(\epsilon\)值不同,\(r_\textrm{min}\)的值具有物理解釋:發射光的物理物件的半徑。小於\(r_\textrm{min}\)\(r\)值對應於穿透物理光源內部的著色表面,這是不可能的。

相比之下,平方反比衰減的第二個問題發生在相對較大的距離處。問題不在於視覺效果,而在於效能。儘管光強度隨著距離不斷降低,但它永遠不會變為0。為了有效渲染,希望光在某個有限距離處達到0強度(第20章)。有許多不同的方法可以修改平方反比方程來實現這一點。理想情況下,修改應該引入儘可能少的變化。為了避免在光的影響邊界處出現突然截斷,最好還是使改進函式的導數和值在相同距離處達到0。一種解決方案是將平方反比方程乘以具有所需屬性的視窗函式。虛幻引擎[861]和Frostbite[960]遊戲引擎都使用了一個這樣的函式[860]:

\[f_\textrm{win}(r) = (1-(\frac{r}{r_\textrm{max}})^4)^{+2} \tag{5.14} \]

\(+2\)表示在平方之前將值(如果為負)夾鉗為0。圖5.6顯示了一個示例,平方反比方程曲線、公式5.14中的視窗函式以及將兩者相乘的結果。

圖5.6. 該圖顯示了平方反比曲線(使用\(\epsilon\)方法避免奇點,\(\epsilon\)值為1)、公式5.14中描述的視窗函式(\(r_\textrm{max}\)設定為3)和視窗曲線。

應用程式的要求將影響所用方法的選擇。例如,當距離衰減函式在相對較低的空間頻率(例如,在光照貼圖或每個頂點中)取樣時,在\(r_\textrm{max}\)處使導數等於0特別重要。CryEngine不使用光照貼圖或頂點光照,因此它採用了更簡單的調整,在\(0.8r_\textrm{max}\)\(r_\textrm{max}\)之間切換到線性衰減 [1591]。

對於某些應用程式,匹配反平方曲線不是優先事項,完全可以使用一些其他的函式。這有效地將方程5.11-5.14推廣到以下公式:

\[\textbf{c}_\textrm{light}(r) = \textbf{c}_{\textrm{light}_0}f_\textrm{dist}(r) \tag{5.15} \]

其中\(f_\textrm{dist}(r)\)是距離的某個函式。此類函式稱為距離衰減函式。在某些情況下,非平方反比衰減函式的使用是由效能限制驅動的。例如,遊戲《正當防衛2》需要計算成本極低的燈光。這決定了一個易於計算的衰減函式,同時也足夠平滑以避免每個頂點的光照偽影[1379]:

\[f_\textrm{dist}(r) = (1-(\frac{r}{r_\textrm{max}})^2)^{+2} \tag{5.16} \]

在其他情況下,衰減函式的選擇可能是出於創造性考慮。例如,用於現實遊戲和風格化遊戲的虛幻引擎有兩種光衰減模式:平方反比模式,如公式5.12中所述,以及指數衰減模式,可以進行調整以建立各種衰減曲線[1802]。遊戲《古墓麗影(2013)》的開發人員使用樣條編輯工具來創作衰減曲線[953],從而可以更好地控制曲線形狀。

聚光燈

與點光源不同,幾乎所有真實世界光源的照明都因方向和距離而異。這種變化可以表示為方向衰減函式\(f_\textrm{dir}(\textbf{l})\),它結合距離衰減函式來定義光強度的整體空間變化:

\[\textbf{c}_\textrm{light}(r) = \textbf{c}_{\textrm{light}_0}f_\textrm{dist}(r)f_\textrm{dir}(\textbf{l}) \tag{5.17} \]

\(f_\textrm{dir}(\textbf{l})\)的不同選擇可以產生不同的光照效果。一種重要的效果是聚光燈,它以圓錐體形式投射光線。聚光燈的方向衰減函式具有圍繞聚光燈方向向量\(\textbf{s}\)的旋轉對稱性,因此可以表示為\(\textbf{s}\)與到表面反向光向量\(-\textbf{l}\)之間的角度\(\theta_s\)的函式。需要反轉光向量,因為我們將表面上的\(\textbf{l}\)定義為指向光,而這裡我們需要指向遠離光的向量。

大多數聚光燈函式使用由\(\theta_s\)的餘弦組成的表示式,它(正如我們之前看到的)是著色中角度最常見的形式。聚光燈通常有一個本影角\(\theta_u\),它限制了光線,使得所有\(\theta_s ≥ \theta_u\)\(f_\textrm{dir}(\textbf{l})= 0\)。這個角度可以用於以類似於前面看到的最大衰減距離\(r_\textrm{max}\)的方式進行剔除。聚光燈的半影角 \(\theta_p\)也很常見,它定義了一個內錐體,其光線處於其最大強度。參見圖5.7。

圖5.7. 聚光燈:\(\theta_s\)是從光的定義方向\(\textbf{s}\)到向量\(-\textbf{l}\)(即到表面的方向)的角度;\(\theta_p\)表示半影;\(\theta_u\)表示光定義的本影角。

聚光燈使用了各種方向衰減函式,但它們往往大致相似。例如,函式\(f_{\textrm{dir}_\textrm{F}}(\textbf{l})\)用於Frostbite遊戲引擎[960],函式\(f_{\textrm{dir}_\textrm{T}}(\textbf{l})\)用於three.js瀏覽器圖形庫[218]:

\[\begin{aligned} t &= (\frac{\textrm{cos}{\theta}_s - \textrm{cos}{\theta}_u}{\textrm{cos}{\theta}_p - \textrm{cos}{\theta}_u})^{\mp} \\ f_{\textrm{dir}_\textrm{F}}(\textbf{l}) &= t^2\\ f_{\textrm{dir}_\textrm{T}}(\textbf{l}) &= \textrm{smoothstep}(t) = t^2(3-2t)\\ \end{aligned} \tag{5.18} \]

複習一下,\(x^{\mp}\)是我們在第1.2節中介紹的將\(x\)限制在0和1之間的符號。smoothstep函式是三次多項式,通常用於著色中的平滑插值。它是大多數著色語言的內建函式。

圖5.8顯示了我們迄今為止討論過的一些光型別。

圖5.8. 某些型別的燈光。從左到右:平行光、無衰減的點光源和平滑過渡的聚光燈。請注意,由於燈光和表面之間的角度變化,點光源向邊緣變暗。

其他精確光源

精確光源的\(\textbf{c}_\textrm{light}\)值可以通過許多其他方式改變。

\(f_\textrm{dir}(\textbf{l})\)函式不限於上面討論的簡單聚光燈衰減函式;它可以代表任何型別的方向變化,包括從現實世界的光源測量的複雜表格模式。照明工程學會(IES)已為此類測量定義了標準檔案格式。許多照明製造商都提供IES配置檔案,並已在遊戲《殺戮地帶:暗影墜落[379,380] 以及Unreal[861]和Frostbite[960]遊戲引擎等中使用。Lagarde對解析和使用此檔案格式相關的問題進行了很好的總結[961]。

遊戲《古墓麗影(2013)》[953]有一種準精確光源,它對沿x、y和z世界軸的距離應用獨立的衰減函式。在《古墓麗影》中,曲線也可用於隨時間改變光強度,例如,產生閃爍的手電筒。

第6.9節中,我們將討論如何通過使用紋理來改變光強度和顏色。

5.2.3 其他型別光

平行光和精確光的主要特徵在於光方向\(\textbf{l}\)的計算方式。可以使用其他方法來定義不同型別的燈光,以此計算燈光方向。例如,除了前面提到的光型別,《古墓麗影》還有膠囊燈,它使用線段而不是點作為光源[953]。對於每個著色畫素,到線段上最近點的方向作為光的方向\(\textbf{l}\)

只要著色器具有用於評估著色方程的\(\textbf{l}\)\(\textbf{c}_\textrm{light}\)值,就可以使用任何方法來計算這些值。

到目前為止討論的光型別是抽象的。在現實中,光源是有大小和形狀的,它們從多個方向照亮表面點。在渲染中,這種光被稱為區域光,它們在實時應用中的使用正在穩步增加。區域光渲染技術分為兩類:模擬由部分遮擋的區域光導致陰影邊緣的柔化(第7.1.2節)和模擬區域光在表面的著色效果(第10.1節)。第二類照明對於光滑、鏡面般的表面最為明顯,在這種情況下,可以從反射中清楚地辨別出光的形狀和大小。平行光和精確光源不太可能被廢棄,儘管它們不再像過去那樣無處不在。已經開發出計算光面積的近似值的技術,其實施成本相對較低,因此得到了更廣泛的使用。與過去相比,GPU效能的提高還允許採用更精細的技術。