版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.01.20 |
前言
OpenGL 圖形庫項(xiàng)目中一直也沒用過,最近也想學(xué)著使用這個(gè)圖形庫,感覺還是很有意思,也就自然想著好好的總結(jié)一下,希望對(duì)大家能有所幫助。下面內(nèi)容來自歡迎來到OpenGL的世界。
1. OpenGL 圖形庫使用(一) —— 概念基礎(chǔ)
2. OpenGL 圖形庫使用(二) —— 渲染模式、對(duì)象、擴(kuò)展和狀態(tài)機(jī)
3. OpenGL 圖形庫使用(三) —— 著色器、數(shù)據(jù)類型與輸入輸出
4. OpenGL 圖形庫使用(四) —— Uniform及更多屬性
5. OpenGL 圖形庫使用(五) —— 紋理
6. OpenGL 圖形庫使用(六) —— 變換
7. OpenGL 圖形庫的使用(七)—— 坐標(biāo)系統(tǒng)之五種不同的坐標(biāo)系統(tǒng)(一)
8. OpenGL 圖形庫的使用(八)—— 坐標(biāo)系統(tǒng)之3D效果(二)
9. OpenGL 圖形庫的使用(九)—— 攝像機(jī)(一)
10. OpenGL 圖形庫的使用(十)—— 攝像機(jī)(二)
11. OpenGL 圖形庫的使用(十一)—— 光照之顏色
12. OpenGL 圖形庫的使用(十二)—— 光照之基礎(chǔ)光照
13. OpenGL 圖形庫的使用(十三)—— 光照之材質(zhì)
14. OpenGL 圖形庫的使用(十四)—— 光照之光照貼圖
15. OpenGL 圖形庫的使用(十五)—— 光照之投光物
16. OpenGL 圖形庫的使用(十六)—— 光照之多光源
17. OpenGL 圖形庫的使用(十七)—— 光照之復(fù)習(xí)總結(jié)
18. OpenGL 圖形庫的使用(十八)—— 模型加載之Assimp
19. OpenGL 圖形庫的使用(十九)—— 模型加載之網(wǎng)格
20. OpenGL 圖形庫的使用(二十)—— 模型加載之模型
21. OpenGL 圖形庫的使用(二十一)—— 高級(jí)OpenGL之深度測試
22. OpenGL 圖形庫的使用(二十二)—— 高級(jí)OpenGL之模板測試Stencil testing
23. OpenGL 圖形庫的使用(二十三)—— 高級(jí)OpenGL之混合Blending
24. OpenGL 圖形庫的使用(二十四)—— 高級(jí)OpenGL之面剔除Face culling
25. OpenGL 圖形庫的使用(二十五)—— 高級(jí)OpenGL之幀緩沖Framebuffers
26. OpenGL 圖形庫的使用(二十六)—— 高級(jí)OpenGL之立方體貼圖Cubemaps
27. OpenGL 圖形庫的使用(二十七)—— 高級(jí)OpenGL之高級(jí)數(shù)據(jù)Advanced Data
28. OpenGL 圖形庫的使用(二十八)—— 高級(jí)OpenGL之高級(jí)GLSL Advanced GLSL
29. OpenGL 圖形庫的使用(二十九)—— 高級(jí)OpenGL之幾何著色器Geometry Shader
30. OpenGL 圖形庫的使用(三十)—— 高級(jí)OpenGL之實(shí)例化Instancing
31. OpenGL 圖形庫的使用(三十一)—— 高級(jí)OpenGL之抗鋸齒Anti Aliasing
32. OpenGL 圖形庫的使用(三十二)—— 高級(jí)光照之高級(jí)光照Advanced Lighting
33. OpenGL 圖形庫的使用(三十三)—— 高級(jí)光照之Gamma校正Gamma Correction
34. OpenGL 圖形庫的使用(三十四)—— 高級(jí)光照之陰影 - 陰影映射Shadow Mapping
35. OpenGL 圖形庫的使用(三十五)—— 高級(jí)光照之陰影 - 點(diǎn)陰影Point Shadows
36. OpenGL 圖形庫的使用(三十六)—— 高級(jí)光照之法線貼圖Normal Mapping
37. OpenGL 圖形庫的使用(三十七)—— 高級(jí)光照之視差貼圖Parallax Mapping
38. OpenGL 圖形庫的使用(三十八)—— 高級(jí)光照之HDR
39. OpenGL 圖形庫的使用(三十九)—— 高級(jí)光照之泛光
40. OpenGL 圖形庫的使用(四十)—— 高級(jí)光照之延遲著色法Deferred Shading
41. OpenGL 圖形庫的使用(四十一)—— 高級(jí)光照之SSAO
理論
注意: 作者正在對(duì)PBR章節(jié)進(jìn)行大的調(diào)整,原文的內(nèi)容時(shí)時(shí)可能有更新,建議仍是閱讀原文。
PBR,或者用更通俗一些的稱呼是指基于物理的渲染(Physically Based Rendering)
,它指的是一些在不同程度上都基于與現(xiàn)實(shí)世界的物理原理更相符的基本理論所構(gòu)成的渲染技術(shù)的集合。正因?yàn)榛谖锢淼匿秩灸康谋闶菫榱耸褂靡环N更符合物理學(xué)規(guī)律的方式來模擬光線,因此這種渲染方式與我們?cè)瓉淼腜hong或者Blinn-Phong光照算法相比總體上看起來要更真實(shí)一些。除了看起來更好些以外,由于它與物理性質(zhì)非常接近,因此我們(尤其是美術(shù)師們)可以直接以物理參數(shù)為依據(jù)來編寫表面材質(zhì),而不必依靠粗劣的修改與調(diào)整來讓光照效果看上去正常。使用基于物理參數(shù)的方法來編寫材質(zhì)還有一個(gè)更大的好處,就是不論光照條件如何,這些材質(zhì)看上去都會(huì)是正確的,而在非PBR的渲染管線當(dāng)中有些東西就不會(huì)那么真實(shí)了。
雖然如此,基于物理的渲染仍然只是對(duì)基于物理原理的現(xiàn)實(shí)世界的一種近似,這也就是為什么它被稱為基于物理的著色(Physically based Shading)
而非物理著色(Physical Shading)
的原因。判斷一種PBR光照模型是否是基于物理的,必須滿足以下三個(gè)條件(不用擔(dān)心,我們很快就會(huì)了解它們的):
- 基于微平面
(Microfacet)
的表面模型。 - 能量守恒。
- 應(yīng)用基于物理的BRDF。
在這次的PBR系列教程之中,我們將會(huì)把重點(diǎn)放在最先由迪士尼(Disney)提出探討并被Epic Games首先應(yīng)用于實(shí)時(shí)渲染的PBR方案。他們基于金屬質(zhì)地工作流(Metallic Workflow)
的方案有非常完備的文獻(xiàn)記錄,廣泛應(yīng)用于各種流行的引擎之中并且有著非常令人驚嘆的視覺效果。完成這次的教程之后我們將會(huì)制作出類似于這樣的一些東西:
注意這次的理論教程是一個(gè)三部曲系列(目前還在創(chuàng)作中)的一部分,所以這部分教程的內(nèi)容有可能隨著其他兩部還沒完成的教程的進(jìn)展而發(fā)生變化。同樣,這部教程的源代碼也并不完整,所以上面的圖片是取自一個(gè)私人項(xiàng)目之中,而非本教程的代碼所生成的。不過在完成之后這個(gè)圖片看上去應(yīng)該會(huì)好很多。
請(qǐng)注意這個(gè)系列的教程所探討的內(nèi)容屬于相當(dāng)高端的領(lǐng)域,因此要求讀者對(duì)OpenGL和著色器光照有較好的理解。讀者將會(huì)需要這些相關(guān)的知識(shí):幀緩沖,立方體貼圖,Gamma校正,HDR和法線貼圖。我們還會(huì)深入探討一些高等數(shù)學(xué)的內(nèi)容,我會(huì)盡我所能將相關(guān)的概念闡述清楚。
微平面模型
所有的PBR技術(shù)都基于微平面理論。這項(xiàng)理論認(rèn)為,達(dá)到微觀尺度之后任何平面都可以用被稱為微平面(Microfacets)
的細(xì)小鏡面來進(jìn)行描繪。根據(jù)平面粗糙程度的不同,這些細(xì)小鏡面的取向排列可以相當(dāng)不一致:
產(chǎn)生的效果就是:一個(gè)平面越是粗糙,這個(gè)平面上的微平面的排列就越混亂。這些微小鏡面這樣無序取向排列的影響就是,當(dāng)我們特指鏡面光/鏡面反射時(shí),入射光線更趨向于向完全不同的方向發(fā)散(Scatter)開來,進(jìn)而產(chǎn)生出分布范圍更廣泛的鏡面反射。而與之相反的是,對(duì)于一個(gè)光滑的平面,光線大體上會(huì)更趨向于向同一個(gè)方向反射,造成更小更銳利的反射:
在微觀尺度下,沒有任何平面是完全光滑的。然而由于這些微平面已經(jīng)微小到無法逐像素的繼續(xù)對(duì)其進(jìn)行區(qū)分,因此我們只有假設(shè)一個(gè)粗糙度(Roughness)參數(shù),然后用統(tǒng)計(jì)學(xué)的方法來概略的估算微平面的粗糙程度。我們可以基于一個(gè)平面的粗糙度來計(jì)算出某個(gè)向量的方向與微平面平均取向方向一致的概率。這個(gè)向量便是位于光線向量l
和視線向量v
之間的中間向量(Halfway Vector)
。我們?cè)?jīng)在之前的高級(jí)光照教程中談到過中間向量,它的計(jì)算方法如下:
微平面的取向方向與中間向量的方向越是一致,鏡面反射的效果就越是強(qiáng)烈越是銳利。然后再加上一個(gè)介于0到1之間的粗糙度參數(shù),這樣我們就能概略的估算微平面的取向情況了:
我們可以看到,較高的粗糙度值顯示出來的鏡面反射的輪廓要更大一些。與之相反地,較小的粗糙值顯示出的鏡面反射輪廓?jiǎng)t更小更銳利。
能量守恒
微平面近似法使用了這樣一種形式的能量守恒(Energy Conservation)
:出射光線的能量永遠(yuǎn)不能超過入射光線的能量(發(fā)光面除外)。如圖示我們可以看到,隨著粗糙度的上升鏡面反射區(qū)域的會(huì)增加,但是鏡面反射的亮度卻會(huì)下降。如果不管反射輪廓的大小而讓每個(gè)像素的鏡面反射強(qiáng)度(Specular Intensity)
都一樣的話,那么粗糙的平面就會(huì)放射出過多的能量,而這樣就違背了能量守恒定律。這也就是為什么正如我們看到的一樣,光滑平面的鏡面反射更強(qiáng)烈而粗糙平面的反射更昏暗。
為了遵守能量守恒定律,我們需要對(duì)漫反射光和鏡面反射光之間做出明確的區(qū)分。當(dāng)一束光線碰撞到一個(gè)表面的時(shí)候,它就會(huì)分離成一個(gè)折射部分和一個(gè)反射部分。反射部分就是會(huì)直接反射開來而不會(huì)進(jìn)入平面的那部分光線,這就是我們所說的鏡面光照。而折射部分就是余下的會(huì)進(jìn)入表面并被吸收的那部分光線,這也就是我們所說的漫反射光照。
這里還有一些細(xì)節(jié)需要處理,因?yàn)楫?dāng)光線接觸到一個(gè)表面的時(shí)候折射光是不會(huì)立即就被吸收的。通過物理學(xué)我們可以得知,光線實(shí)際上可以被認(rèn)為是一束沒有耗盡就不停向前運(yùn)動(dòng)的能量,而光束是通過碰撞的方式來消耗能量。每一種材料都是由無數(shù)微小的粒子所組成,這些粒子都能如下圖所示一樣與光線發(fā)生碰撞。這些粒子在每次的碰撞中都可以吸收光線所攜帶的一部分或者是全部的能量而后轉(zhuǎn)變成為熱量。
一般來說,并非所有能量都會(huì)被全部吸收,而光線也會(huì)繼續(xù)沿著(基本上)隨機(jī)的方向發(fā)散,然后再和其他的粒子碰撞直至能量完全耗盡或者再次離開這個(gè)表面。而光線脫離物體表面后將會(huì)協(xié)同構(gòu)成該表面的(漫反射)顏色。不過在基于物理的渲染之中我們進(jìn)行了簡化,假設(shè)對(duì)平面上的每一點(diǎn)所有的折射光都會(huì)被完全吸收而不會(huì)散開。而有一些被稱為次表面散射(Subsurface Scattering)
技術(shù)的著色器技術(shù)將這個(gè)問題考慮了進(jìn)去,它們顯著的提升了一些諸如皮膚,大理石或者蠟質(zhì)這樣材質(zhì)的視覺效果,不過伴隨而來的則是性能下降代價(jià)。
對(duì)于金屬(Metallic)
表面,當(dāng)討論到反射與折射的時(shí)候還有一個(gè)細(xì)節(jié)需要注意。金屬表面對(duì)光的反應(yīng)與非金屬材料還有電介質(zhì)(Dielectrics)
材料表面相比是不同的。它們遵從的反射與折射原理是相同的,但是所有的折射光都會(huì)被直接吸收而不會(huì)散開,只留下反射光或者說鏡面反射光。亦即是說,金屬表面不會(huì)顯示出漫反射顏色。由于金屬與電介質(zhì)之間存在這樣明顯的區(qū)別,因此它們兩者在PBR渲染管線中被區(qū)別處理,而我們將在文章的后面進(jìn)一步詳細(xì)探討這個(gè)問題。
反射光與折射光之間的這個(gè)區(qū)別使我們得到了另一條關(guān)于能量守恒的經(jīng)驗(yàn)結(jié)論:反射光與折射光它們二者之間是互斥的關(guān)系。無論何種光線,其被材質(zhì)表面所反射的能量將無法再被材質(zhì)吸收。因此,諸如折射光這樣的余下的進(jìn)入表面之中的能量正好就是我們計(jì)算完反射之后余下的能量。
我們按照能量守恒的關(guān)系,首先計(jì)算鏡面反射部分,它的值等于入射光線被反射的能量所占的百分比。然后折射光部分就可以直接由鏡面反射部分計(jì)算得出:
float kS = calculateSpecularComponent(...); // 反射/鏡面 部分
float kD = 1.0 - ks; // 折射/漫反射 部分
這樣我們就能在遵守能量守恒定律的前提下知道入射光線的反射部分與折射部分所占的總量了。按照這種方法折射/漫反射與反射/鏡面反射所占的份額都不會(huì)超過1.0,如此就能保證它們的能量總和永遠(yuǎn)不會(huì)超過入射光線的能量。而這些都是我們?cè)谇懊娴墓庹战坛讨袥]有考慮的問題。
反射率方程
在這里我們引入了一種被稱為渲染方程(Render Equation)的東西。它是某些聰明絕頂人所構(gòu)想出來的一個(gè)精妙的方程式,是如今我們所擁有的用來模擬光的視覺效果最好的模型。基于物理的渲染所堅(jiān)定的遵循的是一種被稱為反射率方程(The Reflectance Equation)
的渲染方程的特化版本。要正確的理解PBR 很重要的一點(diǎn)就是要首先透徹的理解反射率方程:
反射率方程一開始可能會(huì)顯得有些嚇人,不過隨著我們慢慢對(duì)其進(jìn)行剖析,讀者最終會(huì)逐漸理解它的。要正確的理解這個(gè)方程式,我們必須要稍微涉足一些輻射度量學(xué)(Radiometry)的內(nèi)容。輻射度量學(xué)是一種用來度量電磁場輻射(包括可見光)的手段。有很多種輻射度量(radiometric quantities)
可以用來測量曲面或者某個(gè)方向上的光,但是我們將只會(huì)討論其中和反射率方程有關(guān)的一種。它被稱為輻射率(Radiance),在這里用LL來表示。輻射率被用來量化單一方向上發(fā)射來的光線的大小或者強(qiáng)度。由于輻射率是由許多物理變量集合而成的,一開始理解起來可能有些困難,因此我們首先關(guān)注一下這些物理量:
輻射通量:輻射通量Φ表示的是一個(gè)光源所輸出的能量,以瓦特為單位。光是由多種不同波長的能量所集合而成的,而每種波長則與一種特定的(可見的)顏色相關(guān)。因此一個(gè)光源所放射出來的能量可以被視作這個(gè)光源包含的所有各種波長的一個(gè)函數(shù)。波長介于390nm到700nm(納米)的光被認(rèn)為是處于可見光光譜中,也就是說它們是人眼可見的波長。在下面你可以看到一幅圖片,里面展示了日光中不同波長的光所具有的能量:
輻射通量將會(huì)計(jì)算這個(gè)由不同波長構(gòu)成的函數(shù)的總面積。直接將這種對(duì)不同波長的計(jì)量作為參數(shù)輸入計(jì)算機(jī)圖形有一些不切實(shí)際,因此我們通常不直接使用波長的強(qiáng)度而是使用三原色編碼,也就是RGB(或者按通常的稱呼:光色)來作為輻射通量表示的簡化。這套編碼確實(shí)會(huì)帶來一些信息上的損失,但是這對(duì)于視覺效果上的影響基本可以忽略。
立體角:立體角用ωω表示,它可以為我們描述投射到單位球體上的一個(gè)截面的大小或者面積。投射到這個(gè)單位球體上的截面的面積就被稱為立體角(Solid Angle)
,你可以把立體角想象成為一個(gè)帶有體積的方向:
可以把自己想象成為一個(gè)站在單位球面的中心的觀察者,向著投影的方向看。這個(gè)投影輪廓的大小就是立體角。
輻射強(qiáng)度:輻射強(qiáng)度(Radiant Intensity)
表示的是在單位球面上,一個(gè)光源向每單位立體角所投送的輻射通量。舉例來說,假設(shè)一個(gè)全向光源向所有方向均勻的輻射能量,輻射強(qiáng)度就能幫我們計(jì)算出它在一個(gè)單位面積(立體角)內(nèi)的能量大小:
計(jì)算輻射強(qiáng)度的公式如下所示:
其中I表示輻射通量Φ除以立體角ω。
在理解了輻射通量,輻射強(qiáng)度與立體角的概念之后,我們終于可以開始討論輻射率的方程式了。這個(gè)方程表示的是,一個(gè)擁有輻射強(qiáng)度Φ的光源在單位面積A,單位立體角ω上的輻射出的總能量:
輻射率是輻射度量學(xué)上表示一個(gè)區(qū)域平面上光線總量的物理量,它受到入射(Incident)
(或者來射)光線與平面法線間的夾角θ的余弦值cosθ的影響:當(dāng)直接輻射到平面上的程度越低時(shí),光線就越弱,而當(dāng)光線完全垂直于平面時(shí)強(qiáng)度最高。這和我們?cè)谇懊娴?a target="_blank" rel="nofollow">基礎(chǔ)光照教程中對(duì)于漫反射光照的概念相似,其中cosθ就直接對(duì)應(yīng)于光線的方向向量和平面法向量的點(diǎn)積:
float cosTheta = dot(lightDir, N);
輻射率方程很有用,因?yàn)樗汛蟛糠治覀兏信d趣的物理量都包含了進(jìn)去。如果我們把立體角ω和面積A看作是無窮小的,那么我們就能用輻射率來表示單束光線穿過空間中的一個(gè)點(diǎn)的通量。這就使我們可以計(jì)算得出作用于單個(gè)(片段)點(diǎn)上的單束光線的輻射率,我們實(shí)際上把立體角ω轉(zhuǎn)變?yōu)榉较蛳蛄喀厝缓蟀衙鍭轉(zhuǎn)換為點(diǎn)p。這樣我們就能直接在我們的著色器中使用輻射率來計(jì)算單束光線對(duì)每個(gè)片段的作用了。
事實(shí)上,當(dāng)涉及到輻射率時(shí),我們通常關(guān)心的是所有投射到點(diǎn)p上的光線的總和,而這個(gè)和就稱為輻射照度或者輻照度(Irradiance)
。在理解了輻射率和輻照度的概念之后,讓我們?cè)倩剡^頭來看看反射率方程:
我們知道在渲染方程中L代表通過某個(gè)無限小的立體角ωi在某個(gè)點(diǎn)上的輻射率,而立體角可以視作是入射方向向量ωi。注意我們利用光線和平面間的入射角的余弦值cos?θ來計(jì)算能量,亦即從輻射率公式L轉(zhuǎn)化至反射率公式時(shí)的n?ωi。用ωo表示觀察方向,也就是出射方向,反射率公式計(jì)算了點(diǎn)p在ωo方向上被反射出來的輻射率Lo(p,ωo)的總和。或者換句話說:Lo表示了從ωo方向上觀察,光線投射到點(diǎn)p上反射出來的輻照度。
基于反射率公式是圍繞所有入射輻射率的總和,也就是輻照度來計(jì)算的,所以我們需要計(jì)算的就不只是是單一的一個(gè)方向上的入射光,而是一個(gè)以點(diǎn)p為球心的半球領(lǐng)域Ω內(nèi)所有方向上的入射光。一個(gè)半球領(lǐng)域(Hemisphere)
可以描述為以平面法線n為軸所環(huán)繞的半個(gè)球體:
為了計(jì)算某些面積的值,或者像是在半球領(lǐng)域的問題中計(jì)算某一個(gè)體積的時(shí)候我們會(huì)需要用到一種稱為積分(Integral)的數(shù)學(xué)手段,也就是反射率公式中的符號(hào)∫∫,它的運(yùn)算包含了半球領(lǐng)域Ω內(nèi)所有入射方向上的dωi 。積分運(yùn)算的值等于一個(gè)函數(shù)曲線的面積,它的計(jì)算結(jié)果要么是解析解要么就是數(shù)值解。由于渲染方程和反射率方程都沒有解析解,我們將會(huì)用離散的方法來求得這個(gè)積分的數(shù)值解。這個(gè)問題就轉(zhuǎn)化為,在半球領(lǐng)域Ω中按一定的步長將反射率方程分散求解,然后再按照步長大小將所得到的結(jié)果平均化。這種方法被稱為黎曼和(Riemann sum)
,我們可以用下面的代碼粗略的演示一下:
int steps = 100;
float sum = 0.0f;
vec3 P = ...;
vec3 Wo = ...;
vec3 N = ...;
float dW = 1.0f / steps;
for(int i = 0; i < steps; ++i)
{
vec3 Wi = getNextIncomingLightDir(i);
sum += Fr(p, Wi, Wo) * L(p, Wi) * dot(N, Wi) * dW;
}
通過利用dW來對(duì)所有離散部分進(jìn)行縮放,其和最后就等于積分函數(shù)的總面積或者總體積。這個(gè)用來對(duì)每個(gè)離散步長進(jìn)行縮放的dW可以認(rèn)為就是反射率方程中的dωi 。在數(shù)學(xué)上,用來計(jì)算積分的dωi 表示的是一個(gè)連續(xù)的符號(hào),而我們使用的dW在代碼中和它并沒有直接的聯(lián)系(因?yàn)樗淼氖抢杪椭械碾x散步長),這樣說是為了可以幫助你理解。請(qǐng)牢記,使用離散步長得到的是函數(shù)總面積的一個(gè)近似值。細(xì)心的讀者可能已經(jīng)注意到了,我們可以通過增加離散部分的數(shù)量來提高黎曼和的準(zhǔn)確度(Accuracy)。
反射率方程概括了在半球領(lǐng)域Ω<內(nèi),碰撞到了點(diǎn)p上的所有入射方向ωi上的光線的輻射率,并受到fr的約束,然后返回觀察方向上反射光的Lo。正如我們所熟悉的那樣,入射光輻射率可以由光源處獲得,此外還可以利用一個(gè)環(huán)境貼圖來測算所有入射方向上的輻射率,我們將在未來的IBL教程中討論這個(gè)方法。
現(xiàn)在唯一剩下的未知符號(hào)就是fr了,它被稱為BRDF,或者雙向反射分布函數(shù)(Bidirectional Reflective Distribution Function)
,它的作用是基于表面材質(zhì)屬性來對(duì)入射輻射率進(jìn)行縮放或者加權(quán)。
BRDF
BRDF,或者說雙向反射分布函數(shù),它接受入射(光)方向ωi,出射(觀察)方向ωo,平面法線nn以及一個(gè)用來表示微平面粗糙程度的參數(shù)aa作為函數(shù)的輸入?yún)?shù)。BRDF可以近似的求出每束光線對(duì)一個(gè)給定了材質(zhì)屬性的平面上最終反射出來的光線所作出的貢獻(xiàn)程度。舉例來說,如果一個(gè)平面擁有完全光滑的表面(比如鏡面),那么對(duì)于所有的入射光線ωi(除了一束以外)而言BRDF函數(shù)都會(huì)返回0.0 ,只有一束與出射光線ωo擁有相同(被反射)角度的光線會(huì)得到1.0這個(gè)返回值。
BRDF基于我們之前所探討過的微平面理論來近似的求得材質(zhì)的反射與折射屬性。對(duì)于一個(gè)BRDF,為了實(shí)現(xiàn)物理學(xué)上的可信度,它必須遵守能量守恒定律,也就是說反射光線的總和永遠(yuǎn)不能超過入射光線的總量。嚴(yán)格上來說,同樣采用ωi和ωo作為輸入?yún)?shù)的 Blinn-Phong光照模型也被認(rèn)為是一個(gè)BRDF。然而由于Blinn-Phong模型并沒有遵循能量守恒定律,因此它不被認(rèn)為是基于物理的渲染。現(xiàn)在已經(jīng)有很好幾種BRDF都能近似的得出物體表面對(duì)于光的反應(yīng),但是幾乎所有實(shí)時(shí)渲染管線使用的都是一種被稱為Cook-Torrance BRDF
模型。
Cook-Torrance BRDF兼有漫反射和鏡面反射兩個(gè)部分:
這里的kd是早先提到過的入射光線中被折射部分的能量所占的比率,而ksks是被反射部分的比率。BRDF的左側(cè)表示的是漫反射部分,這里用flambert來表示。它被稱為Lambertian漫反射,這和我們之前在漫反射著色中使用的常數(shù)因子類似,用如下的公式來表示:
c表示表面顏色(回想一下漫反射表面紋理)。除以π是為了對(duì)漫反射光進(jìn)行標(biāo)準(zhǔn)化,因?yàn)榍懊婧蠦RDF的積分方程是受π影響的(我們會(huì)在IBL的教程中探討這個(gè)問題的)。
你也許會(huì)感到好奇,這個(gè)Lambertian漫反射和我們之前經(jīng)常使用的漫反射到底有什么關(guān)系:之前我們是用表面法向量與光照方向向量進(jìn)行點(diǎn)乘,然后再將結(jié)果與平面顏色相乘得到漫反射參數(shù)。點(diǎn)乘依然還在,但是卻不在BRDF之內(nèi),而是轉(zhuǎn)變成為了Lo積分末公式末尾處的n?ωi 。
目前存在著許多不同類型的模型來實(shí)現(xiàn)BRDF的漫反射部分,大多看上去都相當(dāng)真實(shí),但是相應(yīng)的運(yùn)算開銷也非常的昂貴。不過按照Epic公司給出的結(jié)論,Lambertian漫反射模型已經(jīng)足夠應(yīng)付大多數(shù)實(shí)時(shí)渲染的用途了。
BRDF的鏡面反射部分要稍微更高級(jí)一些,它的形式如下所示:
Cook-Torrance BRDF
的鏡面反射部分包含三個(gè)函數(shù),此外分母部分還有一個(gè)標(biāo)準(zhǔn)化因子 。字母D,F(xiàn)與G分別代表著一種類型的函數(shù),各個(gè)函數(shù)分別用來近似的計(jì)算出表面反射特性的一個(gè)特定部分。三個(gè)函數(shù)分別為正態(tài)分布函數(shù)(Normal Distribution Function)
,菲涅爾方程(Fresnel Rquation)
和幾何函數(shù)(Geometry Function)
:
- 正態(tài)分布函數(shù):估算在受到表面粗糙度的影響下,取向方向與中間向量一致的微平面的數(shù)量。這是用來估算微平面的主要函數(shù)。
- 幾何函數(shù):描述了微平面自成陰影的屬性。當(dāng)一個(gè)平面相對(duì)比較粗糙的時(shí)候,平面表面上的微平面有可能擋住其他的微平面從而減少表面所反射的光線。
- 菲涅爾方程:菲涅爾方程描述的是在不同的表面角下表面所反射的光線所占的比率。
以上的每一種函數(shù)都是用來估算相應(yīng)的物理參數(shù)的,而且你會(huì)發(fā)現(xiàn)用來實(shí)現(xiàn)相應(yīng)物理機(jī)制的每種函數(shù)都有不止一種形式。它們有的非常真實(shí),有的則性能高效。你可以按照自己的需求任意選擇自己想要的函數(shù)的實(shí)現(xiàn)方法。英佩游戲公司的Brian Karis對(duì)于這些函數(shù)的多種近似實(shí)現(xiàn)方式進(jìn)行了大量的研究。我們將會(huì)采用Epic Games
在Unreal Engine 4
中所使用的函數(shù),其中D使用Trowbridge-Reitz GGX
,F(xiàn)使用Fresnel-Schlick
近似(Fresnel-Schlick Approximation)
,而G使用Smith’s Schlick-GGX
。
1. 正態(tài)分布函數(shù)
正態(tài)分布函數(shù)D,或者說鏡面分布,從統(tǒng)計(jì)學(xué)上近似的表示了與某些(中間)向量h取向一致的微平面的比率。舉例來說,假設(shè)給定向量h,如果我們的微平面中有35%與向量hh取向一致,則正態(tài)分布函數(shù)或者說NDF將會(huì)返回0.35。目前有很多種NDF都可以從統(tǒng)計(jì)學(xué)上來估算微平面的總體取向度,只要給定一些粗糙度的參數(shù)以及一個(gè)我們馬上將會(huì)要用到的參數(shù)Trowbridge-Reitz GGX
:
在這里h表示用來與平面上微平面做比較用的中間向量,而a表示表面粗糙度。
如果我們把h當(dāng)成是不同粗糙度參數(shù)下,平面法向量和光線方向向量之間的中間向量的話,我們可以得到如下圖示的效果:
當(dāng)粗糙度很低(也就是說表面很光滑)的時(shí)候,與中間向量取向一致的微平面會(huì)高度集中在一個(gè)很小的半徑范圍內(nèi)。由于這種集中性,NDF最終會(huì)生成一個(gè)非常明亮的斑點(diǎn)。但是當(dāng)表面比較粗糙的時(shí)候,微平面的取向方向會(huì)更加的隨機(jī)。你將會(huì)發(fā)現(xiàn)與hh向量取向一致的微平面分布在一個(gè)大得多的半徑范圍內(nèi),但是同時(shí)較低的集中性也會(huì)讓我們的最終效果顯得更加灰暗。
使用GLSL代碼編寫的Trowbridge-Reitz GGX
正態(tài)分布函數(shù)是下面這個(gè)樣子的:
float D_GGX_TR(vec3 N, vec3 H, float a)
{
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
2. 幾何函數(shù)
幾何函數(shù)從統(tǒng)計(jì)學(xué)上近似的求得了微平面間相互遮蔽的比率,這種相互遮蔽會(huì)損耗光線的能量。
與NDF類似,幾何函數(shù)采用一個(gè)材料的粗糙度參數(shù)作為輸入?yún)?shù),粗糙度較高的表面其微平面間相互遮蔽的概率就越高。我們將要使用的幾何函數(shù)是GGX與Schlick-Beckmann
近似的結(jié)合體,因此又稱為Schlick-GGX
:
這里的k是α基于幾何函數(shù)是針對(duì)直接光照還是針對(duì)IBL光照的重映射(Remapping)
:
注意,根據(jù)你的引擎把粗糙度轉(zhuǎn)化為α的方式不同,得到α的值也有可能不同。在接下來的教程中,我們將會(huì)廣泛的討論這個(gè)重映射是如何起作用的。
為了有效的估算幾何部分,需要將觀察方向(幾何遮蔽(Geometry Obstruction)
)和光線方向向量(幾何陰影(Geometry Shadowing)
)都考慮進(jìn)去。我們可以使用史密斯法(Smith’s method)
來把兩者都納入其中:
使用史密斯法與Schlick-GGX
作為Gsub
可以得到如下所示不同粗糙度的視覺效果:
幾何函數(shù)是一個(gè)值域?yàn)閇0.0, 1.0]的乘數(shù),其中白色或者說1.0表示沒有微平面陰影,而黑色或者說0.0則表示微平面徹底被遮蔽。
使用GLSL編寫的幾何函數(shù)代碼如下:
float GeometrySchlickGGX(float NdotV, float k)
{
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float k)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx1 = GeometrySchlickGGX(NdotV, k);
float ggx2 = GeometrySchlickGGX(NdotL, k);
return ggx1 * ggx2;
}
3. 菲涅爾方程
菲涅爾(發(fā)音為Freh-nel)方程描述的是被反射的光線對(duì)比光線被折射的部分所占的比率,這個(gè)比率會(huì)隨著我們觀察的角度不同而不同。當(dāng)光線碰撞到一個(gè)表面的時(shí)候,菲涅爾方程會(huì)根據(jù)觀察角度告訴我們被反射的光線所占的百分比。利用這個(gè)反射比率和能量守恒原則,我們可以直接得出光線被折射的部分以及光線剩余的能量。
當(dāng)垂直觀察的時(shí)候,任何物體或者材質(zhì)表面都有一個(gè)基礎(chǔ)反射率(Base Reflectivity)
,但是如果以一定的角度往平面上看的時(shí)候所有反光都會(huì)變得明顯起來。你可以自己嘗試一下,用垂直的視角觀察你自己的木制/金屬桌面,此時(shí)一定只有最基本的反射性。但是如果你從近乎90度(譯注:應(yīng)該是指和法線的夾角)的角度觀察的話反光就會(huì)變得明顯的多。如果從理想的90度視角觀察,所有的平面理論上來說都能完全的反射光線。這種現(xiàn)象因菲涅爾而聞名,并體現(xiàn)在了菲涅爾方程之中。
菲涅爾方程是一個(gè)相當(dāng)復(fù)雜的方程式,不過幸運(yùn)的是菲涅爾方程可以用Fresnel-Schlick
近似法求得近似解:
F0表示平面的基礎(chǔ)反射率,它是利用所謂折射指數(shù)(Indices of Refraction)
或者說IOR計(jì)算得出的。然后正如你可以從球體表面看到的那樣,我們?cè)绞浅蛎媛咏堑姆较蛏峡矗ù藭r(shí)視線和表面法線的夾角接近90度)菲涅爾現(xiàn)象就越明顯,反光就越強(qiáng):
菲涅爾方程還存在一些細(xì)微的問題。其中一個(gè)問題是Fresnel-Schlick
近似僅僅對(duì)電介質(zhì)或者說非金屬表面有定義。對(duì)于導(dǎo)體(Conductor)表面(金屬),使用它們的折射指數(shù)計(jì)算基礎(chǔ)折射率并不能得出正確的結(jié)果,這樣我們就需要使用一種不同的菲涅爾方程來對(duì)導(dǎo)體表面進(jìn)行計(jì)算。由于這樣很不方便,所以我們預(yù)先計(jì)算出平面對(duì)于法向入射(F0)的反應(yīng)(處于0度角,好像直接看向表面一樣)然后基于相應(yīng)觀察角的Fresnel-Schlick近似對(duì)這個(gè)值進(jìn)行插值,用這種方法來進(jìn)行進(jìn)一步的估算。這樣我們就能對(duì)金屬和非金屬材質(zhì)使用同一個(gè)公式了。
平面對(duì)于法向入射的響應(yīng)或者說基礎(chǔ)反射率可以在一些大型數(shù)據(jù)庫中找到,比如這個(gè)。下面列舉的這一些常見數(shù)值就是從Naty Hoffman的課程講義中所得到的:
這里可以觀察到的一個(gè)有趣的現(xiàn)象,所有電介質(zhì)材質(zhì)表面的基礎(chǔ)反射率都不會(huì)高于0.17,這其實(shí)是例外而非普遍情況。導(dǎo)體材質(zhì)表面的基礎(chǔ)反射率起點(diǎn)更高一些并且(大多)在0.5和1.0之間變化。此外,對(duì)于導(dǎo)體或者金屬表面而言基礎(chǔ)反射率一般是帶有色彩的,這也是為什么F0F0要用RGB三原色來表示的原因(法向入射的反射率可隨波長不同而不同)。這種現(xiàn)象我們只能在金屬表面觀察的到。
金屬表面這些和電介質(zhì)表面相比所獨(dú)有的特性引出了所謂的金屬工作流的概念。也就是我們需要額外使用一個(gè)被稱為金屬度(Metalness)的參數(shù)來參與編寫表面材質(zhì)。金屬度用來描述一個(gè)材質(zhì)表面是金屬還是非金屬的。
理論上來說,一個(gè)表面的金屬度應(yīng)該是二元的:要么是金屬要么不是金屬,不能兩者皆是。但是,大多數(shù)的渲染管線都允許在0.0至1.0之間線性的調(diào)配金屬度。這主要是由于材質(zhì)紋理精度不足以描述一個(gè)擁有諸如細(xì)沙/沙狀粒子/刮痕的金屬表面。通過對(duì)這些小的類非金屬粒子/刮痕調(diào)整金屬度值,我們可以獲得非常好看的視覺效果。
通過預(yù)先計(jì)算電介質(zhì)與導(dǎo)體的F0F0值,我們可以對(duì)兩種類型的表面使用相同的Fresnel-Schlick
近似,但是如果是金屬表面的話就需要對(duì)基礎(chǔ)反射率添加色彩。我們一般是按下面這個(gè)樣子來實(shí)現(xiàn)的:
vec3 F0 = vec3(0.04);
F0 = mix(F0, surfaceColor.rgb, metalness);
我們?yōu)榇蠖鄶?shù)電介質(zhì)表面定義了一個(gè)近似的基礎(chǔ)反射率。F0取最常見的電解質(zhì)表面的平均值,這又是一個(gè)近似值。不過對(duì)于大多數(shù)電介質(zhì)表面而言使用0.04作為基礎(chǔ)反射率已經(jīng)足夠好了,而且可以在不需要輸入額外表面參數(shù)的情況下得到物理可信的結(jié)果。然后,基于金屬表面特性,我們要么使用電介質(zhì)的基礎(chǔ)反射率要么就使用F0來作為表面顏色。因?yàn)榻饘俦砻鏁?huì)吸收所有折射光線而沒有漫反射,所以我們可以直接使用表面顏色紋理來作為它們的基礎(chǔ)反射率。
Fresnel Schlick近似可以用代碼表示為:
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
其中cosTheta
是表面法向量n與觀察方向v的點(diǎn)乘的結(jié)果。
4. Cook-Torrance反射率方程
隨著Cook-Torrance BRDF中所有元素都介紹完畢,我們現(xiàn)在可以將基于物理的BRDF納入到最終的反射率方程當(dāng)中去了:
這個(gè)方程現(xiàn)在完整的描述了一個(gè)基于物理的渲染模型,它現(xiàn)在可以認(rèn)為就是我們一般意義上理解的基于物理的渲染也就是PBR。如果你還沒有能完全理解我們將如何把所有這些數(shù)學(xué)運(yùn)算結(jié)合到一起并融入到代碼當(dāng)中去的話也不必?fù)?dān)心。在下一個(gè)教程當(dāng)中,我們將探索如何實(shí)現(xiàn)反射率方程來在我們渲染的光照當(dāng)中獲得更加物理可信的結(jié)果,而所有這些零零星星的碎片將會(huì)慢慢組合到一起來。
編寫PBR材質(zhì)
在了解了PBR后面的數(shù)學(xué)模型之后,最后我們將通過說明美術(shù)師一般是如何編寫一個(gè)我們可以直接輸入PBR的平面物理屬性的來結(jié)束這部分的討論。PBR渲染管線所需要的每一個(gè)表面參數(shù)都可以用紋理來定義或者建模。使用紋理可以讓我們逐個(gè)片段的來控制每個(gè)表面上特定的點(diǎn)對(duì)于光線是如何響應(yīng)的:不論那個(gè)點(diǎn)是金屬的,粗糙或者平滑,也不論表面對(duì)于不同波長的光會(huì)有如何的反應(yīng)。
在下面你可以看到在一個(gè)PBR渲染管線當(dāng)中經(jīng)常會(huì)碰到的紋理列表,還有將它們輸入PBR渲染器所能得到的相應(yīng)的視覺輸出:
反照率:反照率(Albedo)
紋理為每一個(gè)金屬的紋素(Texel)(紋理像素)指定表面顏色或者基礎(chǔ)反射率。這和我們之前使用過的漫反射紋理相當(dāng)類似,不同的是所有光照信息都是由一個(gè)紋理中提取的。漫反射紋理的圖像當(dāng)中常常包含一些細(xì)小的陰影或者深色的裂紋,而反照率紋理中是不會(huì)有這些東西的。它應(yīng)該只包含表面的顏色(或者折射吸收系數(shù))。
法線:法線貼圖紋理和我們之前在法線貼圖教程中所使用的貼圖是完全一樣的。法線貼圖使我們可以逐片段的指定獨(dú)特的法線,來為表面制造出起伏不平的假象。
金屬度:金屬(Metallic)貼圖逐個(gè)紋素的指定該紋素是不是金屬質(zhì)地的。根據(jù)PBR引擎設(shè)置的不同,美術(shù)師們既可以將金屬度編寫為灰度值又可以編寫為1或0這樣的二元值。
粗糙度:粗糙度(Roughness)
貼圖可以以紋素為單位指定某個(gè)表面有多粗糙。采樣得來的粗糙度數(shù)值會(huì)影響一個(gè)表面的微平面統(tǒng)計(jì)學(xué)上的取向度。一個(gè)比較粗糙的表面會(huì)得到更寬闊更模糊的鏡面反射(高光),而一個(gè)比較光滑的表面則會(huì)得到集中而清晰的鏡面反射。某些PBR引擎預(yù)設(shè)采用的是對(duì)某些美術(shù)師來說更加直觀的光滑度(Smoothness)
貼圖而非粗糙度貼圖,不過這些數(shù)值在采樣之時(shí)就馬上用(1.0 – 光滑度)轉(zhuǎn)換成了粗糙度。
AO:環(huán)境光遮蔽(Ambient Occlusion)
貼圖或者說AO貼圖為表面和周圍潛在的幾何圖形指定了一個(gè)額外的陰影因子。比如如果我們有一個(gè)磚塊表面,反照率紋理上的磚塊裂縫部分應(yīng)該沒有任何陰影信息。然而AO貼圖則會(huì)把那些光線較難逃逸出來的暗色邊緣指定出來。在光照的結(jié)尾階段引入環(huán)境遮蔽可以明顯的提升你場景的視覺效果。網(wǎng)格/表面的環(huán)境遮蔽貼圖要么通過手動(dòng)生成,要么由3D建模軟件自動(dòng)生成。
美術(shù)師們可以在紋素級(jí)別設(shè)置或調(diào)整這些基于物理的輸入值,還可以以現(xiàn)實(shí)世界材料的表面物理性質(zhì)來建立他們的材質(zhì)數(shù)據(jù)。這是PBR渲染管線最大的優(yōu)勢之一,因?yàn)椴徽摥h(huán)境或者光照的設(shè)置如何改變這些表面的性質(zhì)是不會(huì)改變的,這使得美術(shù)師們可以更便捷的獲取物理可信的結(jié)果。在PBR渲染管線中編寫的表面可以非常方便的在不同的PBR渲染引擎間共享使用,不論處于何種環(huán)境中它們看上去都會(huì)是正確的,因此看上去也會(huì)更自然。
延伸閱讀
- Background: Physics and Math of Shading by Naty Hoffmann:由于在這一篇文章中要談?wù)摰睦碚擖c(diǎn)太多,所以這里的理論知識(shí)都只是涉及到了皮毛。如果你希望了解更多關(guān)于光線背后的物理知識(shí)以及它們和PBR理論之間有什么關(guān)聯(lián)的話,這才是你需要閱讀的資源。
- Real shading in Unreal Engine 4:探討了Epic Games在他們的Unreal 4引擎中所采用的PBR模型。我們這些教程中主要涉及的PBR系統(tǒng)就是基于他們的PBR模型。
- Marmoset: PBR Theory:主要針對(duì)美術(shù)師的PBR介紹,不過仍然是很好的讀物。
- Coding Labs: Physically based rendering:介紹渲染方程以及它和PBR直接的關(guān)系。
- Coding Labs: Physically Based Rendering - Cook–Torrance:介紹了Cook-Torrance BRDF.
- Wolfire Games - Physically based rendering:介紹了PBR,由Lukas Orsv?rn所著。
后記
本篇已結(jié)束,下一篇關(guān)于PBR - 光照。