目錄php
PBR全稱Physically Based Rendering,譯成中文是基於物理的渲染,是當今很是流行的一種擬真渲染技術。css
國內外研究它的人數不勝數,由此出現的書籍、論文、文章等資料也很是多,其中最富盛名的非《Physically Based Rendering From Theory to Implementation》莫屬,它很是系統全面深刻地介紹了PBR的底層原理、渲染實現、公式推導、進階主題等內容。html
上圖:《Physically Based Rendering From Theory to Implementation, 3rd Edition》的封面,總頁數1270。ios
可是,這些資料大多存在一些問題,它們要麼太簡單太籠統,不能系統全面地介紹PBR的原理和實現;要麼太全面太複雜,動輒上千頁,對剛踏入PBR技術領域的人不夠友好,使人望而卻步。git
PBR無疑是一種涉及綜合性交叉學科的技術,它涉及的技術有:程序員
知乎牛人毛星雲總結出的PBR較完整的知識體系架構圖。若是看不清,請點這裏。github
對於一個剛接觸PBR技術的新人,一旦接觸到如此龐大的知識體系和如此大量陌生的名詞、理論、公式、實現,多半會在PBR的知識體系中迷失,成爲難以逾越的鴻溝,產生畏懼心理,從而放棄PBR的學習。web
剛好筆者在近期研習了大量PBR相關的資料,有些資料反覆觀摩了幾遍,對於PBR的漸進式學習有了必定的認知。因而萌生了撰寫此篇文章的念頭,以便讓從未接觸過PBR的人能按部就班地學習它、應用它、掌握它、實現它。算法
本文有如下特色:數據庫
本篇文章主要有如下章節內容,分別從不一樣層次不一樣側重點描述了PBR的原理、實現、應用,面向的羣體也不同:
從上面能夠看出,每一個章節都是承上啓下,層層遞進地剖析PBR原理及實現,從而達到由淺入深介紹PBR的目的。讀者能夠根據目前的水平,以及想要了解的程度,有針對性有選擇性地閱讀不一樣的章節。其實本文標題更適合取爲《分層漸進式學習PBR的原理和實現》,但,仍是如今的標題淺顯易懂。
須要注意的是,本文將圍繞PBR的核心理論作闡述,以實時渲染領域的Cook-Torrance的BRDF光照模型爲實現案例。其它旁系理論不在本文探討的範圍,能夠查閱其它文獻。
本章內容主要介紹PBR的基本概念和衍變歷史,以及其在主流商業引擎的應用。
面向的羣體:
PBR(Physically Based Rendering)譯成中文是基於物理的渲染。它是利用真實世界的原理和理論,經過各類數學方法推導或簡化或模擬出一系列渲染方程,並依賴計算機硬件和圖形API渲染出擬真畫面的技術。
那它爲何不叫物理渲染(Physical Rendering)呢?
物理渲染(Physical Rendering)是指跟真實世界徹底一致的計算機渲染效果。
爲了回答這個問題,先了解一下真實世界的成相原理。
真實世界的物體有着各自的材質屬性和表面特徵,它們受到各類局部燈光和全局環境光的影響,並且它們之間又相互影響,最終這些信息經過光波的形式進入複雜的人眼構造,刺激視神經造成生物信號進入大腦感光皮層,最終讓人產生視覺認知。(下圖)
有論文指出,絕大多數人的眼睛能夠接收至關於5億~10億個像素的信息量。目前主流的分辨率才百萬~千萬級別,加上顯示器亮度範圍和屏幕像素間距的限制,遠遠達不到億級像素的渲染和亮度表示範圍。
基於現階段的知識水平和硬件水平,還不能渲染跟真實世界徹底一致的效果,只能必定程序上模擬接近真實世界的渲染畫面,故而叫基於物理的渲染(Physically Based Rendering),而非物理渲染(Physical Rendering)。
這節闡述的是PBR呈現的效果特徵,而非底層物理原理的特徵。
相比傳統的Lambert着色和Phong着色,PBR着色在效果上有着質的提高,能夠表示更多更復雜的材質特徵:
Phong模型着色效果,只能簡單地表現理想模型的漫反射和高光,渲染出的效果跟真實世界相差甚遠。
PBR材質效果球,它們真實地渲染出各種材質的粗糙、紋理、高光、清漆、邊緣光等等表面細節特徵。PBR對渲染效果然實感的提高可見一斑。
PBR從最初傳統型的Lambert光照發展至今,已經歷經200多年,期間發生屢次迭代衍變和改進,主流光照模型和分支光照模型也遍地開花。下面按照時間順序着重對PBR衍變的關鍵技術節點作闡述。
Lambert模型是Johann Heinrich Lambert在1760年提出的光照模型。是傳統的光照模型。
它計算的是漫反射。漫反射是光源照射到物體表面後,向四面八方反射,產生的反射效果。這是一種理想的漫反射光照模型。
漫反射光的強度近似地服從於Lambert定律,即漫反射光的光強僅與入射光的方向和反射點處表面法向夾角的餘弦成正比。
Lambert模型着色效果,模擬了理想環境下的漫反射效果。
Smith將Cook-Torrance的DFG部分的G幾何項有效地結合起來,使得幾何函數的近似法獲得了有效地提高,後面章節將會闡述更多細節。
Phong模型由美國越南裔學者裴祥風(Bùi Tường Phong)發明,於1973年的博士論文首度發表。它也是一種傳統的理想的光照模型。
相較Lambert,Phong增長了鏡面反射部分,使得物體渲染效果更接近真實世界(下圖)。
Cook-Torrance是Cook和Torrance於1982年聯合提出的光反射模型。
該模型考慮在同一景物中不一樣材料和不一樣光源的相對亮度。它描述反射光線在方向上的分佈和當反射隨入射角而改變時顏色的變化,並能求得從具體的實際材料製成的物體反射出來的光線的光譜能量分佈,並根據這種光譜能量分佈精確地再現顏色。
簡而言之,Cook-Torrance增長了幾何項G、Fresnel項、粗糙度項D等信息。利用該模型渲染出的圖像真實感有了較大跨度的提高。
Cook-Torrance光照模型渲染效果。它較好地渲染出模型的表面特徵和光照效果。
Lambert模型因爲是理想環境下的光照模擬,不能正確體現物體(特別是粗糙物體)表面的光照效果。
Oren Nayarh模型對此作出了改進,主要對粗糙表面的物體建模,好比石膏、沙石、陶瓷等。用了一系列的Lambert微平面,考慮了微小平面之間的相互遮擋(shadowing and masking)和互相反射照明。它能必定程度上模擬真實物體的表面粗糙度,使物體更有質感。
左:真實照片,中:Lambert模型效果,右:Oren Nayarh模型效果
Schlick模型簡化了Phong模型的鏡面反射中的指數運算。採用如下公式替代:
\[ F = F_0+(1-F_o)(1-cos(\theta))^5 \]
\[ F_0 = (\frac{n_1-n_2}{n_1+n_2})^2 \]
它模擬的高光反射效果跟Pow運算基本一致,且效率比Pow運算高。
GGX模型所解決的問題是,如何將微平面反射模型推廣到表面粗糙的半透明材質,從而可以模擬相似於毛玻璃的粗糙表面的透射效果。同時,它也提出了一種新的微平面分佈函數 。
上圖:GGX很是逼真地模擬半透明物體的效果。
雖然它提出時被用於半透明物體的模擬,但它做爲一種描述微平面法線方向分佈的函數,一樣適用於渲染表面粗糙的不透明物體。
GGX一樣能夠很是逼真地模擬不透明物體的效果
GGX已經普遍應用於各類主流遊戲引擎中,同時也是效果最好的。
在SIGGRAPH 2012會議上,工做於迪斯尼動畫工做室的Brent Burly演講了著名的主題:《Physically Based Shading at Disney》。
Brent Burly在SIGGRAPH 2012演講迪斯尼原則的PBR。
他提出了迪斯尼原則的BRDF(Disney Principled BRDF),奠基了後續遊戲行業和電影行業PBR的方向和標準。後續的主流遊戲引擎,3D渲染器及動畫製做軟件大多基於此方案或變種實現的。
迪斯尼原則的PBR渲染出的《無敵破壞王》畫面。
迪斯尼原則的BRDF用少許簡單易懂的參數和高度完善的美術工做流程,大大簡化了此前複雜的PBR的參數和製做流程。它是藝術導向(Art Directable)的着色模型,而不徹底是物理正確(Physically Correct)。
迪斯尼原則的BRDF抽象出的參數。
基於物理的光照模型已經發展了數十年,期間衍生的關鍵技術和變種技術很是多,它們各有適用場景或解決的各個具體應用場景的問題。
近今年,PBR的技術主要朝着更逼真、更復雜、效能更好的方向,或是結合若干種模型的綜合性技術邁進。表明性技術有:
UE4渲染出的虛擬人Siren。綜合了分層材質、混合材質、混合BxDF、眼球毛髮和皮膚渲染等新興技術。
虛擬人Siren的皮膚細節。與數碼相機攝製的相片一模一樣,逼真程度使人咂舌。若是不特地提醒,很難相信這是遊戲引擎實時渲染出來的畫面。
PBR通過長時間的發展,技術上和渲染的效果日新月異,是計算機圖形學的下一代渲染技術。它在實時渲染和離線渲染領域都有着很是普遍且深刻的應用,主要有:
電影和動漫。使用PBR技術渲染的真人電影,擬真電影,以及各種動漫電影數量很是多,好比早些年的《阿凡達》《飛屋環遊記》,近期的《戰鬥天使》《流浪地球》《馴龍高手3》等。
電影《阿凡達》的人物畫面。
電影《戰鬥天使》的畫面。主角阿麗塔是計算機經過PBR技術渲染出來的虛擬角色,她與真人演員和真實環境無縫地融合在了一塊兒。
電影《流浪地球》的虛擬場景。特效製做公司利用PBR技術模擬出恐怖的身臨其境的畫面。
實時遊戲。PBR的身影流傳於PC遊戲,在線遊戲,移動遊戲,主機遊戲等遊戲細分領域。相信接觸過遊戲的人大多體驗過次世代效果的魅力。
PC網遊《逆水寒》的角色次世代效果。
移動遊戲《絕地求生·刺激戰場》的次世代場景。
單機遊戲《極品飛車20》的動感瞬間。
計算機輔助設計與製造(CAD/CAM)。計算機圖形學剛起步時,便應用於此領域,PBR的引入,更加真實地幫助設計人員設計出與實物相差無幾的產品。
電路板設計預渲染效果圖。
跑車概念設計效果圖。
室內家裝設計效果圖。
計算機輔助教學(CAI)。經過逼真的PBR技術,渲染出教學內容所需的虛擬場景,佐以動畫技術,使得教學更加形象生動有趣。
虛擬現實(VR/AR/MR)。虛擬技術一般須要佩戴眼鏡或頭盔等顯示設備,較多地用於軍事,教學,模擬訓練,醫學等領域。而VR引入PBR技術,能更逼真地模擬現實世界,讓參與者身臨其境。
Magic Leap製做的VR概念圖。
科學計算可視化。氣象、地震、天體物理、分子生物學、醫學等科學領域採用PBR技術將更真實地模擬天然規律,有助於科學家新發現,有助於高校師生教學。
計算機模擬出的DNA雙螺旋結構圖。
迪斯尼自2012年提出迪斯尼原則的PBR理論後,在遊戲和電影界引發轟動,隨後各大主流遊戲引擎和渲染器及建模軟件紛紛實現基於斯尼原則的PBR技術。
下面是主流遊戲引擎支持迪斯尼原則的PBR時間表:
UE4和Unity在算法上的實現略有差異,但本章先不討論算法的實現問題,主要闡述材質上的參數。
UE4的PBR相對其它迪斯尼原則的PBR實現,在參數方面作了精簡,涉及的參數主要有:
下表是通過測量後得出的非金屬材質的基礎色強度(非金屬材質只有單色,即強度):
材質(Material) | 基礎色強度(BaseColor Intensity) |
---|---|
木炭(Charcoal) | 0.02 |
新瀝青(Fresh asphalt) | 0.02 |
舊瀝青(Worn asphalt) | 0.08 |
土壤(Bare soil) | 0.13 |
綠草(Green Grass) | 0.21 |
沙漠沙(desert sand) | 0.36 |
新混泥土(Fresh concrete) | 0.51 |
海洋冰(Ocean Ice) | 0.56 |
鮮雪(Fresh snow) | 0.81 |
下表是通過測量後得出的金屬材質的基礎色(R, G, B),是在Linear色域空間的值:
材質(Material) | 基礎色(BaseColor) |
---|---|
鐵(Iron) | (0.560, 0.570, 0.580) |
銀(Silver) | (0.972, 0.960, 0.915) |
鋁(Aluminum) | (0.913, 0.921, 0.925) |
金(Gold) | (1.000, 0.766, 0.336) |
銅(Copper) | (0.955, 0.637, 0.538) |
鉻(Chromium) | (0.550, 0.556, 0.554) |
鎳(Nickel) | (0.660, 0.609, 0.526) |
鈦(Titanium) | (0.542, 0.497, 0.449) |
鈷(Cobalt) | (0.662, 0.655, 0.634) |
鉑(Platinum) | (0.672, 0.637, 0.585) |
粗糙度(Roughness):表示材質表面的粗糙程度,值限定在0~1之間。越粗糙材質高光反射越不明顯,金屬和非金屬的粗糙度有所區別。
上:非金屬材質隨粗造度從0-1變化而漸變的圖,下:金屬材質隨粗造度從0-1變化而漸變的圖。
金屬度(Metallic):表示材質像金屬的程度,0是電介質(絕緣體),1是金屬。金屬沒有漫反射,只有鏡面反射。
金屬度從0~1的變化圖。
鏡面度(Specular):表示材質的鏡面反射強度,從0(徹底無鏡面反射)~1(徹底鏡面反射。UE4的默認值是0.5。萬物皆有光澤(鏡面反射),對於強漫反射的材質,可經過調節粗糙度,而不該該將鏡面度調成0。
鏡面度從0~1的變化圖。
下表是UE4給出的部分材質鏡面度參考值:
材質(Material) | 鏡面度(Specular) |
---|---|
草(Glass) | 0.500 |
塑料(Plastic) | 0.500 |
石英(Quartz) | 0.570 |
冰(Ice) | 0.224 |
水(Water) | 0.255 |
牛奶(Milk) | 0.277 |
皮膚(Skin) | 0.350 |
UE4模擬的部分材質效果見下圖。
上排從左到右:木炭、生混凝土、舊瀝青;下排從左到右:銅、鐵、金、鋁、銀、鎳、鈦。
Unity的PBR已經歸入內建的標準着色器(Standard Shader),它的實現準則是用戶友好的(user-friendly),故而在材質編輯器裏呈現給用戶是有限的參數,並且跟傳統的各種貼圖信息統一在了一塊兒。
Unity內部實現機制遵循了PBR的基本準則,支持金屬度,表面粗糙度,能量守恆,菲涅爾反射,表面陰影遮蔽等特性。
Unity的Standard Shader編輯界面。
其中跟PBR相關的參數:
上章主要介紹了PBR的歷史和逼真的效果特徵。這章將重點介紹PBR的核心部分的基本原理及主流的實現方案,使讀者對PBR的核心理論有必定了解,並能掌握相關的編碼。
主要面向:
在分析比較了大量資料以後,本章選取了LearnOpenGL的PBR教程做爲依託,闡述PBR的基本原理和實現。
本節的理論和推導儘可能簡化和精簡,更深刻的原理和理論將在下一章闡述。
知足如下條件的光照模型才能稱之爲PBR光照模型:
大多數PBR技術都是基於微平面理論。在此理論下,認爲在微觀上全部材質表面都是由不少朝向不一的微小平面組成,有的材質表面光滑一些,有的粗糙一些。
真實世界的物體表面不必定是不少微小平面組成,也多是帶有弧度或者坑坑窪窪。但對於咱們肉眼能觀察到的維度,PBR的微觀近似模擬方法產生的結果跟實際差異甚微。
全部材質表面由粗糙度不一樣的微小平面組成。左邊材質更粗糙,右邊的平滑一些。
當光線射入這些微平面後,一般會產生鏡面反射。對於越粗糙的表面,因爲其朝向更無序,反射的光線更雜亂,反之,平滑的微平面,反射的光線更平齊。
上圖左邊材質表面更粗糙,反射的光線更雜亂;圖右的平滑許多,反射的光線更有規律。
從微觀角度來講,沒有任何表面是徹底光滑的。因爲這些微平面已經微小到沒法逐像素地繼續對其進行細分,所以咱們只有假設一個粗糙度(Roughness,即2.4.1中提到的粗糙度)參數,而後用統計學的方法來概略的估算微平面的粗糙程度。
咱們能夠基於一個平面的粗糙度來計算出某個向量的方向與微平面平均取向方向一致的機率。這個向量即是位於光線向量\(l\)和視線向量\(v\)之間的中間向量,被稱爲半角向量(Halfway Vector)。
半角向量\(h\)是視線\(v\)和入射光\(l\)的中間單位向量。
半角向量計算公式以下:
\[ h = \frac{l + v}{\|l + v\|} \]
半角向量計算GLSL實現:
// lightPos是光源位置,viewPos是攝像機位置,FragPos是像素位置 vec3 lightDir = normalize(lightPos - FragPos); vec3 viewDir = normalize(viewPos - FragPos); vec3 halfwayDir = normalize(lightDir + viewDir);
越多的微平面取向與其半角向量一致,材質鏡面反射越強越銳利。加上引入取值0~1的粗糙度,能夠大體模擬微平面的總體取向。
粗糙度從0.1~1.0的變化圖。粗糙度越小,鏡面反射越亮範圍越小;粗糙度越大,鏡面反射越弱。
在微平面理論中,採用近似的能量守恆:出射光的總能量不超過入射光的總能量(自發光材質除外)。3.1.1的粗糙度變化圖能夠看出,材質粗糙度越大,反射的範圍越大,但總體亮度變暗。
那麼PBR是如何實現近似的能量守恆呢?
爲了回答這個問題,先弄清楚鏡面反射(specular)和漫反射(diffuse)的區別。
一束光照到材質表面上,一般會分紅反射(reflection)部分和折射(refraction)部分。反射部分直接從表面反射出去,而不進入物體內部,由此產生了鏡面反射光。折射部分會進入物體內部,被吸取或者散射產生漫反射。
折射進物體內部的光若是沒有被當即吸取,將會持續前進,與物體內部的微粒產生碰撞,每次碰撞有一部分能量損耗轉化成熱能,直至光線能量所有消耗。有些折射光線在跟微粒發生若干次碰撞以後,從物體表面射出,便會造成漫反射光。
照射在平面的光被分紅鏡面反射和折射光,折射光在跟物體微粒發生若干次碰撞以後,有可能發射出表面,成爲漫反射。
一般狀況下,PBR會簡化折射光,將平面上全部折射光都視爲被徹底吸取而不會散開。而有一些被稱爲次表面散射(Subsurface Scattering)技術的着色器技術會計算折射光散開後的模擬,它們能夠顯著提高一些材質(如皮膚、大理石或蠟質)的視覺效果,不過性能也會隨着降低。
金屬(Metallic)材質會當即吸取全部折射光,故而金屬只有鏡面反射,而沒有折射光引發的漫反射。
回到能量守恆話題。反射光與折射光它們兩者之間是互斥的,被表面反射出去的光沒法再被材質吸取。故而,進入材質內部的折射光就是入射光減去反射光後餘下的能量。
根據上面的能量守恆關係,能夠先計算鏡面反射部分,此部分等於入射光線被反射的能量所佔的百分比。而折射部分能夠由鏡面反射部分計算得出。
float kS = calculateSpecularComponent(...); // 反射/鏡面部分 float kD = 1.0 - kS; // 折射/漫反射部分
經過以上代碼能夠看出,鏡面反射部分與漫反射部分的和確定不會超過1.0,從而近似達到能量守恆的目的。
渲染方程(Render Equation)是用來模擬光的視覺效果最好的模型。而PBR的渲染方程是用以抽象地描述PBR光照計算過程的特化版本的渲染方程,被稱爲反射方程。
PBR的反射方程可抽象成下面的形式:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} f_r(p,\omega_i,\omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
反射方程看似很複雜,但若是拆分各個部分加以解析,就能夠揭開其神祕的面紗。
爲了更好地理解反射方程,先了解輻射度量學(Radiometry)。輻射度量學是一種用來度量電磁場輻射(包括可見光)的手段。有不少種輻射度量(radiometric quantities)能夠用來測量曲面或者某個方向上的光,此處只討論和反射方程有關的一種量,它就是輻射率(Radiance),用\(L\)來表示。
先用一個表展現輻射度量學涉及的概念、名詞、公式等信息,後面會更加詳細地介紹。
名稱 | 符號 | 單位 | 公式 | 解析 |
---|---|---|---|---|
輻射能量(Radiant energy) | \(Q\) | 焦耳(\(J\)) | - | 電磁輻射能量 |
輻射通量(Radiant Flux) | \(\Phi\) | 瓦(\(W\)) | \(\Phi = \frac{dQ}{dt}\) | 單位時間輻射的能量,也叫輻射功率(Radiant Power)或通量(Flux) |
輻照度(Irradiance) | \(E\) | 瓦/平方米(\({W}/{m^2}\)) | \(\Phi = \frac{d\Phi}{dA^\perp}\) | 到達單位面積的輻射通量 |
輻射度(Radiosity) | \(M\) | 瓦/平方米(\({W}/{m^2}\)) | \(M = \frac{d\Phi}{dA^\perp}\) | 離開單位面積的輻射通量,也叫輻出度、輻射出射度(Radiant Existance) |
輻射強度(Radiant Intensity) | \(I\) | 瓦/立體弧度(\({W}/{sr}\)) | \(I = \frac{d\Phi}{d\omega}\) | 經過單位立體角的輻射通量 |
輻射率(Radiance) | \(L\) | 瓦/平方米立體弧度(\({W}/m^2{sr}\)) | \(L = \frac{d\Phi}{d\omega dA^\perp}\) | 經過單位面積單位立體角的輻射通量 |
立體角(Solid Angle) | \(\omega\) | 立體弧度,球面度(\(sr\)) | \(\omega=\frac{S}{r^2}\) | 是二維弧度在三維的擴展,1球面度等於單位球體的表面面積 |
輻射率被用來量化單一方向上發射來的光線的大小或者強度。輻射率是由多個物理變量集合而成的,它涉及的物理變量有如下幾種:
輻射通量(Radiant Flux):輻射通量用符號\(\Phi\)表示,表示一個光源輸出的能量,以瓦特爲單位。光是由多種不一樣波長的能量集合而成,每種波長與一種特定的(可見的)顏色相關。所以一個光源所放射出來的能量能夠被視做這個光源包含的全部各類波長的一個函數。波長介於390nm(納米)到700nm的光被認爲是處於可見光光譜中,也就是說它們是人眼可見的波長。
上圖展現了太陽光中不一樣波長的光所具備的能量。
傳統物理學上的輻射通量將會計算這個由不一樣波長構成的函數的總面積,這種計算很複雜,耗費大量性能。在PBR技術中,不直接使用波長的強度,而是使用三原色編碼(RGB)來簡化輻射通量的計算。雖然這種簡化會帶來一些信息上的損失,可是這對於視覺效果上的影響基本能夠忽略。
立體角(Solid Angle):用符號\(\omega\)表示,它描述投射到單位球體上的一個截面的大小或者面積。能夠把立體角想象成爲一個帶有體積的方向:
更加形象地描述:觀察者站在單位球面的中心,向着投影的方向看,在單位球體面上的投影輪廓的大小就是立體角。
輻射強度(Radiant Intensity):用符號\(I\)表示,它描述的是在單位球面上,一個光源向每單位立體角所投送的輻射通量。舉個例子,假設一個點光源向全部方向均勻地輻射能量,輻射強度就能計算出它在一個單位面積(立體角)內的能量大小:
計算輻射強度的公式:
\[ I = \frac{d\Phi}{d\omega} \]
其中\(I\)表示輻射通量\(\Phi\)除以立體角\(\omega\)的輻射強度。
理解以上物理變量後,能夠繼續討論輻射率方程了。下面方程表明的意義是:一個輻射強度爲\(\Phi\)的光經過立體角\(\omega\)輻射在區域\(A\)的可被觀察到的總能量。
\[ L=\frac{I}{dA^\perp}=\frac{\frac{d\Phi}{d\omega}}{dA\cos\theta}=\frac{d\Phi}{ dA d\omega \cos\theta} \]
筆者注:原文的公式是\(L = \frac{d^2\Phi}{ dA d\omega \cos\theta}\),經推導以後,並無平方。
輻射率是一個區域內光照量的輻射學度量,按照光的入射(或者來源)角與平面法線的夾角\(\theta\)計算\(\cos \theta\)。越是斜着照射在平面上光越弱,反之越是垂直照射在表面上的光越強,相似基礎光照中的漫反射顏色計算,\(\cos \theta\)直接等於光的方向和表面法線的點積。
float cosTheta = dot(lightDir, N);
上面的物理符號彷佛和PBR的反射方程沒有直接的關係。可是,若是將立體角\(\omega\)跟區域\(A\)都看做無限小,就能夠使用輻射率來分析一束光線打在空間上一個點的通量,也就是說可以計算單束光線對單個(片元)點的輻射率影響。進一步地,將立體角\(\omega\)轉化爲方向向量\(\omega\),將區域\(A\)轉化成點\(p\),所以在shader中直接使用輻射率來計算單束光線對每一個片元的貢獻。
實際上,當談及光的輻射率時,一般只關注的是全部射入點\(p\)的光線,這些光的輻射度總和稱爲輻照度(Irradiance)。理解了輻射率和輻照度,回到反射方程:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} f_r(p,\omega_i,\omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
渲染方程式中\(L\)表明某個點\(p\)的輻射率,而無限小的入射光的立體角\(\omega_i\)能夠看做入射光方向向量\(\omega_i\),將用來衡量入射光與平面法線夾角對能量的影響的\(\cos \theta\)份量移出輻射率方程,做爲反射方程的單獨項\(n \cdot \omega_i\) 。
反射方程計算了點\(p\)在全部視線方向\(\omega_0\)上被反射出來的輻射率\(L_o(p,\omega_o)\)的總和。換言之:\(L_0\)計算的是在\(\omega_o\)方向的眼睛觀察到的\(p\)點的總輻照度。
反射方程裏面使用的輻照度,必需要包含全部以\(p\)點爲中心的半球\(\Omega\)內的入射光,而不僅僅只是某一個方向的入射光。這個半球指的是圍繞面法線\(n\)的那一個半球:
筆者注:爲何只計算半球而不計算整個球體呢?
由於另一邊的半球因與視線方向相反,不能被觀察,也就是輻射通量貢獻量爲0,因此被忽略。
爲了計算這個區域(半球)內的全部值,在反射方程中使用了一個稱做爲積分的數學符號 \(\int\),來計算半球\(\Omega\)內全部的入射向量\(d\omega_i\)。
積分計算面積的方法,有解析(analytically)和漸近(numerically)兩種方法。目前尚沒有能夠知足渲染計算的解析法,因此只能選擇離散漸近法來解決這個積分問題。
具體作法是在半球\(\Omega\)按必定的步長將反射方程離散地求解,而後再按照步長大小將所獲得的結果平均化,這種方法被稱爲黎曼和(Riemann sum)。下面是實現的僞代碼:
int steps = 100; // 分段計算的數量,數量越多,計算結果越準確。 float dW = 1.0f / steps; vec3 P = ...; vec3 Wo = ...; vec3 N = ...; float sum = 0.0f; for(int i = 0; i < steps; ++i) { vec3 Wi = getNextIncomingLightDir(i); sum += Fr(P, Wi, Wo) * L(P, Wi) * dot(N, Wi) * dW; }
dW
的值越小結果越接近正確的積分函數的面積或者說體積,衡量離散步長的dW
能夠看做反射方程中的\(d\omega_i\)。積分計算中咱們用到的\(d\omega_i\)是線性連續的符號,跟代碼中的dW
並無直接關係,可是這種方式有助於咱們理解,並且這種離散漸近的計算方法老是能夠獲得一個很接近正確結果的值。值得一提的是,經過增長步驟數steps
能夠提升黎曼和的準確性,但計算量也會增大。
反射方程加了全部的,以各個方向\(\omega_i\)射入半球\(\Omega\)並打中點\(p\)的入射光,通過反射函數\(f_r\)進入觀察者眼睛的全部反射光\(L_o\)的輻射率之和。入射光輻射度能夠由光源處得到,此外還能夠利用一個環境貼圖來測算全部入射方向上的輻射度。
至此,反射方程中,只剩下\(f_r\)項未描述。\(f_r\)就是雙向反射分佈函數(Bidirectional Reflectance Distribution Function, BRDF),它的做用是基於表面材質屬性來對入射輻射度進行縮放或者加權。
雙向反射分佈函數(Bidirectional Reflectance Distribution Function,BRDF)是一個使用入射光方向\(\omega_i\)做爲輸入參數的函數,輸出參數爲出射光\(\omega_o\),表面法線爲\(n\),參數\(a\)表示的是微平面的粗糙度。
BRDF函數是近似的計算在一個給定了屬性的不透明表面上每一個單獨的光線對最終的反射光的貢獻量。假如表面是絕對光滑的(好比鏡子),對於全部入射光\(\omega_i\)的BRDF函數都將會返回0.0,除非出射光線\(\omega_o\)方向的角度跟入射光線\(\omega_i\)方向的角度以面法線爲中軸線徹底對稱,則返回1.0。
BRDF對於材質的反射和折射屬性的模擬基於以前討論過的微平面理論,想要BRDF在物理上是合理的,就必須遵照能量守恆定律。好比反射光能量總和永遠不該該超過入射光。技術上來講,Blinn-Phong光照模型跟BRDF同樣使用了\(\omega_i\)跟\(\omega_o\)做爲輸入參數,可是沒有像基於物理的渲染這樣嚴格地遵照能量守恆定律。
BRDF有好幾種模擬表面光照的算法,然而,基本上全部的實時渲染管線使用的都是Cook-Torrance BRDF。
Cook-Torrance BRDF分爲漫反射和鏡面反射兩個部分:
\[ f_r = k_d f_{lambert} + k_s f_{cook-torrance} \]
其中\(k_d\)是入射光中被折射的比例,\(k_s\)是另一部分被鏡面反射的入射光。BRDF等式左邊的\(f_{lambert}\)表示的是漫反射部分,這部分叫作倫勃朗漫反射(Lambertian Diffuse)。它相似於咱們以前的漫反射着色,是一個恆定的算式:
\[ f_{lambert} = \frac{c}{\pi} \]
其中\(c\)表明的是Albedo或表面顏色,相似漫反射表面紋理。除以\(\pi\)是爲了規格化漫反射光,爲後期的BRDF積分作準備。
此處的倫勃朗漫反射跟之前用的漫反射之間的關係:之前的漫反射是用表面的漫反射顏色乘以法線與面法線的點積,這個點積依然存在,只不過是被移到了BRDF外面,寫做\(n \cdot \omega_i\),放在反射方程\(L_o\)靠後的位置。
BRDF的高光(鏡面反射)部分更復雜:
\[ f_{cook-torrance} = \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} \]
Cook-Torrance鏡面反射BRDF由3個函數(\(D\),\(F\),\(G\))和一個標準化因子構成。\(D\),\(F\),\(G\)符號各自近似模擬了特定部分的表面反射屬性:
以上的每一種函數都是用來估算相應的物理參數的,並且你會發現用來實現相應物理機制的每種函數都有不止一種形式。它們有的很是真實,有的則性能高效。你能夠按照本身的需求任意選擇本身想要的函數的實現方法。
Epic Games公司的Brian Karis對於這些函數的多種近似實現方式進行了大量的研究。這裏將採用Epic Games在Unreal Engine 4中所使用的函數,其中\(D\)使用Trowbridge-Reitz GGX,\(F\)使用Fresnel-Schlick近似法(Approximation),而\(G\)使用Smith's Schlick-GGX。
法線分佈函數,從統計學上近似的表示了與某些(如中間)向量\(h\)取向一致的微平面的比率。
目前有不少種NDF均可以從統計學上來估算微平面的整體取向度,只要給定一些粗糙度的參數以及一個咱們立刻將會要用到的參數Trowbridge-Reitz GGX(GGXTR):
\[ NDF_{GGX TR}(n, h, \alpha) = \frac{\alpha^2}{\pi((n \cdot h)^2 (\alpha^2 - 1) + 1)^2} \]
這裏的\(h\)是用來測量微平面的半角向量,\(\alpha\)是表面的粗糙度,\(n\)是表面法線。 若是將\(h\)放到表面法線和光線方向之間,並使用不一樣的粗糙度做爲參數,能夠獲得下面的效果:
當粗糙度很低(表面很光滑)時,與中間向量\(h\)取向一致的微平面會高度集中在一個很小的半徑範圍內。因爲這種集中性,NDF最終會生成一個很是明亮的斑點。可是當表面比較粗糙的時候,微平面的取向方向會更加的隨機,與向量\(h\)取向一致的微平面分佈在一個大得多的半徑範圍內,可是較低的集中性也會讓最終效果顯得更加灰暗。
Trowbridge-Reitz GGX的NDF實現代碼:
float DistributionGGX(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; }
菲涅爾方程定義的是在不一樣觀察方向上,表面上被反射的光除以被折射的光的比例。在一束光擊中了表面的一瞬間,菲涅爾根據表面與觀察方向之間的夾角,計算獲得光被反射的百分比。根據這個比例和能量守恆定律咱們能夠直接知道剩餘的能量就是會被折射的能量。
當咱們垂直觀察每一個表面或者材質時都有一個基礎反射率,當咱們以任意一個角度觀察表面時全部的反射現象都會變得更明顯(反射率高於基礎反射率)。你能夠從你身邊的任意一件物體上觀察到這個現象,當你以90度角觀察你的桌子你會法線反射現象將會變得更加的明顯,理論上以完美的90度觀察任意材質的表面都應該會出現全反射現象(全部物體、材質都有菲涅爾現象)。
菲涅爾方程一樣是個複雜的方程,可是幸運的是菲涅爾方程能夠使用Fresnel-Schlick來近似:
\[ F_{Schlick}(h, v, F_0) = F_0 + (1 - F_0) ( 1 - (h \cdot v))^5 \]
\(F_0\)表示的是表面基礎反射率,這個咱們能夠使用一種叫作Indices of refraction(IOR)的方法計算獲得。運用在球面上的效果就是你看到的那樣,觀察方向越是接近掠射角(grazing angle,又叫切線角,與正視角相差90度),菲涅爾現象致使的反射就越強:
菲涅爾方程中有幾個微妙的地方,一個是Fresnel-Schlick算法僅僅是爲電介質(絕緣體)表面定義的算法。對於金屬表面,使用電介質的折射率來計算基礎反射率是不合適的,咱們須要用別的菲涅爾方程來計算。對於這個問題,咱們須要預先計算表面在正視角(即以0度角正視表面)下的反應(\(F_0\)),而後就能夠跟以前的Fresnel-Schlick算法同樣,根據觀察角度來進行插值。這樣咱們就能夠用一個方程同時計算金屬和電介質了。
表面在正視角下的反映或者說基礎反射率能夠在這個數據庫中找到,下面是Naty Hoffman的在SIGGRAPH公開課中列舉的一些常見材質的值:
這裏能夠觀察到的一個有趣的現象,全部電介質材質表面的基礎反射率都不會高於0.17,這實際上是例外而非廣泛狀況。導體材質表面的基礎反射率起點更高一些而且(大多)在0.5和1.0之間變化。此外,對於導體或者金屬表面而言基礎反射率通常是帶有色彩的,這也是爲何要用RGB三原色來表示的緣由(法向入射的反射率可隨波長不一樣而不一樣)。這種現象咱們只能在金屬表面觀察的到。
金屬表面這些和電介質表面相比所獨有的特性引出了所謂的金屬工做流的概念。也就是咱們須要額外使用一個被稱爲金屬度(Metalness)的參數來參與編寫表面材質。金屬度用來描述一個材質表面是金屬仍是非金屬的。
經過預先計算電介質與導體的值,咱們能夠對兩種類型的表面使用相同的Fresnel-Schlick近似,可是若是是金屬表面的話就須要對基礎反射率添加色彩。咱們通常是按下面這個樣子來實現的:
vec3 F0 = vec3(0.04); F0 = mix(F0, surfaceColor.rgb, metalness);
咱們爲大多數電介質表面定義了一個近似的基礎反射率。\(F_0\)取最多見的電解質表面的平均值,這又是一個近似值。不過對於大多數電介質表面而言使用0.04做爲基礎反射率已經足夠好了,並且能夠在不須要輸入額外表面參數的狀況下獲得物理可信的結果。而後,基於金屬表面特性,咱們要麼使用電介質的基礎反射率要麼就使用\(F_0\)做來爲表面顏色。由於金屬表面會吸取全部折射光線而沒有漫反射,因此咱們能夠直接使用表面顏色紋理來做爲它們的基礎反射率。
Fresnel Schlick近似能夠用GLSL代碼實現:
vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); }
其中cosTheta
是表面法向量\(n\)與觀察方向\(v\)的點乘的結果。
幾何函數模擬微平面相互遮擋致使光線的能量減小或丟失的現象。
相似NDF,幾何函數也使用粗糙度做爲輸入參數,更粗糙意味着微平面產生自陰影的機率更高。幾何函數使用由GGX和Schlick-Beckmann組合而成的模擬函數Schlick-GGX:
\[ G_{SchlickGGX}(n, v, k) = \frac{n \cdot v} {(n \cdot v)(1 - k) + k } \]
這裏的\(k\)是使用粗糙度\(\alpha\)計算而來的,用於直接光照和IBL光照的幾何函數的參數:
\[ \begin{eqnarray*} k_{direct} &=& \frac{(\alpha + 1)^2}{8} \\ k_{IBL} &=& \frac{\alpha^2}{2} \end{eqnarray*} \]
須要注意的是這裏\(\alpha\)的值取決於你的引擎怎麼將粗糙度轉化成\(\alpha\),在接下來的教程中咱們將會進一步討論如何和在什麼地方進行這個轉換。
爲了有效地模擬幾何體,咱們須要同時考慮兩個視角,視線方向(幾何遮擋)跟光線方向(幾何陰影),咱們能夠用Smith函數將兩部分放到一塊兒:
\[ G(n, v, l, k) = G_{sub}(n, v, k) G_{sub}(n, l, k) \]
其中\(v\)表示視線向量,\(G_{sub}(n, v, k)\)表示視線方向的幾何遮擋;\(l\)表示光線向量,\(G_{sub}(n, l, k)\)表示光線方向的幾何陰影。使用Smith函數與Schlick-GGX做爲\(G_{sub}\)能夠獲得以下所示不一樣粗糙度R的視覺效果:
幾何函數是一個值域爲[0.0, 1.0]的乘數,其中白色(1.0)表示沒有微平面陰影,而黑色(0.0)則表示微平面完全被遮蔽。
使用GLSL編寫的幾何函數代碼以下:
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; }
Cook-Torrance反射方程中的每個部分咱們咱們都用基於物理的BRDF替換,能夠獲得最終的反射方程:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
上面的方程並不是徹底數學意義上的正確。前面提到菲涅爾項\(F\)表明光在表面的反射比率,它直接影響\(k_s\)因子,意味着反射方程的鏡面反射部分已經隱含了因子\(k_s\)。所以,最終的Cook-Torrance反射方程以下(去掉了\(k_s\)):
\[ L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
這個方程完整地定義了一個基於物理的渲染模型,也就是咱們通常所說的基於物理的渲染(PBR)。
對PBR數學模型有了基本瞭解以後,咱們最後要討論的是美工應該生成怎樣的材質屬性,讓咱們能夠直接用在PBR渲染管線裏。PBR管線中須要的全部材質參數均可以使用紋理來定義或者模擬,使用紋理咱們能夠逐像素控制制定的面如何跟光線交互:這個點是不是金屬,粗糙度如何又或者表面對不一樣波長的光有什麼反映。
下面是在PBR渲染管線中常常用到的紋理:
下面的參數跟2.4 PBR在遊戲引擎的應用描述的不少參數基本一致。
反射率(Albedo):反射率紋理指定了材質表面每一個像素的顏色,若是材質是金屬那紋理包含的就是基礎反射率。這個跟咱們以前用過的漫反射紋理很是的相似,可是不包含任何光照信息。漫反射紋理一般會有輕微的陰影和較暗的裂縫,這些在Albedo貼圖裏面都不該該出現,僅僅只包含材質的顏色(金屬材質是基礎反射率)。
法線(Normal):法線紋理跟咱們以前使用的是徹底同樣的。法線貼圖能夠逐像素指定表面法線,讓平坦的表面也能渲染出凹凸不平的視覺效果。
金屬度(Metallic):金屬度貼圖逐像素的指定表面是金屬仍是電介質。根據PBR引擎各自的設定,金屬程度便可以是[0.0,1.0]區間的浮點值也能夠是非0即1的布爾值。
粗糙度(Roughness):粗糙度貼圖逐像素的指定了表面有多粗糙,粗糙度的值影響了材質表面的微平面的平均朝向,粗糙的表面上反射效果更大更模糊,光滑的表面更亮更清晰。有些PBR引擎用光滑度貼圖替代粗糙度貼圖,由於他們以爲光滑度貼圖更直觀,將採樣出來的光滑度使用(1-光滑度)= 粗糙度 就能轉換成粗糙度了。
環境光遮擋(Ambient Occlusion,AO):AO貼圖爲材質表面和幾何體周邊可能的位置,提供了額外的陰影效果。好比有一面磚牆,在兩塊磚之間的縫隙裏Albedo貼圖包含的應該是沒有陰影的顏色信息,而讓AO貼圖來指定這一塊須要更暗一些,這個地方光線更難照射到。AO貼圖在光照計算的最後一步使用能夠顯著的提升渲染效果,模型或者材質的AO貼圖通常是在建模階段手動生成的。
美術能夠直接根據物體在真實世界裏的物理屬性,來設置和調整用於渲染的基於物理的材質。
基於物理的渲染管線最大的優點在於,材質的物理屬性是不變的,不管環境光怎麼樣設置都能獲得一個接近真實的渲染結果,這讓美術的人生都變得美好了。
基於物理管線的材質能夠很簡單的移植到不一樣的渲染引擎,無論光照環境如何都能正確的渲染出一個天然的結果。
3.1章節闡述了Cook-Torrance反射方程的理論和公式意義。這節將探討如何將前面講到的理論轉化成一個基於直接光照的渲染器:好比點光源,方向光和聚光燈。
3.1章節解釋了Cook-Torrance反射方程的大部分含義,但有一點未說起:具體要怎麼處理場景中的輻照度(Irradiance,也就是輻射的總能量\(L\))?在計算機領域,場景的輻射率\(L\)度量的是來自光源光線的輻射通量\(\phi\)穿過指定的立體角\(\omega\),在這裏咱們假設立體角\(\omega\)無限小,小到輻射度衡量的是光源射出的一束通過指定方向向量的光線的通量。
有了這個假設,咱們又要怎麼將之融合到以前教程講的光照計算裏去呢?想象咱們有一個輻射通量以RGB表示爲(23.47, 21.31, 20.79)的點光源,這個光源的輻射強度等於輻射通量除以全部出射方向。當爲平面上某個特定的點\(p\)着色的時候,全部可能的入射光方向都會通過半球\(\Omega\),但只有一個入射方向\(\omega_i\)是直接來自點光源的,又由於咱們的場景中只包含有一個光源,且這個光源只是一個點,因此\(p\)點全部其它的入射光方向的輻射率都應該是0.
若是咱們暫時不考慮點光源的距離衰減問題,且不管光源放在什麼地方入射光線的輻射率都同樣大(忽略入射光角度\(\cos \theta\)對輻射度的影響),又由於點光源朝各個方向的輻射強度都是同樣的,那麼有效的輻射強度就跟輻射通量徹底同樣:恆定值(23.47, 21.31, 20.79)。
然而,輻射率須要使用位置\(p\)做爲輸入參數,由於現實中的燈光根據點\(p\)和光源之間距離的不一樣,輻射強度多少都會有必定的衰減。另外,從原始的輻射方程中咱們能夠發現,面法線\(n\)於入射光方向向量\(\omega_i\)的點積也會影響結果。
用更精煉的話來描述:在點光源直接光照的狀況裏,輻射率函數\(L\)計算的是燈光顏色,通過到\(p\)點距離的衰減以後,再通過\(n \cdot \omega_i\)縮放。能擊中點\(p\)的光線方向\(\omega_i\)就是從\(p\)點看向光源的方向。把這些寫成代碼:
vec3 lightColor = vec3(23.47, 21.31, 20.79); vec3 wi = normalize(lightPos - fragPos); float cosTheta = max(dot(N, Wi), 0.0); // 計算光源在點fragPos的衰減係數 float attenuation = calculateAttenuation(fragPos, lightPos); // 英文原版的radiance類型有誤,將它改爲了vec3 vec3 radiance = lightColor * (attenuation * cosTheta);
你應該很是很是熟悉這段代碼:這就是之前咱們計算漫反射光的算法!在只有單光源直接光照的狀況下,輻射率的計算方法跟咱們之前的光照算法是相似的。
要注意咱們這裏假設點光源無限小,只是空間中的一個點。若是咱們使用有體積的光源模型,那麼就有不少的入射光方向的輻射率是非0的。
對那些基於點的其餘類型光源咱們能夠用相似的方法計算輻射率,好比平行光源的入射角的恆定的且沒有衰減因子,聚光燈沒有一個固定的輻射強度,而是圍繞一個正前方向量來進行縮放的。
這也將咱們帶回了在表面半球\(\Omega\)的積分\(\int\)。咱們知道,多個單一位置的光源對同一個表面的同一個點進行光照着色並不須要用到積分,咱們能夠直接拿出這些數目已知的光源來,分別計算這些光源的輻照度後再加到一塊兒,畢竟每一個光源只有一束方向光能影響物體表面的輻射率。這樣只須要經過相對簡單的循環計算每一個光源的貢獻就能完成整個PBR光照計算。當咱們須要使用IBL將環境光加入計算的時候咱們纔會須要用到積分,由於環境光可能來自任何方向。
咱們先從寫一個能知足前面講到的PBR模型的片源着色器開始。首先,咱們須要將表面的PBR相關屬性輸入着色器:
#version 330 core out vec4 FragColor; in vec2 TexCoords; in vec3 WorldPos; in vec3 Normal; uniform vec3 camPos; uniform vec3 albedo; uniform float metallic; uniform float roughness; uniform float ao;
咱們能從頂點着色器拿到常見的輸入,另一些是物體表面的材質屬性。
在片源着色器開始的時候,咱們先要作一些全部光照算法都須要作的計算:
void main() { vec3 N = normalize(Normal); vec3 V = normalize(camPos - WorldPos); [...] }
在這個教程的示例中,咱們將會有4個點光源做爲場景輻照度來源。爲了知足反射方程咱們循環處理每個光源,計算它獨自的輻射率,而後加總通過BRDF跟入射角縮放的結果。咱們能夠把這個循環看成是積分運算的一種實現方案。首先,計算每一個光源各自相關參數:
vec3 Lo = vec3(0.0); for(int i = 0; i < 4; ++i) { vec3 L = normalize(lightPositions[i] - WorldPos); vec3 H = normalize(V + L); float distance = length(lightPositions[i] - WorldPos); float attenuation = 1.0 / (distance * distance); vec3 radiance = lightColors[i] * attenuation; [...] // 還有邏輯放在後面繼續探討,因此故意在for循環缺了‘}’。
因爲咱們是在線性空間進行的計算(在最後階段處理Gamma校訂),因此光源的衰減會更符合物理上的反平方律(inverse-square law)。
反平方律雖然物理學正確,但咱們可能還會使用常量、線性、二次方程式來更好地控制光照衰減,即使這些衰減不是物理學正確的。
而後,咱們對每一個光源計算全部的Cook-Torrance BRDF份量:
\[ \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} \]
咱們要作的第一件事是計算高光跟漫反射之間的比例,有多少光被反射出去了又有多少產生了折射。前面的教程咱們講到過這個菲涅爾方程:
vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); }
Fresnel-Schlick算法須要的F0
參數就是咱們以前說的基礎反射率,即以0度角照射在表面上的光被反射的比例。不一樣材質的F0
的值都不同,能夠根據材質到那張很是大的材質表裏去找。在PBR金屬度流水線中咱們作了一個簡單的假設,咱們認爲大部分的電介質表面的F0
用0.04效果看起來很不錯。而金屬表面咱們將F0
放到albedo紋理內,這些能夠寫成代碼以下:
vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, metallic); vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
如上述代碼所見,非金屬的F0
永遠是0.04,除非咱們經過金屬度屬性在F0
跟albedo
之間進行線性插值,才能獲得一個不一樣的非金屬F0
。
有了F
,還剩下法線分佈函數\(D\)跟幾何函數\(G\)須要計算。
在直接光照的PBR光照着色器中它們等價於以下代碼:
float DistributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness*roughness; float a2 = a*a; float NdotH = max(dot(N, H), 0.0); float NdotH2 = NdotH*NdotH; float num = a2; float denom = (NdotH2 * (a2 - 1.0) + 1.0); denom = PI * denom * denom; return num / denom; } float GeometrySchlickGGX(float NdotV, float roughness) { float r = (roughness + 1.0); float k = (r*r) / 8.0; float num = NdotV; float denom = NdotV * (1.0 - k) + k; return num / denom; } float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; }
這裏值得注意的是,相較於3.1理論篇教程,咱們直接傳入了粗糙度參數進函數。這樣咱們就能夠對原始粗糙度作一些特殊操做。根據迪斯尼的原則和Epic Games的用法,在法線分佈函數跟幾何函數中使用粗糙度的平方替代原始粗糙度進行計算光照效果會更正確一些。
當這些都定義好了以後,在計算NDF和G份量就是很簡單的事情了:
float NDF = DistributionGGX(N, H, roughness); float G = GeometrySmith(N, V, L, roughness);
而後就能夠計算Cook-Torrance BRDF了:
vec3 numerator = NDF * G * F; float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); vec3 specular = numerator / max(denominator, 0.001);
denominator
項裏的0.001
是爲了防止除0狀況而特地加上的。
到這裏,咱們終於能夠計算每一個光源對反射方程的貢獻了。由於菲涅爾值至關於\(k_S\),可用F
表明任意光擊中表面後被反射的部分,根據能量守恆定律咱們能夠用\(k_S\)直接計算獲得\(k_D\):
vec3 kS = F; vec3 kD = vec3(1.0) - kS; kD *= 1.0 - metallic; // 因爲金屬表面不折射光,沒有漫反射顏色,經過歸零kD來實現這個規則
\(k_S\)表示的是光能有多少被反射了,剩下的被折射的光能咱們用\(k_D\)來表示。此外,因爲金屬表面不折射光,所以沒有漫反射顏色,咱們經過歸零\(k_D\)來實現這個規則。
有了這些數據,咱們終於能夠算出每一個光源的出射光了:
const float PI = 3.14159265359; float NdotL = max(dot(N, L), 0.0); Lo += (kD * albedo / PI + specular) * radiance * NdotL; }
最終結果Lo
,或者說出射輻射度(Radiosity),其實是反射方程在半球\(\Omega\)的積分\(\int\)的結果。這裏要特別注意的是,咱們將\(k_S\)移除方程式,是由於咱們已經在BRDF中乘過菲涅爾參數F
了,此處不須要再乘一次。
咱們沒有真正的對全部可能的入射光方向進行積分,由於咱們已經清楚的知道只有4個入射方向能夠影響這個片元,因此咱們只須要直接用循環處理這些入射光就好了。
剩下的就是要將AO運用到光照結果Lo
上,咱們就能夠獲得這個片元的最終顏色了:
vec3 ambient = vec3(0.03) * albedo * ao; vec3 color = ambient + Lo;
以上咱們假設全部計算都在線性空間,爲了使用這個結果咱們還須要在着色器的最後進行伽馬校訂(Gamma Correct),在線性空間計算光照對於PBR是很是很是重要的,全部輸入參數一樣要求是線性的,不考慮這一點將會獲得錯誤的光照結果。
另外,咱們但願輸入的燈光參數更貼近實際的物理參數,好比他們的輻射度或者顏色值能夠是一個很是寬廣的值域。這樣做爲結果輸出的Lo
也將變得很大,若是咱們不作處理默認會直接Clamp到0.0至1.0之間以適配低動態範圍(LDR)輸出方式。
爲了有效解決Lo
的值域問題,咱們能夠使用色調映射(Tone Map)和曝光控制(Exposure Map),用它們將Lo
的高動態範圍(HDR)映射到LDR以後再作伽馬校訂:
color = color / (color + vec3(1.0)); // 色調映射 color = pow(color, vec3(1.0/2.2)); // 伽馬校訂
這裏咱們使用的是萊因哈特算法(Reinhard operator)對HDR進行Tone Map操做,儘可能在伽馬矯正以後還保持高動態範圍。咱們並無分開幀緩衝或者使用後處理,因此咱們能夠直接將Tone Mapping和伽馬矯正放在前向片元着色器(forward fragment shader)。
對於PBR渲染管線來講,線性空間跟高動態範圍有着超乎尋常的重要性,沒有這些就不可能繪製出不一樣燈光強度下的高光低光細節,錯誤的計算結果會產生難看的渲染效果。
如今惟一剩下的就是將最終的色調映射和伽瑪校訂的顏色傳遞給片元着色器的輸出通道,咱們就擁有了一個PBR直接光照着色器。基於完整性考慮,下面列出完整的main
函數:
#version 330 core out vec4 FragColor; in vec2 TexCoords; in vec3 WorldPos; in vec3 Normal; // material parameters uniform vec3 albedo; uniform float metallic; uniform float roughness; uniform float ao; // lights uniform vec3 lightPositions[4]; uniform vec3 lightColors[4]; uniform vec3 camPos; const float PI = 3.14159265359; // -------------------------------------------------------------------- float DistributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness*roughness; 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 / max(denom, 0.001); // prevent divide by zero for roughness=0.0 and NdotH=1.0 } // -------------------------------------------------------------------- float GeometrySchlickGGX(float NdotV, float roughness) { float r = (roughness + 1.0); float k = (r*r) / 8.0; float nom = NdotV; float denom = NdotV * (1.0 - k) + k; return nom / denom; } // -------------------------------------------------------------------- float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; } // -------------------------------------------------------------------- vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } // -------------------------------------------------------------------- void main() { vec3 N = normalize(Normal); vec3 V = normalize(camPos - WorldPos); // calculate reflectance at normal incidence; if dia-electric (like plastic) use F0 // of 0.04 and if it's a metal, use the albedo color as F0 (metallic workflow) vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, metallic); // reflectance equation vec3 Lo = vec3(0.0); for(int i = 0; i < 4; ++i) { // calculate per-light radiance vec3 L = normalize(lightPositions[i] - WorldPos); vec3 H = normalize(V + L); float distance = length(lightPositions[i] - WorldPos); float attenuation = 1.0 / (distance * distance); vec3 radiance = lightColors[i] * attenuation; // Cook-Torrance BRDF float NDF = DistributionGGX(N, H, roughness); float G = GeometrySmith(N, V, L, roughness); vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); vec3 nominator = NDF * G * F; float denominator = 4 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); vec3 specular = nominator / max(denominator, 0.001); // prevent divide by zero for NdotV=0.0 or NdotL=0.0 // kS is equal to Fresnel vec3 kS = F; // for energy conservation, the diffuse and specular light can't // be above 1.0 (unless the surface emits light); to preserve this // relationship the diffuse component (kD) should equal 1.0 - kS. vec3 kD = vec3(1.0) - kS; // multiply kD by the inverse metalness such that only non-metals // have diffuse lighting, or a linear blend if partly metal (pure metals // have no diffuse light). kD *= 1.0 - metallic; // scale light by NdotL float NdotL = max(dot(N, L), 0.0); // add to outgoing radiance Lo Lo += (kD * albedo / PI + specular) * radiance * NdotL; // note that we already multiplied the BRDF by the Fresnel (kS) so we won't multiply by kS again } // ambient lighting (note that the next IBL tutorial will replace // this ambient lighting with environment lighting). vec3 ambient = vec3(0.03) * albedo * ao; vec3 color = ambient + Lo; // HDR tonemapping color = color / (color + vec3(1.0)); // gamma correct color = pow(color, vec3(1.0/2.2)); FragColor = vec4(color, 1.0); }
但願在學習了前面教程的反射方程的理論知識以後,這個shader再也不會讓你們苦惱。使用這個shader,4個點光源照射在金屬度和粗糙度不一樣的球上的效果大概相似這樣:
從下往上金屬度的值從0.0到1.0,粗糙度從左往右從0.0增長到1.0。能夠經過觀察小球之間的區別理解金屬度和粗糙度參數的做用。
示例的源碼能夠從LearnOpenGL的網站找到。
3.2.2.3小節的PBR實現中,部分重要的表面材質屬性是float
類型:
uniform float metallic; uniform float roughness; uniform float ao;
實際上,能夠將它們用紋理代替,使用紋理的PBR能夠更加精確地控制表面材質的細節,使得渲染效果更佳。Unity支持這種方法。
爲了實現逐像素的控制材質表面的屬性咱們必須使用紋理替代單個的材質參數:
[...] uniform sampler2D albedoMap; uniform sampler2D normalMap; uniform sampler2D metallicMap; uniform sampler2D roughnessMap; uniform sampler2D aoMap; void main() { vec3 albedo = pow(texture(albedoMap, TexCoords).rgb, 2.2); vec3 normal = getNormalFromNormalMap(); float metallic = texture(metallicMap, TexCoords).r; float roughness = texture(roughnessMap, TexCoords).r; float ao = texture(aoMap, TexCoords).r; [...] }
要注意美術製做的albedo紋理通常都是sRGB空間的,所以咱們要先轉換到線性空間再進行後面的計算。根據美術資源的不一樣,AO紋理也許一樣須要從sRGB轉換到線性空間。
將前面那些小球的材質屬性替換成紋理以後,對比之前用的光照算法,PBR有了一個質的提高:
能夠在這裏找到帶紋理的Demo源碼,全部用到的紋理在這裏(用了白色的AO貼圖)。記住金屬表面在直接光照環境中更暗是由於他們沒有漫反射。在環境使用環境高光進行光照計算的狀況下看起來也是正常的,這個咱們在下一個教程裏再說。
這裏沒有其餘PBR渲染示例中那樣使人驚豔的效果,由於咱們尚未加入基於圖片的光照(Image Based Lighting)技術。儘管如此,這個shader任然算是一個基於物理的渲染,即便沒有IBL你也能夠法線光照看起來真實了不少。
基於圖像的光照(IBL)是對光源物體的技巧集合,與直接光照不一樣,它將周圍環境當成一個大光源。IBL一般結合cubemap環境貼圖,cubemap一般採集自真實的照片或從3D場景生成,這樣能夠將其用於光照方程:將cubemap的每一個像素當成一個光源。這樣能夠更有效地捕獲全局光照和常規感觀,使得被渲染的物體更好地融入所處的環境中。
當基於圖像的光照算法得到一些(全局的)環境光照時,它的輸入被當成更加精密形式的環境光照,甚至是一種粗糙的全局光照的模擬。這使得IBL有助於PBR的渲染,使得物體渲染效果更真實。
在介紹IBL結合PBR以前,先回顧一下反射方程:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
如以前所述,咱們的主目標是解決全部入射光\(w_i\)經過半球\(\Omega\)的積分\(\int\)。與直接光照不一樣的是,在IBL中,每個來自周圍環境的入射光\(\omega_i\)均可能存在輻射,這些輻射對解決積分有着重要的做用。爲解決積分有兩個要求:
對第一個要求,相對簡單,採用環境cubemap。給定一個cubemap,能夠假設它的每一個像素是一個單獨的發光光源。經過任意方向向量\(\omega_i\)採樣cubemap,能夠得到場景在這個方向的輻射。
獲取任意方向向量\(\omega_i\)的場景輻射很簡單,以下:
vec3 radiance = texture(_cubemapEnvironment, w_i).rgb;
對要求二,解決積分能只考慮一個方向的輻射,要考慮環境貼圖的半球\(\Omega\)的全部可能的方向\(\omega_i\),但常規積分方法在片元着色器中開銷很是大。爲了有效解決積分問題,可採用預計算或預處理的方法。所以,須要深究一下反射方程:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
可將上述的\(k_d\)和\(k_s\)項拆分:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i+ \int\limits_{\Omega} (k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
拆分後,可分開處理漫反射和鏡面反射的積分。先從漫反射積分開始。
仔細分析上面方程的漫反射積分部分,發現Lambert漫反射是個常量項(顏色\(c\),折射因子\(k_d\)和\(\pi\))而且不依賴積分變量。所以,可見常量部分移出漫反射積分:
\[ L_o(p,\omega_o) = k_d\frac{c}{\pi} \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
所以,積分只依賴\(\omega_i\)(假設\(p\)在環境貼圖的中心)。據此,能夠計算或預計算出一個新的cubemap,這個cubemap存儲了用卷積(convolution)計算出的每一個採樣方向(或像素)\(\omega_o\)的漫反射積分結果。
卷積(convolution)是對數據集的每一個入口應用一些計算,假設其它全部的入口都在這個數據集裏。此處的數據集就是場景輻射或環境圖。所以,對cubemap的每一個採樣方向,咱們能夠顧及在半球\(\Omega\)的其它全部的採樣方向。
爲了卷積環境圖,咱們要解決每一個輸出\(\omega_o\)採樣方向的積分,經過離散地採樣大量的在半球\(\Omega\)的方向\(\omega_i\)並取它們輻射的平均值。採樣方向\(\omega_i\)的半球是以點\(p\)爲中心以\(\omega_o\)爲法平面的。
這個預計算的爲每一個採樣方向\(\omega_o\)存儲了積分結果的cubemap,可被當成是預計算的在場景中全部的擊中平行於\(\omega_o\)表面的非直接漫反射的光照之和。這種cubemap被稱爲輻照度圖(Irradiance map)。
輻射方程依賴於位置\(p\),假設它在輻照度圖的中心。這意味着全部非直接漫反射光需來自於同一個環境圖,它可能打破真實的幻覺(特別是室內)。渲染引擎用放置遍及場景的反射探頭(reflection probe)來解決,每一個反射探頭計算其所處環境的獨自的輻照度圖。這樣,點p的輻射率(和輻射)是與其最近的反射探頭的輻照度插值。這裏咱們假設老是在環境圖的中心採樣。反射探頭將在其它章節探討。
下面是cubemap環境圖(下圖左)和對應的輻照度圖(下圖右):
經過存儲每一個cubemap像素卷積的結果,輻照度圖有點像環境的平均顏色或光照顯示。從這個環境圖採樣任意方向,可得到這個方向的場景輻照度。
球體圖(Equirectangular map)有些文獻翻譯成全景圖,它與cubemap不同的是:cubemap須要6張圖,而球體圖只須要一張,而且存儲的貼圖有必定形變:
cubemap是能夠經過必定算法轉成球體圖的,詳見這裏。
直接從球體圖採樣出環境光照信息是可能的,但它的開銷遠大於直接採樣立方體圖(cubemap)。所以,須要將球體圖先轉成立方體圖,以便更好地實現後面的邏輯。固然,這裏也會闡述如何從做爲3D環境圖的球體圖採樣,以便你們有更多的選擇權。
爲了將球體圖映射到立方體圖,首先須要構建一個立方體模型,渲染這個立方體模型的頂點着色器以下:
#version 330 core layout (location = 0) in vec3 aPos; out vec3 localPos; uniform mat4 projection; uniform mat4 view; void main() { localPos = aPos; gl_Position = projection * view * vec4(localPos, 1.0); }
在像素着色器中,將會對變形的球體圖的每一個部位映射到立方體的每一邊,具體實現以下:
#version 330 core out vec4 FragColor; in vec3 localPos; uniform sampler2D equirectangularMap; const vec2 invAtan = vec2(0.1591, 0.3183); vec2 SampleSphericalMap(vec3 v) { vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); uv *= invAtan; uv += 0.5; return uv; } void main() { // make sure to normalize localPos vec2 uv = SampleSphericalMap(normalize(localPos)); vec3 color = texture(equirectangularMap, uv).rgb; FragColor = vec4(color, 1.0); }
渲染出來的立方體效果以下:
對於立方體圖的採樣,頂點着色器以下:
#version 330 core layout (location = 0) in vec3 aPos; uniform mat4 projection; uniform mat4 view; out vec3 localPos; void main() { localPos = aPos; // remove translation from the view matrix mat4 rotView = mat4(mat3(view)); vec4 clipPos = projection * rotView * vec4(localPos, 1.0); gl_Position = clipPos.xyww; // 注意這裏的份量是`xyww`!! }
對於立方體圖的採樣,像素着色器以下:
#version 330 core out vec4 FragColor; in vec3 localPos; uniform samplerCube environmentMap; void main() { // 從cubemap採樣顏色 vec3 envColor = texture(environmentMap, localPos).rgb; // HDR -> LDR envColor = envColor / (envColor + vec3(1.0)); // Gamma校訂(只在顏色爲線性空間的渲染管線才須要) envColor = pow(envColor, vec3(1.0/2.2)); FragColor = vec4(envColor, 1.0); }
上述代碼中,要注意在輸出最終的顏色以前,作了HDR到LDR的轉換和Gamma校訂。
渲染的效果以下圖:
輻射度圖提供了漫反射部分的積分,該積分表示來自非直接的全部方向的環境光輻射之和。因爲輻射度圖被當成是無方向性的光源,因此能夠將漫反射鏡面反射合成環境光。
首先,得聲明預計算出的輻射度圖的sample:
uniform samplerCube irradianceMap;
經過表面的法線,得到環境光能夠簡化成下面的代碼:
// vec3 ambient = vec3(0.03); vec3 ambient = texture(irradianceMap, N).rgb;
儘管如此,在以前所述的反射方程中,非直接光依舊包含了漫反射和鏡面反射兩個部分,因此咱們須要加個權重給漫反射。下面採用了菲涅爾方程來計算漫反射因子:
vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0); vec3 kD = 1.0 - kS; vec3 irradiance = texture(irradianceMap, N).rgb; vec3 diffuse = irradiance * albedo; vec3 ambient = (kD * diffuse) * ao;
因爲環境光來自在半球內全部圍繞着法線N
的方向,沒有單一的半向量去決定菲涅爾因子。爲了仍然能模擬菲涅爾,這裏採用了法線和視線的夾角。以前的算法採用了受表面粗糙度影響的微平面半向量,做爲菲涅爾方程的輸入。這裏,咱們並不考慮粗糙度,表面的反射因子被視做至關大。
非直接光照將沿用直接光照的相同的屬性,因此,指望越粗糙的表面鏡面反射越少。因爲不考慮表面粗糙度,非直接光照的菲涅爾方程強度被視做粗糙的非金屬表面(下圖)。
爲了緩解這個問題,可在Fresnel-Schlick方程注入粗糙度項(該方程的來源):
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) { return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); }
考慮了表面粗糙度後,菲涅爾相關計算最終以下:
vec3 kS = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec3 kD = 1.0 - kS; vec3 irradiance = texture(irradianceMap, N).rgb; vec3 diffuse = irradiance * albedo; vec3 ambient = (kD * diffuse) * ao;
如上所述,實際上,基於圖片的光照計算很是簡單,只須要單一的cubemap紋理採樣。大多數的工做在於預計算或卷積環境圖到輻射度圖。
加入了IBL的渲染效果以下(豎向是金屬度增長,水平是粗糙度增長):
本節全部代碼可在這裏找到。
3.3.1 描述的是IBL的漫反射部分,本節將討論IBL的鏡面反射部分先回顧一下反射方程:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} (k_d\frac{c}{\pi} + k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
上述的鏡面反射部分(被\(k_s\)相乘)不是恆定的,而且依賴於入射光方向和視線入射方向,嘗試實時地計算全部入射光和全部入射視線的積分是幾乎不可能的。Epic Games推薦折中地使用預卷積鏡面反射部分的方法來解決實時渲染的性能問題,這就是分裂和近似法(split sum approximation)。
分裂和近似法將鏡面反射部分從反射方程分離出兩個部分,這樣能夠單獨地對它們卷積,後面在PBR的shader中爲鏡面的非直接IBL將它們結合起來。跟預卷積輻射度圖相似,分裂和近似法須要HDR環境圖做爲輸入。爲了更好地理解分裂和近似法,下面着重關注反射方程的鏡面部分:
\[ \begin{eqnarray*} L_o(p,\omega_o) & = & \int\limits_{\Omega} (k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} L_i(p,\omega_i) n \cdot \omega_i d\omega_i \\ & = & \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \end{eqnarray*} \]
出於跟輻射度圖相同的性能問題的考慮,咱們要預計算相似鏡面IBL圖的積分,而且用片元的法線採樣這個圖。輻射度圖的預計算只依賴於\(\omega_i\),而且咱們能夠將漫反射項移出積分。但此次從BRDF能夠看出,不只僅是依賴於\(\omega_i\):
\[ f_r(p, w_i, w_o) = \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} \]
如上方程所示,還依賴\(\omega_o\),而且咱們不能用兩個方向向量來採樣預計算的cubemap。預計算全部\(\omega_i\)和\(\omega_o\)的組合在實時渲染環境中不實際的。
Epic Games的分裂和近似法將鏡面反射部分從反射方程分離出兩個部分,這樣能夠單獨地對它們卷積,後面在PBR的shader中爲鏡面的非直接IBL將它們結合起來。分離後的方程以下:
\[ \begin{eqnarray*} L_o(p,\omega_o) & = & \int\limits_{\Omega} (k_s\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)} L_i(p,\omega_i) n \cdot \omega_i d\omega_i \\ & = & \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \\ & = & \int\limits_{\Omega} L_i(p,\omega_i) d\omega_i * \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) n \cdot \omega_i d\omega_i \end{eqnarray*} \]
第一部分\(\int\limits_{\Omega} L_i(p,\omega_i) d\omega_i\)是預過濾環境圖(pre-filtered environment map),相似於輻射度圖的預計算環境卷積圖,但會加入粗糙度。隨着粗糙度等級的增長,環境圖使用更多的散射採樣向量來卷積,建立出更模糊的反射。
對每一個卷積的粗糙度等級,循環地在預過濾環境圖的mimap等級存儲更加模糊的結果。下圖是5個不一樣粗糙度等級的預過濾環境圖:
生成採樣向量和它們的散射強度,須要用到Cook-Torrance BRDF的法線分佈圖(NDF),而其帶了兩個輸入:法線和視線向量。當卷積環境圖時並不知道視線向量,Epic Games用了更近一步的模擬法:假設視線向量(亦即鏡面反射向量)老是等於輸出採樣向量\(\omega_o\)。因此代碼變成以下所示:
vec3 N = normalize(w_o); vec3 R = N; vec3 V = R;
這種方式預過濾環境圖卷積不須要關心視線方向。這就意味着當從某個角度看向下面這張圖的鏡面表面反射時,沒法得到很好的掠射鏡面反射(grazing specular reflections)。然而一般這被認爲是一個較好的妥協:
第二部分\(\int\limits_{\Omega} f_r(p, \omega_i, \omega_o) n \cdot \omega_i d\omega_i\)是鏡面積分。假設全部方向的入射輻射率是全白的(那樣\(L(p, x) = 1.0\)),那就能夠用給定的粗糙度和一個法線\(n\)和光源方向\(\omega_i\)之間的角度或\(n \cdot \omega_i\)來預計算BRDF的值。Epic Games存儲了用變化的粗糙度來預計算每個法線和光源方向組合的BRDF的值,該粗糙度存儲於2D採樣紋理(LUT)中,它被稱爲BRDF積分圖(BRDF integration map)。
2D採樣紋理輸出一個縮放(紅色)和一個偏移值(綠色)給表面的菲涅爾方程式(Fresnel response),以便提供第二部分的鏡面積分:
上圖水平表示BRDF的輸入\(n \cdot \omega_i\),豎向表示輸入的粗糙度。
有了預過濾環境圖和BRDF積分圖,能夠在shader中將它們結合起來:
float lod = getMipLevelFromRoughness(roughness); vec3 prefilteredColor = textureCubeLod(PrefilteredEnvMap, refVec, lod); vec2 envBRDF = texture2D(BRDFIntegrationMap, vec2(NdotV, roughness)).xy; vec3 indirectSpecular = prefilteredColor * (F * envBRDF.x + envBRDF.y)
首先是聲明IBL鏡面部分的兩個紋理採樣器:
uniform samplerCube prefilterMap; uniform sampler2D brdfLUT;
接着用法線N
和視線-V
算出反射向量R
,再結合MAX_REFLECTION_LOD
和粗糙度等參數採樣預過濾環境圖:
void main() { [...] vec3 R = reflect(-V, N); const float MAX_REFLECTION_LOD = 4.0; vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; [...] }
而後用視線、法線的夾角及粗糙度採樣BRDF查找紋理,結合預過濾環境圖的顏色算出IBL的鏡面部分:
vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y);
自此,反射方程的非直接的鏡面部分已經算出來了。能夠將它和上一小節的IBL的漫反射部分結合起來:
vec3 F = FresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); vec3 kS = F; vec3 kD = 1.0 - kS; kD *= 1.0 - metallic; vec3 irradiance = texture(irradianceMap, N).rgb; vec3 diffuse = irradiance * albedo; const float MAX_REFLECTION_LOD = 4.0; vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; vec2 envBRDF = texture(brdfLUT, vec2(max(dot(N, V), 0.0), roughness)).rg; vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); vec3 ambient = (kD * diffuse + specular) * ao;
此時能夠算出由IBL的漫反射和鏡面反射部分結合而成的環境光ambient
,渲染效果以下:
擴展一下,加入一些酷酷的材質:
或者加載這些極好又免費的PBR 3D模型(by Andrew Maximov):
很是確定地,加了IBL光照後,渲染效果更真實更加物理正確。下圖展現了在未改變任何光照信息的狀況下,在不一樣的預計算HDR圖中的效果,它們看起來依然是物理正確的:
IBL的教程結束了,本節的代碼可在球體場景和紋理場景中找到。
上章主要介紹了PBR中Cook-Torrance的BRDF的兩個部分:直接光照和IBL。
這章將深刻介紹PBR核心部分的底層理論和原理,使讀者對PBR的底層原理有更完全的理解。本章部份內容在上一章已經有所涉及,但會更加深刻。
主要面向:
上章講述了符合PBR必須知足如下3個條件:
如果將上面3點進一步詳細論述,將涉及如下知識點:
總結起來,PBR就是光學原理和物體結構交互做用的抽象和模擬。下面先從光的性質提及。
有人說光是粒子,有人說光是電磁,有人說光是一種波,有人說光是一種能量,還有人說光是量子,那麼光究竟是什麼?
狹義上說,光是電磁輻射的某一部份內人眼可見的電磁頻譜,便可見光,它是人眼可感知的可見光譜,是形成視覺的緣由。
可見光一般被定義爲具備波長在400-700納米(nm)的範圍內,不可見的有紅外線(具備更長的波長)和紫外線(具備更短的波長)。
廣義上說,光指的是任何波長的電磁輻射,不管是否可見。包括伽馬射線、X射線、微波和無線電波。而可見光(400-700納米)只是全部波長區域的一小部分:
電磁輻射(Electromagnetic Radiation,EMR)按波長從長到短分爲:無線電波、微波、紅外線、可見光、紫外線、X射線和伽瑪射線。
EMR的行爲取決於其波長。較高頻率具備較短波長,較低頻率具備較長波長。不一樣波長的電磁輻射攜帶着不一樣的能量。當EMR與單個原子和分子相互做用時,其行爲取決於它攜帶的每一個量子的能量。
不一樣波長的可見光表明着不一樣的顏色。太陽光、日光燈等可見光是一組不一樣波長的電磁輻射的集合,在三棱鏡下能夠被分離出不一樣的顏色:
不一樣來源對可見光的定義略有不一樣,有的將可見光定義爲狹窄的420-680nm,有的寬達380-800nm。在理想的實驗室條件下,人們能夠看到至少1050納米的紅外線; 兒童和年輕人可能會感知波長低至約310-313納米的紫外線。
上節闡述了可見光的範圍和簡單的感知理論,本小節將深刻闡述人類爲何會感知而且只感知波長爲380-800納米的可見光。
首先要了解人眼的結構和視覺的分子機制。
人眼的結構相似於一架高精度的照相機,光線穿過透明的角膜(cornea)和虹膜(iris)包圍的瞳孔(pupil),通過晶狀體(lens)的折射在視網膜(retina)上造成空間分佈的像。而視網膜上則分佈着主要檢測光強度的視杆細胞(rod cell)和主要檢測顏色的視錐細胞(cone cell),它們是視覺造成的細胞基礎。
視杆細胞與視錐細胞對光的響應程度雖然略有差別,但它們發生光響應的機制都是相似的。以視杆細胞上的視紫紅質(rhodopsin)爲例,它由一個細胞膜上的七次跨膜蛋白(視蛋白,opsin)和視黃醛(retinal)輔基組成。視蛋白是G蛋白偶聯受體(GPCR)的一種,視黃醛輔基以共價鍵結合在其第七個跨膜\(\alpha\)螺旋片斷的賴氨酸殘基上。
視黃醛分子是由維生素 A 氧化而來的,一個維生素 A 分子氧化獲得一個視黃醛。視黃醛具備兩種構型:11 位順式(11-cis)和 全反式(All-trans),正常與視蛋白結合的是 11 位順式構型。恰巧在可見光(對視紫紅質而言是波長 500 nm 左右的電磁波)照射下,11 位順式構型能夠轉變爲全反式構型,從而致使視黃醛輔基從視蛋白上脫離。輔基的脫離形成視紫紅質構象變化,通過信號轉導致使細胞膜內外離子電位發生變化,產生神經電信號。這一信號通過視神經傳入大腦,就使得咱們產生了視覺。
(a)視紫紅質的結構;(b)視黃醛分子的光敏異構反應。
所以,從視覺的分子機制出發,能夠這麼回答:正因爲視黃醛分子的構型轉變反應剛好響應了可見光波段的電磁波,這才致使這一波段的電磁波能被人類 「看見」。
此外,視黃醛分子是維生素 A 的部分氧化產物,又可由植物中普遍存在的自然色素——β-胡蘿蔔素氧化獲得,來源和代謝路徑明確,被大多數生物進化選中做爲光敏分子也在情理之中。
那爲何高度進化的人類不能感知可見光譜以外的電磁輻射呢?爲何視黃醛分子恰好只對可見波段的電磁波產生反應?
這個能夠從各個波段的電磁輻射的性質來回答。
波長最短的伽馬射線(Gamma ray)和高能X射線(X ray)因爲攜帶的能量(光子)過高,很快就會致使分子電離、分解甚至激發原子核(致使原子核爆發)。首先被排除。
波長較短的深紫外(deep Ultraviolet)和軟 X 射線激發的電子能級通常是內層電子或高能電子,這種激發獲得的分子高能態很不穩定,在常溫下的水溶液或空氣中都難以保證信息的有效傳遞。也被排除。
波長較長的紅外(Infrared)與微波(Microwave)頻段的電磁波主要與分子的振動、轉動和平動相耦合,而這些運動主要以隨機熱運動形式存在,很難實現信息的準確表達。
波長更長的中波、長波(Radio)的運動尺度超過了單個分子可以接收的尺度,更不適合以細胞爲基礎的生物選擇。
這樣考察的結果,若是細胞必定要採起分子層面上的光敏機制對電磁波進行響應,那麼最合適的波段可能就是如今的可見光波段。這一波段在分子運動中至關於電子光譜的外層電子激發能量,與分子中化學鍵的能量高低大體至關而略低,既不至於損傷通常較爲穩定的化學鍵(尤爲是做爲生命體基礎的 C-C、C-H、C=O、C-N 等化學鍵),又能夠使得一些 「動態」 化學鍵(例如視黃醛中具備順反異構的 11 位雙鍵)發生光響應,並實現信息的有效傳遞。
因此,人類感知當前波段的可見光,是億萬年不斷進化的結果。換個角度說,感知其它波段的人類祖先已經被淘汰了,他們的基因沒法遺傳傳承下來。
可謂:物競天擇,適者生存。
衆所皆知,光是電磁波,而物質是由原子組成,原子是由原子核與核外運轉着的電子組成。那麼,物質原子中的電磁波是哪裏來的?電磁波難道會無中生有?
奧斯特實驗發現了直流導線的周圍產生磁場,由於電子的運動伴生着磁場。電子的運動分爲線性運動和振動:
本小節開頭的問題有了答案:光源中的光來自於電子的振動,電子振動所伴生的電磁波輻射造成了光波,電子振動的頻率構成了光波的頻率,大量電子振動所伴生的電磁波輻射造成了光源。
光的研究和理論通過數百年的發展,至今出了不少理論學說,每種理論都是爲了解釋部分光的物理現象。
目前,光存在的理論主要有:粒子理論、波動理論、電磁理論、量子理論及波粒二象性等。
光的粒子說又稱光的微粒說,這種理論認爲光的本質與經過它反射而可見的實體物質同樣,是一種粒子(下圖)。
光的粒子性示意圖
法國數學家皮埃爾·加森迪(Pierre Gassendi)於1660年提出了一種光的粒子理論。 Isaac Newton在Gassendi的理論基礎上作了擴展:光是由來自各個方向或從各個方向發射的微粒(物質粒子)組成的。
牛頓隨後對於加森迪的這種觀點進行研究,他根據光的直線傳播規律、光的偏振現象,最終於1675年提出假設,認爲光是從光源發出的一種物質微粒,在均勻媒質中以必定的速度傳播。
微粒說很容易解釋光的直進性,也很容易解釋光的反射,由於粒子與光滑平面發生碰撞的反射定律與光的反射定律相同。
然而微粒說在解釋一束光射到兩種介質分界面處會同時發生反射和折射,以及幾束光交叉相遇後彼此絕不妨礙的繼續向前傳播等現象時,卻發生了很大困難。
在1660年代,胡克(Robert Hooke)發表了他的光波動理論。他認爲光線在一個名爲光以太(Luminiferous ether)的介質中以波的形式四射,而且因爲波並不受重力影響,光在進入高密度介質時會減速。
光的波理論預言了干涉現象以及光的偏振性。
歐拉是波動學說的支持者之一,他認爲波理論更容易解釋衍射現象。
菲涅耳也支持並獨立完成了他的波動理論。在1821年,菲涅爾使用數學方法使光的偏振在波動理論上獲得了惟一解釋。
上圖:光的偏振現象,迴旋光波前後通過四分一波偏振板和線性偏振板的情形。
可是,波動理論的弱點在於,波相似於聲波,傳播須要介質。雖然曾有過光以太介質的假想,但由於19世紀邁克耳孫-莫雷實驗陷入了強烈的質疑。
牛頓推測光速在高密度下變高,惠更斯和其餘人以爲正相反,但當時並無準確測量光速的條件。直到1850年,萊昂·傅科(Léon Foucault)的實驗獲得了和波動理論一樣的結果。以後,經典粒子理論才真正被拋棄。
光的電磁理論是關於光的本性的一種現代學說,19世紀60年代由麥克斯韋提出。把光當作是頻率在某一範圍的電磁波。能解釋光的傳播、干涉、衍射、散射、偏振等現象,以及光與物質相互做用的規律。
電磁理論還認爲,電磁波具備互相垂直的電場與磁場,電場與磁場的頻率、振幅、波長、傳播方向是一致的。
但因爲光還具備粒子性,因此它不能解釋光電效應、康普頓效應等物理現象。
光的量子理論是以輻射的量子理論研究光的產生、傳輸、檢測及光與物質相互做用的學科。
量子光學示意圖
1900年,普朗克在研究黑體輻射時,爲了從理論上推導出獲得的與實際相符甚好的經驗公式,他大膽地提出了與經典概念迥然不一樣的假設,即「組成黑體的振子的能量不能連續變化,只能取一份份的分立值」。
1905年,愛因斯坦在研究光電效應時推廣了普朗克的上述量子論,進而提出了光子的概念。他認爲光能並不像電磁波理論所描述的那樣分佈在波陣面上,而是集中在所謂光子的微粒上。在光電效應中,當光子照射到金屬表面時,一次爲金屬中的電子所有吸取,而無需電磁理論所預計的那種累積能量的時間,電子把這能量的一部分用於克服金屬表面對它的吸力即做逸出功,餘下的就變成電子離開金屬表面後的動能。
1923年,亞瑟·霍利康普頓代表,當從電子散射的低強度X射線(所謂的康普頓散射)中看到的波長漂移能夠經過X射線的粒子理論來解釋,而不是波動理論。
1926年Gilbert N. Lewis將這些光量子粒子命名爲光子。
2018年2月,科學家首次報道了一種可能涉及極化子的新型光的發現,這可能對量子計算機的發展有用。
量子力學做爲一門「很數學」化的物理體系,已經像經典力學那樣成熟了,併成爲洞悉微觀世界的重要工具。
但量子力學也給留下了許多物理上的困惑,如粒子運動的波粒二象性問題、概率波問題、粒子糾纏問題、波函數崩塌問題等等。
歷史上關於光是粒子仍是波動的爭論,已有兩千多年(下圖)。
光的種種現象和性質代表它既有粒子的特徵又有波動的特徵,處於兩個派別立場的研究者各執一詞,各執己見。
直到1905年,愛因斯坦在德國《物理年報》上發表了題爲《關於光的產生和轉化的一個推測性觀點》的論文。他認爲對於時間的平均值,光表現爲波動;對於時間的瞬間值,光表現爲粒子性。這是歷史上第一次揭示微觀客體波動性和粒子性的統一,即波粒二象性。這一科學理論最終獲得了學術界的普遍接受。
在新的事實與理論面前,光的波動說與粒子說之爭以「光具備波粒二象性」而落下了帷幕。
即:光粒子的運動軌跡是呈週期性的波。
Wikipedia提供了一個視頻,形象地描述了光在各類理論下的特徵。
光是能量的一種傳播方式。光能量也被稱爲光子能量(按粒子性)或電磁輻射(按波動性)。每一個光子都具備必定量的能量,頻率越高,能量也越高。
光的度量跟能量或輻射測量相似,常被用於太陽能、加熱、照明、電信、計算機圖形學等領域。
光能量做爲能量,可被測量,單位是焦耳(J)。能夠經過將輻射通量(或功率)相對於時間、面積、空間積分來計算輻射能量的量。
測量輻射能量的概念和符號很是多,完整的表有數十個。下面只列出跟PBR相關的概念:
名稱 | 符號 | 單位 | 公式 | 解析 |
---|---|---|---|---|
輻射能量(Radiant energy) | \(Q\) | 焦耳(\(J\)) | - | 電磁輻射能量 |
輻射通量(Radiant Flux) | \(\Phi\) | 瓦(\(W\)) | \(\Phi = \frac{dQ}{dt}\) | 單位時間輻射的能量,也叫輻射功率(Radiant Power)或通量(Flux) |
輻照度(Irradiance) | \(E\) | 瓦/平方米(\({W}/{m^2}\)) | \(\Phi = \frac{d\Phi}{dA^\perp}\) | 到達單位面積的輻射通量 |
輻射度(Radiosity) | \(M\) | 瓦/平方米(\({W}/{m^2}\)) | \(M = \frac{d\Phi}{dA^\perp}\) | 離開單位面積的輻射通量,也叫輻出度、輻射出射度(Radiant Existance) |
輻射強度(Radiant Intensity) | \(I\) | 瓦/立體弧度(\({W}/{sr}\)) | \(I = \frac{d\Phi}{d\omega}\) | 經過單位立體角的輻射通量 |
輻射率(Radiance) | \(L\) | 瓦/平方米立體弧度(\({W}/m^2{sr}\)) | \(L = \frac{d\Phi}{d\omega dA^\perp}\) | 經過單位面積單位立體角的輻射通量 |
光學(Optics)是物理學的一個分支,研究光的行爲和性質,包括它與物質的相互做用以及使用或檢測它的儀器的結構。
光學一般描述可見光、紫外光和紅外光的行爲。因爲光是電磁波,其它波段的電磁輻射(如X射線、微波和無線電波)表現出相似的特性。
光學按照不一樣角度、不一樣粒度和不一樣側重點大體能夠分爲如下幾類:
光學與許多相關學科聯合進行研究,包括天文學、工程領域、攝影、計算機和醫學等等。光學的應用存在於各類平常物品中,包括鏡子、透鏡、望遠鏡、顯微鏡、激光器和光纖等等。
光的反射是當光在兩種物質分界面上改變傳播方向又返回原來物質中的現象。
上圖:光的反射現象。
產生反射的原理:光是電磁波,射在物體上的光波引發單個原子中的極化振盪(或電子在金屬中的振盪),導致每一個粒子在各個方向上輻射小的二次波,如偶極天線( dipole antenna)。根據惠更斯-菲涅耳原理,全部這些波加起來就產生反射和折射。
光的反射細分爲如下幾種:
光的折射是指光從一種介質斜射入另外一種介質時,傳播方向發生改變,從而使光線在不一樣介交界處發生的偏折。
上圖:光的折射現象。
折射的原理與反射相似:光波是一種特定頻段的電磁波,光在傳播過程當中有兩個垂直於傳播方向的份量:電場份量和磁場份量。當電場份量與介質中的原子發生相互做用,引發電子極化,造成電子雲和原子荷重心發生相對位移。致使光的一部分能量被吸取,同時光在介質中的速度被減慢,方向發生變化,引起了折射。
上圖:光從一種物質進入另外一種物質後發生了折射,波長、方向、速率都發生了改變。
近代物理學指出,光是一種沒有靜質量、體積很是小、運動速度比較高的物質。光和其它物質有相同的性質。光和物質間的相互做用力使光的運動方向發生改變即折射。
光經過不均勻媒質時,部分光束將偏離原來方向而分散傳播,從側向也能夠看到光的現象。
散射發生的原理:當光子與分子或原子相互接近時,因爲雙方具備很強的相互斥力,迫使它們在接觸前就偏離了原來的運動方向而分開。
散射是觀察和辨別物體的主要現象,是天然中最廣泛存在的現象。漫反射其實也是散射的一種。
光的色散指的是複色光分解爲單色光的現象。
色散現象說明光在介質中的速度\(v=\frac{c}{n}\)(\(n\)爲介質的折射率)隨光的頻率\(f\)而變。光的色散能夠用三棱鏡、衍射光柵、干涉儀等來實現。
光的色散說明了光具備波動性。由於色散是光的成分(不一樣色光)折射率不一樣引發的,而折射率由波的頻率決定。
對同一種介質,光的頻率越高,介質對這種光的折射率就越大。在可見光中,紫光的頻率最高,紅光頻率最小。當白光經過三棱鏡時,棱鏡對紫光的折射率最大,光經過棱鏡後,紫光的偏折程度最大,紅光偏折程度最小。這樣,三棱鏡將不一樣頻率的光分開,就產生了光的色散。
爲何在同一介質中,不一樣波長的光,其速度和折射率會不一樣呢?
因爲光有粒子性,與介質的原子、分子有相互做用力。對於波長越短的光,其攜帶的能量越大、運動越強,與介質的原子、分子的相互做用力越大,導致其速度越小、折射率越大。(這個回答是筆者根據經典物理學結合波動論的推測,未找到確切的依據和論據,有待考證!)
電磁理論認爲,光的吸取是光(電磁輻射)經過材料時,與材料發生相互做用,電磁輻射能量被部分地轉化爲其餘能量形式的物理過程。
當被吸取的光能量以熱能的形式被釋放,即造成了光熱轉化;當未被吸取的光能量被物體反射、散射或透射,便影響着咱們看到的物體的色彩。
量子理論認爲,光的吸取是指分子或原子在光波輻射場(光照)下,會吸取光子的能量由低能態躍遷到高能態的現象。這種躍遷也等效於一個具備必定固有頻率的振子。
電磁理論證實,當物體對某種頻率光的吸取係數很大時,它對該頻率光的反射率也大。若干電介質具備很強的吸取帶,故它們對於吸取帶附近頻率的光也有很強的反射,這稱爲選擇反射。
半導體材料在不一樣的程度上具有電介質和金屬材料的所有光學特性。當半導體材料從外界以某種形式(如光、電等)吸取能量,則其電子將從基態被激發到激發態,即光吸取。而處於激發態的電子會自發或受激再從激發態躍遷到基態,並將吸取的能量以光的形式輻射出來(輻射覆合),即發光;固然也能夠無輻射的形式如發熱將吸取的能量發散出來(無輻射覆合)
金屬的光吸取要同時考慮束縛電子與自由電子的做用。對於紅外線或更低頻率的輻射,自由電子起主要做用;而對於紫外線及更高頻率的輻射,則束縛電子的做用比較顯著,這時金屬實際上表現出與電介質類似的光學性質。
衍射是指當光波遇到障礙物或狹縫時發生的各類現象。它被定義爲圍繞障礙物或孔的角落的波浪彎曲到障礙物的幾何陰影區域中。
因爲光具備波動性,因此也會產生衍射。
光波穿過單波長的縫隙後發生了衍射現象。
當光波穿過具備變化的折射率的介質時,也會發生衍射的效果。全部波都會發生衍射,包括聲波、水波和電磁波(可見光、X射線和無線電波)。
光的衍射產生的原理能夠從兩方面解釋:
若干個光波組合在一塊兒造成的複合效果即是光的疊加。
更準確地說,在沒有非線性效應的狀況下,疊加原理可用於經過簡單地添加干涉來預測相互做用波形的形狀。產生光波的複合圖案的相互做用一般被稱爲干涉,干涉可能致使不一樣的結果。
光波在不一樣相位產生的疊加效果。
光產生疊加和干涉現象的原理與衍射類型,便再也不累述。
偏振是波的通常屬性,描述了它們的振盪方向。因爲光具備波動性,因此也會偏振。
偏振爲橫向波(如許多電磁波)描述了垂直於行進波方向的平面中的振盪方向。振盪能夠在單個方向上(線性偏振),或者隨着波行進方向而旋轉(圓形或橢圓形偏振)。
左:線性偏振;中:圓形偏振;右:橢圓偏振。
幾何光學是將光的波長視做無限小,以至能夠將光當成直線來研究的一門物理分支。它以光線爲基礎,研究光的傳播和成像規律。
在幾何光學中,把組成物體的物點看做是幾何點,把它所發出的光束看做是無數幾何光線的集合,光線的方向表明光能的傳播方向。
幾何光學中光線的概念與光的波動性質相違背。由於從能量和光的波動性現象(如衍射)來看,這種幾何光線都是不可能存在的。幾何光學只是波動光學的近似,把全部光當成波長極小的狀況處理,小到光線被當成了直線。可是,簡化後的幾何光學能夠不涉及光的物理本性,而能以其簡便的方法解決光學儀器中的光學技術問題和計算機圖形渲染的複雜度問題。
在幾何光學中,特別是在計算機圖形學中,光線處理作了如下簡化或遵循如下基本定律:
因爲PBR的相關技術及諸多理論跟幾何光學相關,因此本節將深刻地探討幾何光學的內容。
反射定律描述了反射光的角度:入射光的角度與反射光的角度相同。
如上圖所示,入射光線P射在介質點O上,反射光線是Q,點O的法線是normal,則根據反射定律:
\[ \theta_i = \theta_r \]
即入射角\(\theta_i\)和反射角\(\theta_r\)相同。它們的另一種等效表達形式:
\[ \theta_r = \pi - \theta_i \]
須要注意的是,反射定律描述的是反射角問題,並不涉及能量分配。
反射還涉及到反射率的問題。反射率是反射波的功率與入射波的功率之比。每種材料的反射率不同,而且跟入射光與介質的夾角有關,這種現象叫菲涅爾反射效應,與之相關的方程是菲涅爾方程(Fresnel equations)。
上圖:菲涅爾反射效應,球體的反射率從中心到邊緣以某種曲線提高。
實際上,當光照射到介質表面時,光可能產生的結果:
折射定律也叫斯涅爾定律(Snell's law,Snell–Descartes law),描述了光在兩種介質之間折射後的角度、折射率、光速的關係。
上圖:折射定律動畫示意圖。
如上圖所示,光在介質之間發生了折射,介質1的入射角、折射率和光速分別是\(\theta_1\)、\(n_1\)、\(v_1\),介質2的入射角、折射率和光速分別是\(\theta_2\)、\(n_2\)、\(v_2\),則根據折射定律,它們有如下的關係:
\[ \frac{\text{sin}\theta_2}{\text{sin}\theta_1} = \frac{v_2}{v_1} = \frac{n_2}{n_1} \]
用折射定律會出現一種異常狀況:當光從較高折射率的介質傳播到較低折射率的介質時,在入射角足夠大的狀況下,折射定律彷佛要求折射角的正弦大於1。
這固然是不可能的。實際上,在這種狀況下,光線徹底被邊界反射,這種現象稱爲全內反射(Total internal reflection)。仍能致使折射的最大入射角稱爲臨界角,此時折射角是\(90^\circ\)。
如上圖所示,光線從較高折射率的水射到較低折射率的空氣中,當入射角大於臨界角時,會出現圖右的全內反射。
其中,臨界角\(\theta_\text{crit}\)可由折射定律推導出來:
\[ \theta_\text{crit} = \text{arcsin} \left( \frac{n_2}{n_1}\text{sin}\theta_2 \right) \]
因爲水的折射率\(n_1 = 1.333\),空氣的折射率\(n_2 = 1.0\),折射角\(\theta_2 = 90^\circ\),則根據上面的公式能夠算出水相對空氣折射的臨界角:
\[ \theta_\text{crit_water2air} = \text{arcsin} \left( \frac{1.0}{1.333}\text{sin}90^\circ \right) = 48.6^\circ \]
在幾何光學中,除了反射定律和折射定律以外,還有如下定律:
因爲這些定律跟PBR技術關聯不大,本文不詳述,有興趣的能夠另外找資料。
從經典物理學上,物質是任何具備質量而且有體積佔據空間的東西。
從現代物理學上,物質是構成宇宙間一切物體的實物和能量場(光、電場、磁場、聲等),還包括反物質和暗物質。
從宏觀上,物質是全部看得見摸得着感覺獲得的東西。
從微觀上,物質是由原子構成的全部東西,而原子又是由相互做用的亞原子粒子構成。實體粒子包含但不限於:原子、中子、質子、電子、夸克、輕子、重子、費米子等等;能量粒子包含但不限於:光量子、聲波等。
從哲學上,物質除了客觀實體,還有包含了主觀意識。
雖然物質的概念和構成很是複雜,但計算機圖形學的PBR領域,只要研究經典物理學的物質和能量場的光波便可。
物質的構成千姿百態,結構也形態萬千。
從不一樣的微觀尺寸觀察,物體的結構描述以下:
物質的形態(Phase,也叫相態)常見的有:固態、氣態、液態、非晶態、液晶態,另外還有奇特的形態:等離子態、超固態、中子態、超導態、超流態、玻色–愛因斯坦凝聚、費米子凝聚態。
這些狀態都是物質在不一樣的密度、溫度、壓強、輻射下的形態,當所處的環境發生改變時,會從一種形態轉成另一種形態。
物質在固態、液態、氣態、等離子態的轉化圖。
物質的屬性有不少,從不一樣角度有不一樣的屬性,但常見的物理和化學屬性有:體積、尺寸、質量、密度、硬度、導電性、導磁性、磁性、範性(可塑性)、透光性(透明度)、比熱容、彈性、可燃性、助燃性、酸鹼性等等。
下面只闡述跟PBR相關的物質屬性。
導電性是指物質內載電荷粒子的運動。含有電荷的粒子稱爲電荷載子,它們的運動造成了電流。
電流產生的緣由有兩種:
根據導電性,能夠將物質分爲:
粗糙度是反映物質微觀表面的輪廓紊亂的程度。
根據粗糙程度能夠將物體分紅如下幾類:
有不少估算物體粗糙度的方法:
\(R_a\):算術平均誤差法,公式:
\[ R_a = \frac{1}{n}\sum_{i=1}^n|y_i| \]
\(R_q\):均方根法,公式:
\[ R_q = \sqrt{\frac{1}{n}\sum_{i=1}^n y_i^2} \]
\(R_{sk}\):偏態分佈法,公式:
\[ R_{sk} = \frac{1}{nR_{q^3}}\sum_{i=1}^n y_i^3 \]
\(R_{ku}\):峯態法,公式:
\[ R_{ku} = \frac{1}{nR_{q^4}}\sum_{i=1}^n y_i^4 \]
還有\(R_zDIN\)、\(R_zJIS\)法,但最經常使用的是 \(R_a\)。
透光性亦即透明度,描述物質透過光線的程度。
物體的透光性主要取決於物質內部結構對外來光子的吸取和散射。
金屬物質在可見光波段的電子軌道密集(有能帶),能強烈地吸取對應能量的光子併發射(相同能量或較小能量的)光子,即其表面能夠強烈地反射光,是不透明的。
物質內部對光的散射,主要取決於其內部缺陷的多少,缺陷多的物質,散射率高。普通陶瓷材料,其內部充斥着大量的微氣孔等缺陷,氣孔會對通過的光產生強烈的散射,因此普通陶瓷是不透明的;而氣孔率保持極低的陶瓷材料是能夠像玻璃同樣透明的。單晶體(如自然水晶)和液體(如水)因爲內部排列規則,缺陷極少,是透明的。
各向性是描述物質任意一點的物理和化學等屬性跟方向是否相關,若是與方向無關叫各向同性(isortropy),不然叫各向異性(anisortropy)。
好比清澈平靜的水是各向同性,由於水的每一個部分的屬性(密度、壓力、溫度、折射率質量等等)都與方向無關;而飄忽不定的煙或霧則是各向異性,很明顯同個部位不一樣方向有着不同的密度或外力。
在計算機圖形學,特別是實時渲染領域,一般將物體簡化成均勻的,即各向同性的。幾何光學也一般將光線傳播的介質當作各向同性。
能量是必須轉移到物體以便對物體進行影響或加熱的定量屬性。能量是物質運動轉換的量度,是表徵物理系統作功本領的量度。
能量的單位與功的單位相同,在國際單位制中是焦耳(J);在微觀研究領域,經常使用電子伏(eV)做爲單位。
能量存在的形式多種多樣,在不一樣學科不一樣分支不一樣角度有着不一樣的類別,主要有:
能量的類型多鍾多樣,並且它們之間是能夠相互轉化的。
能量的轉化在生活中隨處可見。好比,「利用太陽能發的電煮開了水,水蒸氣一直往上冒」,蘊含了不少能量轉化的過程:
愛因斯坦在二十世紀初提出了著名的質能轉化公式:
\[ E = Pc = mc\cdot c = mc^2 \]
其中\(E\)是能量,\(m\)是物質的靜止質量,\(c\)是真空的光速,約\(3\times10^8m/s\)。
質能等值公式揭示了任意有靜止質量的物質均可以轉化成能量,並且不多一部分的物質轉成能量後是很是巨量的。
好比,1kg的物質轉成能量後:
\[ E_{1kg} \approx 1.0 \times (3\times10^8)^2 = 9\times10^{16} J \]
其能量至關於:
因而可知,物質轉化成能量後,是很是恐怖的。一塊小石頭,足以毀掉一個小星球。
幸虧,目前尚沒有很便捷地將物質轉化成能量的方法。然而,雖然核爆(原子彈、中子彈、氫彈)不是利用質能轉化定理,可是核能表現出的威力已經足夠使人望而生畏了。
傳統物理學上,能量守恆定律代表在封閉的系統中,能量不會憑空出現或消失,只會從一種形式轉成另一種形式,能量的總量保持不變。
現代物理學上,因爲質量和能量能夠相互轉化,能量守恆擴展到能量和質量的總和保持不變。
諾埃德定理(Noether's theorem)能夠嚴格證實能量守恆,它也代表了永動機是不可行的。
好比,上圖所示,光線照到物質表面上時,光能可能一部分被鏡面反射(黃色),一部分被散射(深藍色),一部分被吸取(褐色),還有一部分被透射(圖中未標識)。與之對應的能量分別是反射部分依舊是光能,吸取部分轉化成熱能、電能等形式,透射部分依舊仍是光能,而且:
\[ E_{in} = E_{specular} + E_{diffuse} + E_{absorb} + E_{transmit} \]
亦即入射光能與反射(包含鏡面反射和散射)、吸取、透射部分的總光能相等,遵循了能量守恆定律。
PBR的BRDF也遵循了這一守恆定律,引入粗糙度、反射率等概念,使得原理上更加物理正確,渲染效果上更加真實。
本節將闡述本章前幾節涉及的光學理論與PBR結合的理論,特別是物質和光的交互原理及理論。主要參考了Naty Hoffman在2013~2015年(特別是2015年)的SIGGRAPH公開課中演講的主題:《Physics and Math of Shading》。
回顧一下4.2.5.3 光的電磁理論(Electromagnetic theory)描述的電磁理論:
光是電磁波,在介質中做爲能量以特定波長(Wavelength)和頻率振盪着向前方傳播。電磁波可被分解成電場(Electric)和磁場(Magnetic),而且它們相互垂直(以下圖)。
人類可見的光波波長分佈在400nm~780nm之間,可見光的波長跟蜘蛛絲寬度相仿,但遠小於人類頭髮絲的寬度(下圖)。
上圖:可見光波長(圖左)與蜘蛛絲(圖右斜灰線)、人類頭髮絲(圖右黃色區域)寬度比較。
實際上,絕大多數光包含了不少個波段的電磁場,每一個波段的電磁場包含了不一樣的能量。好比下圖的位於500~550nm波段的電磁波,在左上角的光譜能量分佈(Spectral Power Distribution,SPD)中顯示出了綠色光的能量。激光能夠發出單色的光。
下圖左R、G、B的光譜能量分佈乘以各自的縮放因子,將它們的結果相加以後就成了下圖右的摸樣。
這個原理與已投入影院使用的R、G、B激光投影系統相似。
上圖所示的是能量分佈圖,若是將它們用波形圖表示,會顯得更加複雜,由於涉及到光的疊加和干涉,見下圖:
可是,因爲大多數光源都不是單一波長的光波,而是有必定寬度的連續的複合光波,因而它們的最終疊加的波形更加複雜。舉個例子,以標準白光光源D65爲例,它的光譜能量分佈圖和複合波形以下:
有趣的是,下圖的兩種SPD雖然不同(上部分是連續的分佈,下部分是離散的RGB分佈),可是恰好跟人類視覺成像原理(詳見4.2.3 人眼感知可見光原理)匹配,因此人類並無發現它們之間的差異。也就是說,人類的視覺是有損的,將無限維度的SPD簡化成了三維視覺空間。
在納米級別,當一束電磁光波和原子或分子相遇後,會發生什麼呢?
答案是,光波會引起原子、分子偏振,而且使它們的正負電荷分離,造成偶極子(dipole)。這就意味着進入的光波被吸取了。
吸取了能量而且極化後的原子、分子會迅速恢復,從新向外輻射,造成二次光波。固然被吸取的部分能量可能轉化成了熱運動,即熱能。
對均勻介質(Homogeneous Medium)來講,光線是沿着直線傳播。因爲全部物質在原子維度上看是不可能徹底均勻的,因此均勻介質是在實踐中抽象出來的概念。
在渲染技術中,會使用宏觀的統計和組合爲材質提供參數。這個參數就是折射率(Index of refraction,IOR),它由兩個部分組成:
對於介質中局部不均勻的部分將被建模成粒子(原子核等),折射率不連續的物質會散射入射光,散射出的光方向向着四面八方。
雖然這跟前面討論的單一分子或原子的極化相似,但能夠將這些微粒組合起來,造成宏觀模型。
下圖是吸取係數和散射係數組合成的宏觀維度的材質表現圖。
橫軸是散射係數,從左到右能夠看出物質從清澈到混濁的程度;縱軸是吸取係數,從下到上表示物質從透明(光被全透射而表現出跟入射光幾乎同樣的顏色)到不透明(光被全吸取而表現出與入射光不同的顏色)的程度。好比牛奶,它是吸取係數低而散射係數高的物質,因此它表現出入射光一致的白色,而且是混濁的。有色液體能很輕易吸某些波段的光,而其它波段的就沒那麼容易。
從光學角度上看,最重要的事情是全部材質的表面都是粗糙的。沒有表面是徹底平坦的,至少在原子維度上是不規則的。原子間排布距離若是跟光波差很少或更小,就會引發一種現象,它就是以前章節涉及過的衍射。
惠根斯-菲涅爾原理(Huygens-Fresnel Principle)能夠解釋這個現象。當光波遇到跟它波長相仿的障礙時,會「繞彎」傳播,繞到了障礙後面:
在納米維度,當光波傳播到光學平坦的表面時,惠根斯-菲涅爾原理一樣適用。當與入射光碰撞後,每一個粒子都會發射球面波,有些強有些弱。這些不平行的球面波結合起來,就會造成複雜的波形,在不少方向發射不同的光:
納米幾何體(Namogeometry)越小,越少光波被衍射。入射光與單個原子碰撞後,少部分會被衍射。
如今聊回幾何和射線光學,它們都是更簡化的而且普遍應用於計算機圖形領域。其中一種簡化方法是忽略納米級別的不規則和衍射,將光學平坦的表面當成徹底平坦。由此能夠應用幾何光學的反射和折射定律。
不少材質表面看起來是平滑的,但實際並不是如此,對於微觀幾何體(Microgeometry),它們同樣凹凸不平:
上圖的上部分因爲物體更加平滑,因此在微觀的表面看起來更加規則,反射的光也相對規則,宏觀表現就是被反射的光更加清晰,反射的畫面更容易表達出被反射物體的輪廓;而下部分因爲微觀更加不規則,反射的光線取向更紊亂,因此宏觀表現就是高光變模糊了,被反射的物體看不清楚。
對於折射進入介質的光,會發生什麼呢?
對這個問題,須要對物質的導電性進行分類,而後分開探討:
導體(Conductor):導體是金屬、電解質等導電性強的物質,因爲它們的微粒組合特性,會當即吸取折射光造成熱運動。
絕緣體(Dielectric):即電介質,指沒有導電性的物質。如前面章節描述的同樣,折射光進入除了透明的電介質後,小部分被吸取,至關一部分在介質內部被屢次散射並從新射出表面造成漫反射。
從新被散射出介質表面的光線造成了不一樣的散射距離。散射距離的分佈取決於散射微粒的密度和屬性。
以下圖所示,若是像素尺寸(綠色標識區域)大於散射距離(黃色線段),就能夠無視次表面散射效果。
因爲忽略次表面散射的效果,因此入射光附近的區域能夠當成一個點來處理(下圖),採用經典的關照計算方式,好比Lambert或Phong光照模型。
當成單點處理後,即可以將光照分紅兩個部分:鏡面反射和漫反射(包含了折射、吸取、散射和從新折射回表面的光),見下圖:
對於下圖中所示的,散射範圍(黃色線段)大於像素尺寸(綠色小圓區域),就不能採用上面的簡化模型,而須要採起次表面散射(Subsurface scattering)渲染技術。
半導體(Semiconductor):因爲半導體與光交互的特性介於金屬和非金屬之間,在實際渲染中,經常使用迪尼斯原則的金屬度係數來模擬半導體特性。
上節講述了物質和光的不一樣狀況的交互原理,本節將講述BxDF的主要類型。
目前計算機圖形渲染領域,基於物理的渲染方式主要有:
輻射度(Radiance):計算光源的鏡面反射和漫反射佔總的輻射能量的比例,從而算出顏色。在實時渲染領域,是最主流的渲染方式。BRDF大多數都是基於此種方式,包括Cook-Torrance。
光線追蹤(Ray Tracing):即光線追蹤技術,它的作法是將攝像機的位置與渲染紋理的每一個像素構造一條光線,從屏幕射出到虛擬世界,每遇到幾何體就計算一次光照,同時損耗必定比例的能量,繼續分拆成反射光線和折射光線,如此遞歸地計算,直到初始光線及其全部分拆的光線能量耗盡爲止。
因爲這種方式開銷很是大,特別是場景複雜度高的狀況,因此經常使用於離線渲染,如影視製做、動漫製做、設計行業等。
近年來,隨着NVIDIA的RTX系列和AMD的RX系列顯卡問世,它們的共同特色是硬件級別支持光線追蹤,從而將高大上的光線追蹤技術帶入了實時渲染領域。
路徑追蹤(Path Tracing):實際上路徑追蹤是光線追蹤的一種改進方法。它與光線追蹤不一樣的是,引入了蒙特卡洛方法,利用BRDF隨機跟蹤多條反射光線,隨後根據這些光線的貢獻計算該點的顏色值。
這種方法更加真實(下圖),但同時也更加耗時,一般用於離線渲染領域。
上章已經詳細描述了基於輻射度的Cook-Torrance的BRDF模型的理論和實現。實際上,Cook-Torrance模型在整個渲染體系中,只是冰山一角。下面是BRDF光照模型體系:
限於篇幅和本文主題,下面將介紹基於輻射度方式的BxDF光照模型。
BxDF可細分爲如下幾類:
BRDF(雙向反射分佈函數,Bidirectional Reflectance Distribution Function):用於非透明材質的光照計算。Cook-Torrance就是BRDF的一種實現方式,上章詳述過,很少說。
BTDF(雙向透射分佈函數,Bidirectional Transmission Distribution Function):用於透明材質的光照計算。折射光穿透介質進入另一種介質時的光照計算模型,只對有透明度的介質適用。
BSDF(雙向散射分佈函數,Bidirectional Scattering Distribution Function):其實是BRDF和BTDF的綜合體:
簡單地用公式表達:BSDF = BRDF + BTDF。
SVBRDF(空間變化雙向反射分佈函數,Spatially Varying Bidirectional Reflectance Distribution Function):將含有雙參數的柯西分佈替代常規高斯分佈引入微面元雙向反射分佈函數(BRDF)模型,同時考慮了目標自身輻射強度的方向依賴性,在此基礎上推導了長波紅外偏振的數學模型,並在合理範圍內對模型作簡化與修正使之適用於仿真渲染。
BTF(雙向紋理函數,Bidirectional Texture Function):主要用於模擬非平坦表面,參數跟SVBRDF一致。可是,BTF包含了非局部的散射效果,好比陰影、遮擋、相互反射、次表面散射等。用BTF給表面的每一個點建模的方法被成爲Apparent BRDFs(表面雙向反射分佈函數)。
SSS(次表面散射,也稱3S,Subsurface Scattering):它是模擬光進入半透明或者有必定透明深度的材質(皮膚、玉石、大理石、蠟燭等)後,在內部散射開來,而後又經過表面反射出來的光照模擬技術。下面是用SSS模擬的玉石效果圖:
關於次表面散射方面的研究,比較好的是Jensen的文章《A Practical Model for Subsurface Light Transport》,該文提出了一個較爲全面的SSS模型,將它建模成一個雙向表面散射反射分佈函數(BSSRDF)。
BSSRDF(雙向表面散射分佈函數,Bidirectional Surface Scattering Reflectance Distribution Function):它經常使用於模擬透明材質,目前是主流技術。它和BRDF的不一樣之處在於,BSSRDF能夠再現光線透射材質的效果,還能夠指定不一樣的光線入射位置和出射位置:
從上面能夠看出,BxDF的形式多種多樣,但因爲它們都是基於輻射度的光照模型,因此最終能夠用如下公式抽象出來:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} f_r(p,\omega_i,\omega_o) L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
用更簡潔的方式描述,入射光\(\omega_i\)在\(p\)點的顏色的計算公式:
\[ \begin{eqnarray*} p點顏色 & = & 光源顏色 \times 材質顏色 \times 反射係數 \times 光照函數 \\ 光照函數 & = & f(n_{法線}, \omega_{光源方向}, v_{視點方向}) \end{eqnarray*} \]
因爲篇幅問題,本文不會對BTDF、BSDF、SSS、BSSRDF進行詳細討論,有興趣的能夠另外找資料瞭解。筆者之後也可能另外開闢專題探討。
本章末,值得一提的是,BRDF最終的光照計算結果是幾何函數和油墨算法(ink-selection)結合的結果。
其中油墨算法描述瞭如何計算各顏色份量的反射率,可參看論文《A Multi-Ink Color-Separation Algorithm Maximizing Color Constancy》。
上章詳細介紹了PBR的核心原理:光學理論和物質交互的原理。
這章將深刻介紹PBR核心原理相關的理論,並對其中一些公式作推導或詳細闡述,使讀者對PBR的關聯技術和原理有更完全的理解。
主要面向:
數學物理( Mathematical Physics)是一門數學和物理相結合的學科,意圖將物理現象和理論用數學公式或理論表達出來。本章主要是跟數學物理相關的內容。
因爲微積分能夠解決不少物理學上的現象或問題,好比光照輻射度量、電磁場、量場等等,因此頗有必要重溫一下微積分的基礎知識。
微積分是數學的一門基礎學科,是高等數學中研究函數的一個分支。它包含的主要內容有:
極限的概念:設\(y=f(x)\)是給定函數,若是自變量\(x\)在定義域內按照某種趨勢變化時,若\(x\to a\)時,函數值與某個常數\(A\)可無限接近(甚至相等),則稱\(f(x)\)在此變化過程當中有極限,\(A\)爲其極限,記作\(\lim_{x \to a}f(x) = A\),不然稱\(f(x)\)在此過程當中無極限。
例如,函數\(y=xf(x)\),它的曲線以下圖,從中能夠觀察到,當\(x\)無限趨近於0時,\(y\)也趨近於0,用公式表達就是:\(\lim_{x \to 0}xf(x) = 0\)。
\(\lim[ f(x)\pm g(x)] = \lim f(x) \pm \lim g(x)\)
\(\lim[ f(x) g(x)] = \lim f(x) \lim g(x)\)
\(\lim_{x \to a}f(x) = A \iff f(a+0) = f(a-0) = A\)(極限與左、右極限的關係)
\[ \lim_{x \to 0} \frac{\sin{x}}{x} = 1 \]
其中\(\frac{\sin{x}}{x}\)曲線圖以下:
能夠將\(x\)擴展到\(f(x)\):
\[ \lim_{x \to 0} \frac{\sin{f(x)}}{f(x)} = 1 \]
利用這一重要極限,能夠求得一系列涉及三角函數的極限。
\[ \lim_{x \to \infty} (1+\frac{1}{x})^x = e \]
\[ \lim_{x \to 0} (1+x)^\frac{1}{x} = e \]
其中\(e\)是無理數,\(e=2.718281828459045…\)。
能夠將\(x\)擴展到\(f(x)\):
\[ \begin{eqnarray*} \lim_{x \to \infty} (1+\frac{1}{f(x)})^{f(x)} &=& e \; (\lim_{x \to a}f(x) = \infty) \\ \lim_{x \to a} (1+f(x))^\frac{1}{f(x)} &=& e \; (f(x)\ne0且\lim_{x \to a}f(x) = 0) \end{eqnarray*} \]
利用這一重要極限,能夠求得一系列涉及冪指函數\(u(x)^{v(x)}\)的極限。
導數的定義:
設\(y=f(x)\)在\(x_0\)和\(x_0+\triangle x\)有定義,則函數有增量\(\triangle y = f(x_0 + \triangle x)-f(x_0)\)。若是極限
\[ \lim_{\triangle x\to 0}\frac{\triangle y}{\triangle x} = \lim_{\triangle x\to 0}\frac{f(x_0+\triangle x)-f(x)}{\triangle x} \]
存在,則稱\(y=f(x)\)在\(x_0\)可導,稱極限值爲\(f(x)\)在\(x_0\)處的導數(其實就是斜率、變化率),可記爲如下幾種形式:
\[ \begin{eqnarray*} f'(x_0) \\ \frac{df}{dx}|_{x=x_0}\\ y'|_{x=x_0}\\ \frac{dy}{dx}|_{x=x_0} \end{eqnarray*} \]
求導步驟:
好比,求\(y=f(x)=x^2\)的導數,分爲如下步驟:
\[ \begin{eqnarray*} \triangle y &=& (x+\triangle x)^2 - \triangle x = 2x\triangle x + (\triangle)^2 \\ \frac{\triangle y}{\triangle x} &=& 2x + \triangle x \\ \lim_{\triangle x\to 0}\frac{\triangle y}{\triangle x}&=& 2x + \triangle x = 2x \end{eqnarray*} \]
更抽象地,對冪函數\(f(x)=x^\alpha\)的導數:
\[ (x^\alpha)' = \alpha \cdot x^{\alpha - 1} \]
求導法則:
基本運算求導法則:設\(u=u(x)\)和\(v=v(x)\)是\(x\)的可導函數,且\(v(x)\ne 0\),則:
\[ \begin{eqnarray*} (u\pm v)' &=& u' \pm v' \\ (uv)' &=& u'v + uv' \\ (\frac{u}{v})' &=& \frac{u'v-uv'}{v^2} \end{eqnarray*} \]
設\(C\)爲常數,則\([Cv(x)]' = Cv'(x)\)。
其它常見函數的導數:
\[ \begin{eqnarray*} (\ln x)' &=& \frac{1}{x} \\ (\log_ax)' &=& \frac{1}{x\ln a} \\ (\sin x)' &=& \cos x \\ (\cos x)' &=& -\sin x \\ (\tan x)' &=& \frac{1}{cos^2x} = \sec^2 x \\ (\cot x)' &=& -\csc^2 x \\ (\csc x)' &=& -\csc x \cot x \end{eqnarray*} \]
複合函數求導法則:設\(y=f[h(x)]\)是由\(y=f(u)\)和\(u=h(x)\)組成的複合函數,而且設\(u=h(x)\)可導,\(y=f(u)\)也可導,則複合函數\(y=f[h(x)]\)的導數爲:
\[ \frac{dy}{dx} = \frac{dy}{du}\cdot \frac{du}{dx} \]
還有其它幾種等價表示形式:
\[ \begin{eqnarray*} y'_x &=& y'_u \cdot u'_x \\ (f[h(x)])' &=& f'(u)|_{u=h(x)}h'(x) \end{eqnarray*} \]
以上法則也叫鏈式求導法則,它代表:複合函數的導數等於函數對中間變量的導數乘以中間變量
對自變量的導數。
反函數的導數:反函數的導數等於原來函數導數的倒數。
更具體地,設單調函數\(y=f(x)\),則它的反函數是\(x=f^{-1}(y)\),則有:
\[ f'(x) = \frac{1}{f^{-1}(y)}或\frac{dx}{dy} = \frac{1}{\frac{dy}{dx}} \]
隱函數的導數:若是方程\(F(x,y)=0\)肯定了\(y\)是\(x\)的函數,那麼,這樣的函數叫作隱函數。
若隱函數\(y\)關於\(x\)可導,則可根據複合函數求導法則求出函數\(y\)對\(x\)的導數。
高階導數:
通常狀況下,函數\(y=f(x)\)的導數\(y'=f'(x)\)仍然是\(x\)的函數。若是\(f'(x)\)仍然可導,則把\(f'(x)\)的導數稱爲函數\(y=f(x)\)的二階導數,記做\(f''(x)\)或\(y''\),也可用如下表達式:
\[ y'' = (y')^{'} 或 f''(x) = (f'(x))' \]
若二階導數仍然可導,則二階導數的導數稱爲函數\(y=f(x)\)的三階導數,記做\(f'''(x)\)或\(y'''\)。
二階及以上的導數統稱爲高階導數。
微分的定義:
設函數\(y=f(x)\)在點\(x\)處可導,則把函數\(y=f(x)\)在\(x\)處的導數\(f'(x)\)與自變量在\(x\)處的增量\(\triangle x\)之積\(f'(x) \triangle x\)稱爲函數\(y=f(x)\)在點\(x\)處的微分,記作\(dy\),即\(dy=f'(x)\triangle x\),這時稱函數\(y=f(x)\)在點\(x\)處可微。
對自變量\(x\)的微分,能夠認爲是對函數\(y=x\)的微分,有如下等式:
\[ dy = dx = x'\triangle x = \triangle x \]
故而,\(y=f(x)\)的微分又可記爲\(dy=f'(x)dx\),推導出:\(\frac{dy}{dx} = f'(x)\)。
也就是說,函數\(y=f(x)\)的微分\(dy\)與自變量的微分\(dx\)之商是函數\(y=f(x)\)的導數。所以,函數的導數也叫微商(注意,跟朋友圈的微商概念不同o_o!!)。
也就是說,函數\(y=f(x)\) 在\(x\)處可微與可導等價。
微分的幾何意義:
以下圖所示,\(MT\)是曲線\(y=f(x)\)上的點\(M\)處的切線,設它與\(x\)軸正向的夾角爲\(\alpha\),則\(QP = MQ \cdot \tan \alpha = \triangle x \cdot f'(x_0)\),因此\(dy = QP\),即函數\(y=f(x)\)在\(x\)_0處相對於\(\triangle x\)的微分\(dy=f'(x)\triangle x\)。
也就是說,微分的幾何意義是曲線上點的切線的縱座標的改變量。
微分公式和法則:
因爲微分\(dy=f'(x)dx\),也就是說,只要求出函數的導數,便可求得對應的微分。
於是求導數和求微分的方法統稱爲微分法。
導數和微分之間的公式和法則高度一致:
導數公式 | 微分公式 |
---|---|
\((C)' = 0\) | \(d(C) = 0\) |
\((x^u)' = ux^{u-1}\) | \(d(x^u) = ux^{u-1}dx\) |
\((\log_a x)' = \frac{1}{x\ln a}\) | \(d(\log_a x) = \frac{dx}{x\ln a}\) |
\((\ln x)' = \frac{1}{x}\) | \(d(\ln x) = \frac{1}{x}dx\) |
\((a^x)' = a^x\ln a\) | \(d(a^x) = a^x\ln a dx\) |
\((\sin x)' = \cos x\) | \(d(\sin x) = \cos xdx\) |
\((\cos x)' = -\sin x\) | \(d(\cos x) = -\sin xdx\) |
\((\tan x)' = \sec^2 x\) | \(d((\tan x) = \sec^2 xdx\) |
\((\cot x)' = - \csc^2 x\) | \(d(\cot x) = - \csc^2 xdx\) |
\((\sec x)'=\sec x \tan x\) | \(d(\sec x)=\sec x \tan xdx\) |
\((\csc x)' = -\csc x \cot x\) | \(d(\csc x) = -\csc x \cot xdx\) |
\((\arcsin x)' = \frac{1}{\sqrt{1-x^2}}\) | \(d(\arcsin x) = \frac{1}{\sqrt{1-x^2}}dx\) |
\((\arccos x)' = -\frac{1}{\sqrt{1-x^2}}\) | \(d(\arccos x) = -\frac{1}{\sqrt{1-x^2}}dx\) |
\((\arctan x)' = \frac{1}{1+x^2}\) | \(d(\arctan x) = \frac{1}{1+x^2}dx\) |
\((\text{arccot} x)' = -\frac{1}{1+x^2}\) | \(d(\text{arccot} x) = -\frac{1}{1+x^2}dx\) |
導數法則 | 微分法則 |
---|---|
$(u\pm v)' = u' \pm v' $ | $d(u\pm v) = du \pm dv $ |
\((uv)' = u'v + uv'\) | \(d(uv) = vdu + udv\) |
\((\frac{u}{v})' = \frac{u'v-uv'}{v^2}\) | \(d(\frac{u}{v}) = \frac{vdu-udv}{v^2}\) |
\(y_x' = y_u'u_x'\) | \(dy = y_u'u_x'dx\) |
一階微分形式不變性:
不管是自變量仍是中間變量,函數的微分形式老是:
\[ dy = f'(u)du \]
此性質就是一階微分形式不變性。
原函數:若是在區間\(I\)上,可導函數\(F(x)\)的導函數爲\(f(x)\),即當\(x\in I\)時,即
\[ F'(x)=f(x) \; \text{或} \; d(F(x))=f(x)dx \]
則\(F(x)\)是\(f(x)\)在區間\(I\)上的原函數。
原函數存在定理:連續函數必定有原函數。
不定積分定義:
在區間\(I\)上,函數\(f(x)\)的帶有任意常數項的原函數稱爲\(f(x)\)(或\(f(x)dx\))在區間\(I\)上的不定積分,記作
\[ \int f(x)dx \]
其中符號\(\int\)稱爲積分號,\(f(x)\)爲被積函數,\(f(x)dx\)爲被積表達式,\(x\)爲積分變量。
求一個函數的不定積分實際上只需求出它的一個原函數,再加上一個任意常數:
\[ \int f(x)dx = f(x) + C \]
不定積分的性質:
\(\big[\int f(x)dx\big]' = f(x)\),或\(d\big[\int f(x)dx\big] = f(x)dx\)
\(\int F'(x)dx = F(x) + C\),或\(\int dF(x) = F(x) + C\)
由上面兩個性質可得出:微分運算\(d\)與不定積分運算\(\int\)互爲逆運算,當它們的符號連在一塊兒時,可抵消,抵消後可能相差一個常數。
速記口訣:先積後微,形式不變;先微後積,差一常數。\(\int \big[f(x)\pm g(x)\big]dx = \int f(x)dx \pm \int g(x)dx\)
\(\int k f(x)dx = k \int f(x) dx\)(\(k\)是常數且\(k\ne 0\))
與求積分變量無關的常數\(k\),能夠提出積分號。
常見積分表:
\(\int x^n dx = \frac{1}{n+1}x^{n+1} + C \; (n \ne -1)\)
\(\int \frac{dx}{\sqrt{a^2 + x^2}} = \ln|x+\sqrt{x^2+a^2}| + C\)
積分的方法:
直接積分法:利用不定積分的4個性質求不定積分。
例如,求\(\int \cos x dx\)。
由於\((\sin x)' = \cos x\),因此\(\int \cos x dx = \sin x + C\)。
換元積分法:
設\(f(u)\)具備原函數\(F(u)\),\(u=h(x)\)可導,則有換元公式:
\[ \begin{eqnarray} \int f(h(x))d(h(x)) &=& \big[\int f(u)du\big]_{u=h(x)} \\ &=& (F(u)+C)_{u=h(x)} \\ &=& F(h(x)) + C \end{eqnarray} \]
設\(f(x)\)連續,\(x=h(t)\)的導數\(h'(t)\)也連續,且\(h'(t)\ne 0\),倘若
\[ \int f(h(t))h'(t)dt = G(t) + C, \]
則有換元公式:
\[ \int f(x)dx = \big[\int f[h(t)] h'(t) dt \big] = (G(t)+C)_{t=h^{-1}(x)} = G(h^{-1}(x) + C) \]
其中\(t=h^{-1}(x)\)爲\(x=h(t)\)的反函數。
分部積分法:
分部積分法由兩個函數乘積的導數公式推導而來,最終形式:
\[ \int u dv= uv - \int v du \]
如果分部的一部分有困難時,能夠嘗試另一部分可能相對容易,這就是分部積分法的做用。
定積分是積分學的另外一個重要概念,天然科學與生產實踐中的許多問題,如平面圖形的面積、曲線的弧長、水壓力、變力所作的功等均可以歸結爲定積分問題。
計算機圖形學的不少計算問題也歸結於定積分問題,如輻射度量、採樣、卷積、預計算等等。
定積分的定義:
設\(f(x)\)在區間\([a,b]\)上有界,在\([a,b]\)中插入若干個分點
\[ a = x_0 < x_1 < \;...\; < x_n = b \]
把區間\([a,b]\)分紅\(n\)個小區間
\[ [x_0,x_1], \;[x_1,x_2], \; \; ..., \; [x_{n-1},x_n] \]
各個小區間的長度依次爲
\[ \triangle x_1 = x_1 - x_0, \; \triangle x_2 = x_2 - x_1, \; ..., \; \triangle x_{n-1} = x_n - x_{n-1} \]
在每一個小區間\([x_{i-1},x_i]\)上任取一點\(\xi_i(x_{i-1} \leqslant \xi_i \leqslant x_i)\),取函數值\(f(\xi_i)\)與小區間長度\(\triangle x_i\)的乘積\(f(\xi_i)\triangle x_i(i=1,2,...,n)\),再求和
\[ S = \sum_{i=1}^nf(\xi_i)\triangle x_i \]
記\(\lambda = \max\{{\triangle x_1, \triangle x_2, \; ..., \; \triangle x_n}\}\),當\(\lambda \to 0\)時,和\(S\)的極限\(I\)(有限)存在且\([a,b]\)的劃分和\(\lambda_i\)無關,則成函數\(f(x)\)在\([a,b]\)上可積,且稱這個極限\(I\)爲函數\(f(x)\)在區間\([a,b]\)上的定積分(簡稱積分),記做
\[ \int_a^bf(x)dx,即\int_a^bf(x)dx = \lim_{\lambda \to 0}\sum_{i=1}^nf(\xi_i)\triangle x_i = I \]
其中稱\(f(x)\)爲被積函數,\(f(x)dx\)爲被積表達式,\(x\)爲積分變量,\(a\)爲積分下限,\(b\)爲積分上限,\([a,b]\)爲積分區間。
定積分性質:
設\(f(x)\)在區間\([a,b]\)上連續,則\(f(x)\)在\([a,b]\)上可積。
設\(f(x)\)在區間\([a,b]\)上有界,且只有有限個間斷點,則\(f(x)\)在\([a,b]\)上可積。
當\(a=b\)時,\(\int_a^bf(x)dx,即\int_a^bf(x)dx=0\)
當\(a>b\)時,\(\int_a^bf(x)dx,即\int_a^bf(x)dx=-\int_b^af(x)dx\)
\(\int_a^b[f(x)\pm g(x)]dx = \int_a^bf(x)dx\pm\int_a^bg(x)dx\)
$ \int_a^b kf(x)dx = k\int_a^bf(x)dx$
設\(a<c<b\),則
\[ \int_a^bf(x)dx = \int_a^cf(x)dx+\int_c^bf(x)dx \]
設\(f(x) \equiv 1\),\(x\in[a,b]\),則
\[ \int_a^bf(x)dx = b - a \]
設\(f(x) \geqslant 0\),\(x\in[a,b]\)且\(a<b\),則
\[ \int_a^bf(x)dx \geqslant 0 \]
設\(f(x) \leqslant g(x)\),\(x\in[a,b]\)且\(a<b\),則
\[ \int_a^bf(x)dx \leqslant \int_a^bg(x)dx \]
定積分估值定理:若是\(m\leqslant f(x) \leqslant M,x\in [a,b]\),即\(m\)、\(M\)分別是\(f(x)\)在區間\([a,b]\)的最小、最大值,那麼
\[ m(b-a) \leqslant \int_a^b f(x)dx \leqslant M(b-a) \]
定積分中值定理:若是函數\(f(x)\)在\([a,b]\)上連續,則至少存在一點\(\xi\in[a,b]\),使得
\[ \int_a^bf(x)dx = f(\xi)(b-a) \; (a\leqslant\xi\leqslant b) \]
若是函數\(f(x)\)在區間\([a,b]\)上連續,則積分上限的函數
\[ \Phi(x) = \int_a^bf(t)dt \]
在\([a,b]\)上可導,而且它的導數
\[ \Phi'(x) = \frac{d}{dx}\int_a^xf(t)dt = f(x)\;(a\leqslant x \leqslant b) \]
若是函數\(f(x)\)在區間\([a,b]\)上連續,則函數
\[ \Phi(x) = \int_a^xf(t)dt \]
就是\(f(x)\)在區間\([a,b]\)上的一個原函數。
牛頓-萊布尼茲公式(Newton-Leibniz Formula):若是函數\(F(x)\)是連續的,是\(f(x)\)在區間\([a,b]\)上的一個原函數,則
\[ \int_a^bf(x)dx = F(x)\bigg|_a^b = F(b) - F(a) \]
也叫微積分基本公式。它揭示了被積函數與原函數之間的聯繫,說明一個連續函數在區間\([a,b]\)上的定積分等於它的任意一個原函數在區間\([a,b]\)上的增量,它爲定積分的計算提供了一個簡單而有效的方法。
定積分的方法:
有換元法、分部積分法,跟不定積分相似,惟一的區別是加了區間限制,再也不累述。
微積分就介紹到這裏了,其它更多高級的概念和性質,如多元微積分、多重微積分、無窮級數、冪級數等等,能夠另外找資料,也能夠在參考文獻裏尋得。
章節3.1.3 反射方程和4.2.6 光的能量已經列出了輻射度量的基本概念、符合、公式,本小節將作一些補充。
在二維平面幾何中,弧度(Radian,rad)是測量角度的標準單位,表示了與圓心角與其對應的圓弧長度的關係,見下圖:
上圖動態地描述了圓半徑如何轉化成圓弧,以及圓心角與弧度的對應關係。
能夠明顯看出,弧度只是衡量角度大小,跟半徑無關,因此弧度的計算方式是圓弧的長度除以圓半徑:
\[ 弧度 = 圓心角 = \frac{圓心角對應的弧長度}{圓半徑} \]
若是對角度和弧長進行微分,就可用微分的方式表達(\(ds\)表示微分的弧長):
\[ d\theta = \frac{ds}{r} \]
一樣地,在三維立體幾何中,也有跟弧度相似的概念,用來衡量三維球體的圓心角,它就是立體角(宏觀符號\(\Omega\),微分符號\(\omega\))。
立體角的定義,用公式表達就是:
\[ 立體角 = \frac{立體角對應的球表面面積}{半徑的影響因子} \]
從上面能夠看出,立體角與球體半徑無關,因爲是三維立體空間,且1單位立體角的球表面面積爲\(r^2\)(上圖),因此半徑的影響因子就是\(r^2\),用宏觀符號公式表達:
\[ \Omega = \frac {A}{r^{2} } sr \]
其中\(sr\)(Steradian)是立體角的單位,叫立體弧度或球面度。
若對立體角和球表面面積微分,可獲得微分形式的公式:
\[ d\omega = \frac {dA}{r^{2}} \]
利用Spherical Cap的面積公式,可求得半個球體的立體角:
\[ \Omega_{hemisphere} = \frac {2\pi r\cdot r}{r^{2} } sr = 2\pi \ sr \]
也就是說半個球體的立體角爲\(2\pi \ sr\),整個球體的立體角爲\(4\pi \ sr\):
\[ \Omega_{sphere} = 4\pi \ sr \]
輻射強度指經過單位立體角的輻射通量。用符號\(I\)表示,單位W\(/sr\),微分公式:
\[ I = \frac{d\Phi}{d\omega} \]
既然已有了輻照度和輻射度,爲何還要引入輻射強度呢?
緣由是在計算輻射時,有時會考慮某個點的通量的密度,但一個點的面積是0,沒法用輻照度和輻射度的公式,故而引入跟面積無關的輻射強度。而輻射強度之因此跟面積無關,是由於立體角只跟角度相關,跟球體的半徑、距離、面積無關。
也就是說,因爲立體角不會隨距離變化而變化,輻射強度不會隨距離變化而變化,不像點光源的輻照度會隨距離增大而衰減。
輻射率是測量微小方向照到微小表面的通量,即每單位面積每單位立體角的輻射通量密度。用公式表達:
\[ L = \frac{d\Phi}{d\omega dA^{\bot}} \]
輻射率實際上就是材質的顏色,在基於物理着色時,計算表面一點的顏色就是計算它的輻射率。
輻射率不會隨距離變化而衰減,這和真實世界的物理原理一致:在沒有空氣干擾的狀況下,咱們看到的物體顏色並不會隨距離變化而變化。
既然光的不少現象,包括反射、折射定律均可以用麥克斯韋方程組解釋,那咱們就有必要揭開它的神祕面紗。
麥克斯韋方程組是描述了電場、磁場與電荷密度、電流密度之間關係的偏微分方程。利用麥克斯韋方程組,能夠推論出電磁波在真空中以光速傳播,並進而作出光是電磁波的猜測。
實際上,麥克斯韋方程組雖然由英國物理數學家詹姆斯·克拉克·麥克斯韋(James Clerk Maxwell)提出,但最初提出來時有20個方程和20個變量。後來由英國物理學家奧利弗·赫維賽德(Oliver Heaviside)和美國物理數學家約西亞·威拉德·吉布斯(Josiah Willard Gibbs)以矢量分析的形式從新表達,也就是如今咱們使用的形式。
麥克斯韋方程組和洛倫茲力方程是經典電磁學的基礎方程。從這些基礎方程的相關理論,發展出現代的電力科技與電子科技。
它由4個方程組成:
高斯定律代表在靜電場中,穿過任一封閉曲面的電場通量只與封閉曲面內的電荷的代數和有關,且等於封閉曲面的電荷的代數和除以真空中的電容率。用積分形式表達的公式:
\[ \Phi_{\boldsymbol E} = \frac{Q}{\varepsilon_0} \]
其中,\(\Phi_E\)是穿過封閉曲面的電場通量,\(Q\)是封閉曲面內的總電荷,\(\varepsilon_0\)是真空中的電容率。
如果更嚴謹一些,引入封閉曲面\(S\)和及封閉曲面包含的體積\(V\),則有積分形式的公式:
\[ \Phi_{\boldsymbol E} = \oint_{\boldsymbol S} \boldsymbol E\cdot dA \]
其中\(\boldsymbol E\)是電場,\(dA\)是是封閉曲面的一塊極小的面積,\(\oint_S\)表示封閉曲面面積,有些文獻會寫成\(\iiint_S\)、\(\iint_S\)、\(\int_S\),表達的都是曲面面積。
還能夠用微分的形式表達:
\[ \triangle \cdot \boldsymbol E = \frac{\rho}{\varepsilon_0} \]
其中\(\triangle \cdot \boldsymbol E\)是電場散度,\(\rho\)是電荷密度。
因爲微分形式是從微觀層面描述的,從宏觀維度上,可表達成:
\[ D = \varepsilon \boldsymbol E \]
其中\(\boldsymbol D\)表示電場散度,\(\varepsilon\)表示材質電容率,\(\boldsymbol E\)表示電場。此公式只適應與均勻、各向同性、非分散的線性物質。
高斯磁定律代表磁場的散度等於零,所以磁場是一個螺線矢量場,還能夠推斷磁單極子不存在(下圖)。磁的基本實體是磁偶極子,而不是磁荷。
用微分形式的公式:
\[ \triangle \cdot \boldsymbol B = 0 \]
\(\triangle \cdot \boldsymbol B\)表示磁場的散度。
一樣地,能夠用積分形式:
\[ \newcommand{\oiint}{\bigcirc \hspace{-1.3em}\int \hspace{-0.8em}\int} \oiint_{\delta\Omega}\boldsymbol E \cdot d\boldsymbol S = \frac{1}{\varepsilon_0}\iiint_\Omega \rho dV \]
法拉第定律描述時變磁場怎樣感應出電場。在積分形式,它代表在閉環移動電荷所需的每單位電荷的工做量等於經過封閉表面的磁通量的減小速率。微分形式:
\[ \Delta \times \boldsymbol E = -\frac{\delta \boldsymbol B }{\delta t } \]
積分形式:
\[ \oint_{\delta \sum} \boldsymbol E \cdot d\boldsymbol l = -\frac{d}{dt}\iint_{\sum}\boldsymbol B \cdot d \boldsymbol S \]
麥克斯韋-安培定律代表,磁場的產生有兩種:
微分形式的公式:
\[ \Delta \times \boldsymbol B = \mu_0\bigg(\boldsymbol J + \varepsilon_0\frac{\delta\boldsymbol E}{\delta t} \bigg) \]
積分形式複雜一些:
\[ \oint_{\delta \sum} \boldsymbol B \cdot d\boldsymbol l = \mu_0\bigg(\iint_{\sum} \boldsymbol J \cdot d \boldsymbol S + \varepsilon_0\frac{d}{dt}\iint_{\sum}\boldsymbol E\cdot d \boldsymbol S \bigg) \]
因爲標準的\(L_AT^EX\)沒法表示閉合曲面的符號,可能跟維基百科的有些出入,具體參看維基百科的麥克斯韋方程組。
幾何光學有三條基本定律:
它們能夠由麥克斯韋方程組推導出來。
光波是電磁輻射,必須知足麥克斯韋方程組與伴隨的邊界條件,其中一條邊界條件爲,在邊界的臨近區域,電場平行於邊界的份量必須具備連續性。假設邊界爲xy-平面,則在邊界,有:
\[ E_{{||,i}}(x,y,0)+E_{{||,r}}(x,y,0)=E_{{||,t}}(x,y,0) \]
其中,\(E_{{||,i}}\)、 \(E_{{||,r}}\)、\(E_{{||,t}}\)分別爲在入射波、反射波、折射波(透射波)的電場平行於邊界的份量。
假設入射波是頻率爲$ \omega $的單色平面波,則爲了在任意時間知足邊界條件,反射波、折射波的頻率一定爲 $ \omega $。設定 \(E_{{||,i}}\)、\(E_{{||,r}}\)、\(E_{{||,t}}\)的形式分別爲
\(E_{{||,i}}=E_{{||,i0}}\ e^{{i{\mathbf {k}}_{i}\cdot {\mathbf {r}}-\omega t}}\)、
\(E_{{||,r}}=E_{{||,r0}}\ e^{{i{\mathbf {k}}_{r}\cdot {\mathbf {r}}-\omega t}}\)、
\(E_{{||,t}}=E_{{||,t0}}\ e^{{i{\mathbf {k}}_{t}\cdot {\mathbf {r}}-\omega t}}\),
其中,\(\mathbf{k}_i\)、 \({\mathbf {k}}_{r}\)、\({\mathbf {k}}_{t}\)分別是入射波、反射波、折射波的波矢,\(E_{{||,i0}}\)、\(E_{{||,r0}}\)、\(E_{{||,t0}}\)分別是入射波、反射波、折射波的波幅(多是復值)。
爲了在邊界任意位置\((x,y,0)\)知足邊界條件,相位變化必須同樣,必須設定
\(k_{{ix}}x+k_{{iy}}y=k_{{rx}}x+k_{{ry}}y=k_{{tx}}x+k_{{ty}}y\)。
所以,
\(k_{{ix}}=k_{{rx}}=k_{{tx}}\)、
\(k_{{iy}}=k_{{ry}}=k_{{ty}}\)。
不失通常性,假設\(k_{{iy}}=k_{{ry}}=k_{{ty}}=0\),則馬上能夠推斷第必定律成立,入射波、反射波、折射波的波矢,與界面的法線共同包含於入射平面。
從波矢x-份量的相等式,能夠獲得
\(k_{{i}}\sin \theta _{i}=k_{{r}}\sin \theta _{r}\)。
而在同一介質裏,\(k_{{i}}=k_{{r}}\)。因此,第二定律成立,入射角\(\theta _{i}\)等於反射角\(\theta _{r}\)。
應用折射率\(n\)的定義式:
\(n\ {\stackrel {def}{=}}\ {\frac {c}{v}}={\frac {ck}{\omega }}\),
能夠推斷第三定律成立:
\(n_{i}\sin \theta _{i}=n_{t}\sin \theta _{t}\);
其中,\(n_{t}\)、\(\theta _{t}\)分別是折射介質的折射率與折射角。
從入射波、反射波、折射波之間的相位關係,就能夠推導出幾何光學的三條基礎定律。
此外,還能夠用費馬原理、惠更斯原理、平移對稱性推導出來,更多參看維基百科的Snell's Law。
本節參考了基於物理着色:BRDF的公式推導部分。
假設有一束光照射到微表面上,入射光方向\(\omega_i\),視線方向\(\omega_o\),對反射到\(\omega_o\)方向的反射光有貢獻的微表面法線爲半角向量\(\omega_h\),則這束光的微分通量是:
\[ d \Phi_h = L_i(\omega_i) d \omega_i dA^{\bot}(\omega_h) = L_i(\omega_i) d \omega_i cos \theta_h dA(\omega_h) \]
其中\(dA(\omega_h)\)是法線爲半角向量\(\omega_h\)的微分微表面面積,\(dA^{\bot}(\omega_h)\)爲\(dA(\omega_h)\)在入射光線方向的投影,\(\theta_h\)爲入射光線\(\omega_i\)和微表面法線\(\omega_h\)的夾角。
Torrance-Sparrow將微分微表面面積\(dA(\omega_h)\)定義爲\(dA(\omega_h) = D(\omega_h) d \omega_h dA\),Torrance-Sparrow將前兩項解釋爲單位面積微平面中朝向爲\(\omega_h\)的微分面積。
要從一組微表面面積dA中獲得朝向爲\(\omega_h\)的微表面面積\(dA(\omega_h)\),只須要將\(D(\omega_h)\)定義爲\(dA\)中朝向爲\(\omega_h\)的比例,取值範圍在\([0, 1]\)就能夠了。這裏引入\(d \omega_h\)的實際用途稍後再討論。
由上兩式可得:
\[ d \Phi_h = L_i(\omega_i) d \omega_i cos \theta_h D(\omega_h) d \omega_h dA \]
設定微表面反射光線遵循菲涅爾定理,則反射通量:
\[ d \Phi_o = F_r(\omega_o) d \Phi_h \]
由上兩式可得反射輻射率:
\[ dL_o(\omega_o) = \frac{d \Phi_o}{d \omega_o cos \theta_o dA} = \frac{F_r(\omega_o) L_i(\omega_i) d \omega_i cos \theta_h D(\omega_h) d \omega_h dA}{d \omega_o cos \theta_o dA} \]
由BRDF的定義可得:
\[ f_r(\omega_i, \omega_o) = \frac{d L_o(\omega_o)}{d E_i(\omega_i)} = \frac{d L_o(\omega_o)}{L_i(\omega_i) cos \theta_i d \omega_i} = \frac{F_r(\omega_o) cos \theta_h D(\omega_h) d \omega_h}{cos \theta_o cos \theta_i d \omega_o} \]
這裏須要特別強調幾個夾角:
回到反射方程:
\[ L_o(v) = \int_{\Omega }^{} f(l, v) \otimes L_i(l) cos \theta_i d\omega_i \]
它是對\(d \omega_i\)積分,而上式分母包含\(d \omega_o\),能夠經過找到\(\frac{d \omega_h}{d \omega_o}\)的關係,把\(d \omega_o\)消掉。塞入\(d \omega_h\)並不會影響方程的合理性,由於\(D(\omega_h)\)是能夠調整的,如今\(D(\omega_h)\)是一個有單位的量,單位爲\(1/sr\)。
繼續\(d\omega_h\)和\(d\omega_o\)關係的推導:
如上圖,入射光線照射到一個微表面上,與微表面的單位上半球相交於點\(I\),與微表面相交於點\(O\),反射光線與單位上半球相交於點\(R\),反射光束立體角\(d \omega_o\)(圖中是\(d \omega_r\))等於光束與單位上半球相交區域面積\(dA_r\),法線立體角\(d \omega_h\)(圖中是\(d \omega^\prime\))等於法線立體角與單位上半球相交區域面積\(dA^\prime\),所以求\(\frac{d \omega_h}{d \omega_o}\)等價於求\(\frac{dA^\prime}{dA_r}\)。
連線\(IR\)與法線\(n^\prime\)相交於點\(P\),則\(IR = 2IP\),因爲\(dA_r\)與\(dA^{\prime \prime \prime}\)半徑的比值等於\(\frac{IR}{IP}\),而面積爲\(\pi r^2\),與半徑的平方成正比,因此\(dA_r = 4 dA^{\prime \prime \prime}\)
連線\(OQ\)長度爲1,\(OP\)長度爲\(cos \theta_i ^ \prime\),因此
\[ \frac{dA^{\prime \prime}}{dA^{\prime \prime \prime}} = \frac{1}{cos ^ 2 \theta_i ^ \prime} \]
而\(dA^{\prime \prime} = \frac{dA^{\prime}}{cos \theta_i^{\prime}}\)
由以上幾式可得\(\frac{dA^\prime}{dA_r} = \frac{1}{4 cos \theta_i ^ \prime}\)
須要注意的是,上圖中的\(\theta_i ^ \prime\)其實是微表面的半角\(\theta_h\),因此\(\frac{d \omega_h}{d \omega_o} = \frac{1}{4 cos \theta_h}\)
所以
\[ f_r(\omega_i, \omega_o) = \frac{F_r(\omega_o) D(\omega_h)}{4 cos \theta_o cos \theta_i} \]
前面講到過並不是全部朝向爲\(\omega_h\)的微表面都能接受到光照(Shadowing),也並不是全部反射光照都能到達觀察者(Masking),考慮幾何衰減因子G的影響,最終得出Cook-Torrance公式:
\[ f_r(\omega_i, \omega_o) = \frac{F_r(\omega_o) D(\omega_h) G(\omega_i, \omega_o)}{4 cos \theta_o cos \theta_i} \]
在第三章闡述PBR的Cook-Torrance原理和實現的時候,說起過不少預渲染技術,諸如:Cubemap、HDR環境光等。本章節主要是講解這些預計算或預卷積的技術,爲將耗時的部分提早渲染,以便減輕實時光照時的渲染消耗。
立方體圖卷積是以離線的方式預先爲場景的輻照度求解全部漫反射間接光照的積分。爲了解決積分問題,必須對每一個片元在半球\(\Omega\)內的全部可能方向對場景的輻射進行採樣。
然而,代碼實現上不可能在半球\(\Omega\)從每一個可能的方向採樣環境的照明,可能的方向數量在理論上是無限的。但能夠經過採用有限數量的方向或樣原本近似方向的數量,均勻間隔或從半球內隨機取得,以得到至關精確的輻照度近似,從而有效地用離散的方法求解積分\(\int\)。
即使採用離散的近似方法,對於每一個片元實時執行此操做仍然太昂貴,由於樣本數量仍然須要很是大才能得到不錯的結果,所以一般採用預計算解決實時的消耗問題。因爲半球\(\Omega\)的朝向決定所需捕獲輻照度的位置,能夠預先計算每一個可能的半球方向的輻照度,全部採樣的半球環繞着全部傳出的方向\(w_o\):
\[ L_o(p,\omega_o) = k_d\frac{c}{\pi} \int\limits_{\Omega} L_i(p,\omega_i) n \cdot \omega_i d\omega_i \]
給定任意方向向量\(w_i\)後,就能夠預計算的輻照度圖進行採樣。爲了肯定小塊表面的間接漫射(輻照)光的數量,能夠從半球的整個輻照度中採樣出圍繞其表面法線的總輻照度。取得場景的輻照度的代碼很簡單:
vec3 irradiance = texture(irradianceMap, N);
爲了生成輻照度圖,須要將環境的光照卷積轉換爲立方體圖。鑑於對於每一個片斷,表面的半球沿着法向量定向\(N\),對立方體圖進行卷積等於計算沿着法線\(N\)的半球\(\Omega\)內的每一個方向的總平均輻射度\(w_i\)。
3.3.1.2 從球體圖到立方體圖描述瞭如何從球體圖轉換成立方體貼圖,這樣就能夠直接獲取轉換後的立方體貼圖,以便在片斷着色器中對其進行卷積,並使用朝向全部6個面部方向呈現的幀緩衝區將其計算結果放到新的立方體貼圖中。因爲已經描述了將球體圖轉換爲立方體圖,能夠採用相似的方法和代碼:
#version 330 core out vec4 FragColor; in vec3 localPos; uniform samplerCube environmentMap; const float PI = 3.14159265359; void main() { // the sample direction equals the hemisphere's orientation vec3 normal = normalize(localPos); vec3 irradiance = vec3(0.0); [...] // convolution code FragColor = vec4(irradiance, 1.0); }
用environmentMap
從球體HDR環境圖轉換到HDR立方體圖。
卷積環境貼圖有不少種方法,此處將爲半球上的每一個立方體貼圖像素生成固定數量的樣本方向向量圍繞半球\(\Omega\)並平均結果。固定量的樣本向量將均勻地分佈在半球內部。注意,積分是連續函數,而且在給定固定量的樣本向量的狀況下離散地採樣積分函數只是近似值。若是使用的樣本向量越多,就越接近積分實際值,但同時預計算過程越慢。
圍繞着立體角\(dw\)的反射方程的積分\(\int\)很難處理,因此用其等效的球面座標\(\theta\)和\(\phi\)。
咱們使用極面方位角\(\phi\)在半球環之間採樣,其角度範圍是\(0\)和\(2\pi\),並使用仰角\(\theta\),其角度範圍是\(0\)和 \(\frac{1}{2}\pi\),這樣可方便地對半球進行採樣。採用球面角度後的反射公式:
\[ L_o(p,\phi_o, \theta_o) = k_d\frac{c}{\pi} \int_{\phi = 0}^{2\pi} \int_{\theta = 0}^{\frac{1}{2}\pi} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta \]
用黎曼和的方法以及給定的\(n_1\)、\(n_2\)球面座標採樣數量,可將積分轉換爲如下離散版本:
\[ L_o(p,\phi_o, \theta_o) = k_d\frac{c}{\pi} \frac{1}{n_1 n_2} \sum_{\phi = 0}^{n_1} \sum_{\theta = 0}^{n_2} L_i(p,\phi_i, \theta_i) \cos(\theta) \sin(\theta) d\phi d\theta \]
當離散地對兩個球面值進行採樣時,仰角越高\(\theta\),面積越小,如上圖所示。若是不對面積差進行處理,就會出現累積偏差。爲了彌補較小的區域,能夠增長額外的\(\sin\)值來縮放\(\sin \theta\)的權重。
給定每一個片斷調用的積分球面座標對半球進行離散採樣轉換爲如下代碼:
vec3 irradiance = vec3(0.0); vec3 up = vec3(0.0, 1.0, 0.0); vec3 right = cross(up, normal); up = cross(normal, right); float sampleDelta = 0.025; float nrSamples = 0.0; for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta) { for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta) { // spherical to cartesian (in tangent space) vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); // tangent space to world vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal; irradiance += texture(environmentMap, sampleVec).rgb * cos(theta) * sin(theta); nrSamples++; } } irradiance = PI * irradiance * (1.0 / float(nrSamples));
經過指定一個固定的sampleDelta
值來遍歷半球,減少或增長樣本增量將分別增長或減小準確度。
在兩個for
循環內,採用球面座標將它們轉換爲3D笛卡爾樣本向量,將樣本從切線空間轉換爲世界空間,並使用此樣本向量直接對HDR環境貼圖進行採樣。循環的最後將每一個樣本結果添加到irradiance
,併除以採樣的總數,獲得平均採樣輻照度。請注意,縮放採樣的顏色值是cos(theta)
,由於光線在較大的角度處較弱,而且sin(theta)
是爲了彌補較高仰角的半球區域中面積較小的樣本區域。
預過濾環境圖與預卷積輻照圖很是類似。不一樣之處在於,須要考慮粗糙度並在預過濾環境圖的不一樣mip級別中按順序地存儲更粗糙的反射。
經過使用球面座標生成均勻分佈在半球\(\Omega\)上的樣本向量來對環境貼圖進行復雜處理的方法,雖然這個方法適用於輻照度,但對於鏡面反射效果較差。當涉及鏡面反射時,基於表面的粗糙度,光在經過法線\(n\)附近的反射就越粗糙,範圍越大:
光線反射後全部可能的出射光造成的形狀被稱爲鏡面波瓣。隨着粗糙度的增長,鏡面波瓣的大小增長; 而且鏡面波瓣的形狀在變化的入射光方向上變化。所以,鏡面波瓣高度取決於材質。
當談到微表面模型時,能夠將鏡面波瓣想象爲給定一些入射光方向的微平面中間向量的反射方向。當看到的大多數光線最終反射在微平面中間矢量周圍的鏡面波瓣中,這樣的方法生成的樣本向量纔是有意義的,這個處理過程就是重要性採樣(Importance sampling)。
爲了充分掌握重要性採樣的重要性,須要先深刻研究已知的數學方法:蒙特卡洛積分。
蒙特卡洛積分主要圍繞着統計和機率理論的組合。它幫咱們離散地解決了一個羣體統計或重要性的問題,而沒必要考慮全部羣體。
例如,假設想要計算一個國家全部公民的平均身高。爲了獲得結果,能夠測量每一個公民並平均他們的身高,這將提供確切**的答案。可是,因爲大多數國家人口衆多,這不是一個現實的方法:須要花費太多的精力和時間。
另外一種方法是選擇一個小得多的徹底隨機(無誤差)的人口子集,測量他們的身高並平均結果。這我的口可能只有100人。雖然不如確切的答案准確,但也會獲得一個相對接近真相的答案,它被稱爲大數定律(Law of large numbers)。這個方法是,若是測量一個較小數量的子集\(N\),它從總人口中獲得真正隨機的樣本,結果將與真實答案相對接近,而且隨着樣本數量\(N\)的增長而變得更接近實際結果。
蒙特卡羅積分創建在這個大數定律的基礎上,並採用相同的方法來求解積分。從總人口和平均值中隨機抽取的方式簡單地生成樣本值\(N\),而不是爲全部可能的(理論上無限的)樣本值\(x\)求解積分。如\(N\)增長獲得的結果更接近積分的確切答案:
\[ O = \int\limits_{a}^{b} f(x) dx = \frac{1}{N} \sum_{i=0}^{N-1} \frac{f(x)}{pdf(x)} \]
爲了解決積分,咱們採起用\(N\)從人口\(a\)到\(b\)中隨機抽樣,將它們加在一塊兒併除以樣本總數以平均它們。該\(pdf\) 表明着機率密度函數(Probability density function),它代表特定樣本在整個樣本集上發生的機率。例如,人口高度的\(pdf\)看起來有點像這樣:
從該圖中能夠看出,若是咱們採用任意隨機樣本的人口,那麼挑選高度爲1.70的人的樣本的可能性更高,而樣本高度爲1.50的機率較低。
當涉及蒙特卡羅積分時,一些樣本可能比其餘樣本具備更高的生成機率。這就是爲何對於任何通常的蒙特卡羅估計,咱們根據\(pdf\)將採樣值除以採樣機率。到目前爲止,在估算積分的每一個例子中,生成的樣本是均勻的,具備徹底相同的生成概率。到目前爲止咱們的估計是不偏不倚,這意味着,鑑於樣本數量不斷增長,咱們最終將會收斂到積分的精確解。
可是,蒙特卡羅的一些樣本是有偏倚的,意味着生成的樣本不是徹底隨機的,而是聚焦於特定的值或方向。這些有偏倚的蒙特卡羅估計有一個更快的收斂速度,這意味着它們能夠以更快的速度收斂到精確值。可是,因爲此方法的偏向性質,它們可能永遠不會收斂到精確值。這一般是可接受的平衡,特別是在計算機圖形學中,由於只要結果在視覺上可接受,精確的解決方案就不過重要。正如咱們很快就會看到重要性採樣(使用偏置估計器)所生成的樣本偏向於特定方向,在這種狀況下,咱們經過將每一個樣本乘以或除以其對應的\(pdf\)來達到這一點。
蒙特卡羅積分在計算機圖形學中很是廣泛,由於它是以離散和有效的方式近似連續積分的一種至關直觀的方式:取任何面積或體積進行採樣(如半球\(\Omega\)),生成\(N\)區域/體積內的隨機樣本量和總和,並權衡每一個樣本對最終結果的權重。
蒙特卡洛積分是一個普遍的數學主題,這裏不會深刻研究具體細節,但會提到有多種方法能夠生成隨機樣本。默認狀況下,每一個樣本都是徹底隨機(僞隨機)的,由於咱們習慣了,可是經過利用半隨機序列的某些屬性,咱們能夠生成仍然是隨機的但具備有趣屬性的樣本向量。例如,咱們能夠用低差別序列(Low-discrepancy sequences)對蒙特卡洛進行積分,以生成隨機樣本,且每一個樣本分佈更均勻:
圖左:徹底僞隨機序列生成的採用點;圖右:低差別序列生成的採樣點。能夠看出右邊的更均勻。
當使用低差別序列生成蒙特卡羅樣本向量時,該過程稱爲準蒙特卡羅積分(Quasi-Monte Carlo integration)。準蒙特卡羅方法有更快的收斂速度,這使它們對性能繁重的應用程序感興趣。
鑑於新得到的蒙特卡羅和準蒙特卡羅積分的知識,咱們能夠使用一個有趣的屬性來實現更快的收斂速度,它就是重要性採樣( Importance sampling)。當涉及光的鏡面反射時,反射光向量被約束在鏡面波瓣中,其尺寸由表面的粗糙度決定。看到鏡面外的任何(準)隨機生成的樣本與鏡面積分無關,將樣本生成集中在鏡面波瓣內是有意義的,代價是蒙特卡羅估計有誤差。
重要性採樣是這樣的:在一些區域內生成樣本向量,該區域受到圍繞微平面中間向量的粗糙度的約束。經過將準蒙特卡羅採樣與低差別序列相結合並使用重要性採樣偏置採樣向量,能夠得到高收斂率。由於以更快的速度到達解決方案,因此只須要更少的樣原本達到足夠的近似值。所以,該組合甚至容許圖形應用程序實時解決鏡面反射積分,儘管它仍然比預先計算結果慢得多。
這裏,將經過基於準蒙特卡羅方法的隨機低差別序列,使用重要性採樣預先計算間接反射方程的鏡面反射部分。本小節使用的序列稱爲哈默斯利序列(Hammersley Sequence)。哈默斯利序列序列基於範德科皮特(Van Der Corpus)序列,它將以基數\(b\)表示的天然數列反轉可得結果。
鑑於一些巧妙的技巧,咱們能夠很是有效地產生,咱們將用它來得到一個序列哈默斯利樣品着色器程序範德語料庫序列,N
是總樣本:
// 反轉的範德科皮特序列 float RadicalInverse_VdC(uint bits) { bits = (bits << 16u) | (bits >> 16u); bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); return float(bits) * 2.3283064365386963e-10; // / 0x100000000 } // ---------------------------------------------------------------------------- // 哈默斯利序列 vec2 Hammersley(uint i, uint N) { return vec2(float(i)/float(N), RadicalInverse_VdC(i)); }
GLSL代碼Hammersley
函數給出了總樣本集爲\(N\)的低差別樣本\(i\)。
並不是全部與OpenGL的驅動程序都支持位運算符(例如WebGL和OpenGL ES 2.0),在這種狀況下,咱們可能但願使用不依賴於位運算符的替代版Van Der Corpus Sequence:
float VanDerCorpus(uint n, uint base) { float invBase = 1.0 / float(base); float denom = 1.0; float result = 0.0; for(uint i = 0u; i < 32u; ++i) { if(n > 0u) { denom = mod(float(n), 2.0); result += denom * invBase; invBase = invBase / 2.0; n = uint(float(n) / 2.0); } } return result; } // ---------------------------------------------------------------------------- vec2 HammersleyNoBitOps(uint i, uint N) { return vec2(float(i)/float(N), VanDerCorpus(i, 2u)); }
請注意,因爲舊硬件中的GLSL循環限制,序列會循環遍歷32
位能表示的全部數。這個版本性能較差,但能夠在全部硬件上運行。
值得一提的是,生成低差別序列的方法還有不少:
詳細請參看Low-discrepancy sequence。
咱們將基於表面粗糙度生成偏向於微表面中間矢量的通常反射方向的樣本矢量來取代統一或隨機(蒙特卡羅)地在積分半球\(\Omega\)上生成樣本向量。採樣過程將相似於以前的過程:開始一個大循環,生成一個隨機(低差別)序列值,取序列值在切線空間中生成一個樣本向量,轉換到世界空間並採樣場景的輻射。不一樣的是,咱們如今使用低差別序列值做爲輸入來生成樣本向量:
const uint SAMPLE_COUNT = 4096u; for(uint i = 0u; i < SAMPLE_COUNT; ++i) { // 使用Hammersley序列 vec2 Xi = Hammersley(i, SAMPLE_COUNT);
另外,爲了構建樣本向量,咱們須要一些方法來定向和偏置本來朝向某些表面粗糙度的鏡面波瓣的樣本向量。咱們能夠按照章節3.1.4 雙向反射分佈函數(BRDF)中的描述獲取NDF ,並將GGX NDF結合在Epic Games所描述的那樣球形採樣向量:
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness) { float a = roughness*roughness; float phi = 2.0 * PI * Xi.x; float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); float sinTheta = sqrt(1.0 - cosTheta*cosTheta); // from spherical coordinates to cartesian coordinates vec3 H; H.x = cos(phi) * sinTheta; H.y = sin(phi) * sinTheta; H.z = cosTheta; // from tangent-space vector to world-space sample vector vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); vec3 tangent = normalize(cross(up, N)); vec3 bitangent = cross(N, tangent); vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; return normalize(sampleVec); }
這給了咱們一個樣本向量,它基於一些輸入粗糙度和低差別序列值\(X_i\),而且在預期的微表面中間向量的周圍。請注意,根據迪斯尼原則的PBR研究,Epic Games使用平方粗糙度來得到更好的視覺效果。
用低差別序列的Hammersley序列和樣本生成爲咱們提供了最終肯定預過濾卷積着色器:
#version 330 core out vec4 FragColor; in vec3 localPos; uniform samplerCube environmentMap; uniform float roughness; const float PI = 3.14159265359; float RadicalInverse_VdC(uint bits); vec2 Hammersley(uint i, uint N); vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness); void main() { vec3 N = normalize(localPos); vec3 R = N; vec3 V = R; const uint SAMPLE_COUNT = 1024u; float totalWeight = 0.0; vec3 prefilteredColor = vec3(0.0); for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); vec3 H = ImportanceSampleGGX(Xi, N, roughness); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(dot(N, L), 0.0); if(NdotL > 0.0) { prefilteredColor += texture(environmentMap, L).rgb * NdotL; totalWeight += NdotL; } } prefilteredColor = prefilteredColor / totalWeight; FragColor = vec4(prefilteredColor, 1.0); }
根據輸入的粗糙度預先過濾環境,這些粗糙度在預過濾器立方體貼圖的每一個mipmap級別(從0.0
到1.0
)中變化,並將結果存儲在prefilteredColor
中。獲得的預過濾顏色除以總樣品權重,其中對最終結果影響較小的樣品(對於小NdotL)對最終重量的權重較小。
雖然上述的預過濾圖在大多數狀況下都能正常,但總會遇到一些瑕疵。下面列出最多見的,包括如何解決它們。
在具備粗糙表面的表面上對預濾鏡圖進行採樣意味着在其一些較低的mip級別上對預濾鏡圖進行採樣。對立方體貼圖進行採樣時,默認狀況下,OpenGL不會在立方體貼圖面上進行線性插值。因爲較低的mip級別都具備較低的分辨率,而且預濾波器映射與較大的樣本波瓣進行了卷積,所以立方體面之間的濾波的瑕疵變得很是明顯:
幸運的是,OpenGL爲咱們提供了經過啓用GL_TEXTURE_CUBE_MAP_SEAMLESS來正確過濾立方體貼圖面的選項:
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
只需在應用程序啓動時的某個位置啓用此屬性,接縫就會消失。
因爲鏡面反射中的高頻細節和劇烈變化的光強度,使鏡面反射卷積須要大量樣本以適當地解析HDR環境反射的普遍變化的性質。咱們已經採集了大量樣本,但在某些環境中,在某些較粗糙的mip級別上可能仍然不夠,在這種狀況下,將開始看到明亮區域周圍出現點狀圖案:
一種選擇是進一步增長樣本數,但這對全部環境都還不足夠。能夠經過(在預過濾卷積期間)不直接對環境貼圖進行採樣來減小這種僞影,而是基於積分的PDF和粗糙度對環境貼圖的mip級別進行採樣:
float D = DistributionGGX(NdotH, roughness); float pdf = (D * NdotH / (4.0 * HdotV)) + 0.0001; float resolution = 512.0; // resolution of source cubemap (per face) float saTexel = 4.0 * PI / (6.0 * resolution * resolution); float saSample = 1.0 / (float(SAMPLE_COUNT) * pdf + 0.0001); float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel);
不要忘記在環境貼圖上啓用三線性過濾,以便從如下位置對其mip級別進行採樣:
glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
而後讓OpenGL 在設置立方體貼圖的基本紋理後生成mipmap :
// convert HDR equirectangular environment map to cubemap equivalent [...] // then generate mipmaps glBindTexture(GL_TEXTURE_CUBE_MAP, envCubemap); glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
這種效果很是好,而且能夠在粗糙表面上的預過濾圖中刪除大多數點。
在預過濾環境啓動和運行的狀況下,咱們能夠關注分裂和近似的第二部分:BRDF。讓咱們再次簡要回顧一下鏡面分裂和近似:
\[ L_o(p,\omega_o) = \int\limits_{\Omega} L_i(p,\omega_i) d\omega_i * \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) n \cdot \omega_i d\omega_i \]
咱們已經在不一樣粗糙度級別的預過濾圖中預先計算了分裂和近似的左側部分。右側要求咱們在角度上收集BRDF方程\(n \cdot \omega_o\)、表面粗糙度和菲涅耳的\(F_0\)。這相似於將鏡面BRDF與純白環境或1.0
的恆定輻射\(L_i\)進行積分。將BRDF壓縮爲3個變量有點多,但咱們能夠將\(F_0\)移出鏡面BRDF方程式:
\[ \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) n \cdot \omega_i d\omega_i = \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) \frac{F(\omega_o, h)}{F(\omega_o, h)} n \cdot \omega_i d\omega_i \]
\(F\)是菲涅耳方程。將菲涅耳分母移動到BRDF給出瞭如下等效方程:
\[ \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} F(\omega_o, h) n \cdot \omega_i d\omega_i \]
用Fresnel-Schlick近似法代替最右邊的\(F\)可獲得:
\[ \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 + (1 - F_0){(1 - \omega_o \cdot h)}^5) n \cdot \omega_i d\omega_i \]
再進一步地,用\(\alpha\)替換\({(1 - \omega_o \cdot h)}^5\),將更容易解決\(F_0\):
\[ \begin{eqnarray*} &&\int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 + (1 - F_0)\alpha) n \cdot \omega_i d\omega_i \\ &=& \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 + 1*\alpha - F_0*\alpha) n \cdot \omega_i d\omega_i \\ &=& \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 * (1 - \alpha) + \alpha) n \cdot \omega_i d\omega_i \end{eqnarray*} \]
而後拆分菲涅耳函數\(F\)成兩個積分:
\[ \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (F_0 * (1 - \alpha)) n \cdot \omega_i d\omega_i + \int\limits_{\Omega} \frac{f_r(p, \omega_i, \omega_o)}{F(\omega_o, h)} (\alpha) n \cdot \omega_i d\omega_i \]
因爲\(F_0\)是常量,能夠從積分號內移出。接下來,咱們替換\(\alpha\)回原來的形式,獲得最終的BRDF方程:
\[ F_0 \int\limits_{\Omega} f_r(p, \omega_i, \omega_o)(1 - {(1 - \omega_o \cdot h)}^5) n \cdot \omega_i d\omega_i + \int\limits_{\Omega} f_r(p, \omega_i, \omega_o) {(1 - \omega_o \cdot h)}^5 n \cdot \omega_i d\omega_i \]
兩個獲得的積分分別表明了\(F_0\)的縮放和偏移。請注意,做爲\(f(p, \omega_i, \omega_o)\)已包含一個\(F\)項,因此\(F\)項都從f$中刪除了!
以相似於早期卷積環境圖的方式,咱們能夠在其輸入上卷積BRDF方程:\(n\)和\(\omega_o\)之間的角度和粗糙度,並將卷積的結果存儲在2D查找紋理(LUT)中。
BRDF卷積着色器在2D平面上運行,使用其2D紋理座標直接做爲BRDF卷積的輸入(NdotV
和roughness
)。卷積代碼很大程度上相似於預過濾卷積,不一樣之處在於它如今根據咱們的BRDF幾何函數和Fresnel-Schlick的近似值處理樣本向量:
vec2 IntegrateBRDF(float NdotV, float roughness) { vec3 V; V.x = sqrt(1.0 - NdotV*NdotV); V.y = 0.0; V.z = NdotV; float A = 0.0; float B = 0.0; vec3 N = vec3(0.0, 0.0, 1.0); const uint SAMPLE_COUNT = 1024u; for(uint i = 0u; i < SAMPLE_COUNT; ++i) { vec2 Xi = Hammersley(i, SAMPLE_COUNT); vec3 H = ImportanceSampleGGX(Xi, N, roughness); vec3 L = normalize(2.0 * dot(V, H) * H - V); float NdotL = max(L.z, 0.0); float NdotH = max(H.z, 0.0); float VdotH = max(dot(V, H), 0.0); if(NdotL > 0.0) { float G = GeometrySmith(N, V, L, roughness); float G_Vis = (G * VdotH) / (NdotH * NdotV); float Fc = pow(1.0 - VdotH, 5.0); A += (1.0 - Fc) * G_Vis; B += Fc * G_Vis; } } A /= float(SAMPLE_COUNT); B /= float(SAMPLE_COUNT); return vec2(A, B); } // ---------------------------------------------------------------------------- void main() { vec2 integratedBRDF = IntegrateBRDF(TexCoords.x, TexCoords.y); FragColor = integratedBRDF; }
從上面可看到,BRDF卷積是從數學到代碼的直接轉換。採起角度\(\theta\)和粗糙度做爲輸入,生成具備重要性採樣的樣本向量,在幾何體上處理它而且導出BRDF的菲涅耳項,並輸出對於每一個樣本的\(F_0\)的縮放和偏移,最後將它們平均化。
當與IBL一塊兒使用時,BRDF的幾何項略有不一樣,亦即變量\(k\)的解釋略有不一樣:
\[ \begin{eqnarray*} k_{direct} &=& \frac{(\alpha + 1)^2}{8} \\ k_{IBL} &=& \frac{\alpha^2}{2} \end{eqnarray*} \]
因爲BRDF卷積是咱們將使用的鏡面IBL積分的一部分,因此用\(k_{IBL}\)做爲Schlick-GGX幾何函數的參數:
float GeometrySchlickGGX(float NdotV, float roughness) { float a = roughness; float k = (a * a) / 2.0; // k_IBL float nom = NdotV; float denom = NdotV * (1.0 - k) + k; return nom / denom; } // ---------------------------------------------------------------------------- float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); float ggx2 = GeometrySchlickGGX(NdotV, roughness); float ggx1 = GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2; }
分裂和積分卷積的BRDF部分渲染結果以下:
利用預濾環境圖和BRDF 2D LUT,咱們能夠根據分裂和近似計算間接光照鏡面部分的積分。而後,組合間接或環境鏡面反射光,最終算出IBL光照結果。
5.4 預計算技術章節提到了一些離線渲染的加速技術,除此以外,常見的離線技術還有:
還能夠從如下小節中闡述的方法加速離線渲染部分。
主要是利用5.1 微積分(Calculus)描述的性質和定理對渲染公式進行優化:
具體例子能夠參看5.4 預計算技術。
將渲染通用的邏輯集成硬件指令或內建接口,能夠充分利用硬件的性能,從而爲渲染加速。
例如,將光線追蹤算法集成進GPU顯卡,而nVidia新一代RTX20系顯卡已經集成了光線追蹤技術,使得渲染效率更上一層樓。Unreal Engine 4.22的版本也集成了這一特性。
經過多線程、多進程、多設備的架構分攤消耗的幀渲染,使得每幀的渲染時間大大下降。這種技術在實時渲染領域也逐漸被普及。
不一樣於並行渲染的小規模架構,分佈式渲染一般以圖形工做站、集羣式渲染簇等中大型硬件架構爲依託,以知足電影級別的離線渲染加速需求。
下圖是《A MultiAgent System for Physically based Rendering Optimization》提出的一種多代理的加速渲染架構:
以上都是本文前面章節描述過的加速算法,這對於性能敏感的實時渲染領域是很是有必要的。
若干貼圖合成一張蒙板圖。將若干獨立的PBR屬性蒙板貼圖合成一張:
使用同一張蒙板貼圖同時控制PBR的顏色、金屬度、粗糙度等屬性。
減小PBR標準參數的使用。例如,金屬材質的漫反射大部分是黑色,因此無需額外的漫反射貼圖。
其它資源優化:材質、模型、渲染參數、紋理、PBR參數等等幾乎都有優化的餘地。
實時渲染領域還有不少優化方法值得嘗試和應用,好比:
《Moving Frostbite to PBR》提出的IES光照模擬。
《Applying Visual Analytics to Physically-Based Rendering》提出的可視化分析優化。
因爲移動設備廣泛的性能與PC機有必定的差距,因此要將PBR應用到移動端,性能優化的需求更加迫切。
上一小節提到的實時渲染優化一樣適用於移動端,此外,還可針對移動端作一些特殊的優化:
更多請參看《Optimizing PBR》,還可參看筆者的另一篇原創技術文章:《移動遊戲性能優化通用技法》
當今階段,因爲硬件、技術、理論等種種緣由的限制,PBR技術在不少時候只能是採起近似模擬的方法,特別是在實時渲染領域,甚至在不少中低端PC或移動端設備還沒法運行PBR技術。
可是,這不妨礙咱們想象PBR技術將來的趨勢和前景。
目前大多數PBR都是基於微平面(microfacet)的光照模型,micro即微米(\(10^{-6}m\)),而且將反射模型簡化成了幾何光學,忽略了衍射、干擾、色散、光譜能量分佈等等精確物理模型。
近兩年,有人提出了基於納米(nano,\(10^{-9}m\))級別的光照模型,能夠先看看它和微米級別的區別:
微米幾何(Microgeometry) | 納米幾何(Nanogeometry) |
---|---|
波瓣形狀取決於表面統計(微米級別的NDF) | 波瓣形狀取決於表面統計(納米級別的光譜能量分佈) |
忽略光波波長 | 強依賴於光波波長 |
入射角經過可見性機率影響表面 | 入射角經過透視收縮(foreshortening)機率影響表面 |
也就是說納米級別理論引入了SPD、光波波長,先進的表面統計,使得光照渲染更加物理正確真實了。
納米幾何將引入光波長、SPD等,會考慮光的衍射、干擾、色散等現象,顯得更加物理真實。
固然,這種技術目前只能用於電影級別的離線渲染,將來還有很長一段時間才能進入實時渲染領域的視野。
當前的PBR光照模型,包括Cook-Torrance及BSSRDF,大可能是基於一維的曲線擬合。
從上圖能夠看出,因爲是一維曲線,因此它們都是從中心向周邊散開的圓形形狀,只是圓的過渡稍有不一樣。
如果引入考慮光波波長的納米級別的SPD(光譜能量分佈),則能夠引入更加複雜的二維光照模型曲線圖:
上圖能夠看出,光的分佈曲線再也不是圓形形狀,而是變成複雜的相似棉花絮狀的二維圖。這種纔是更接近真實世界的光照曲線擬合。
雖然目前這種技術開銷很是昂貴,但相信將來不久,這種技術會逐漸成爲主流。
5.4 預計算技術中提到了不少預渲染、預卷積技術,這些都是爲了減輕實時渲染的負擔。並且實時渲染部分採用大量近似、簡化的手段,使得光照不那麼物理正確和真實。
將來若干年,離線渲染技術將會逐漸引入到實時渲染領域,使得實時渲染可以得到更加真實的渲染效果。
這裏所指的離線技術包括但不限於:光照追蹤、路徑追蹤、全局光照、環境光、局部靜態光、烘焙光。
如果能將這些技術引入到實時渲染領域,那將是振奮人心的。近期發佈的Unreal Engine 4.22的版本已經支持實時光線追蹤技術:
短片《Troll》展示虛幻引擎4.22的全新光線追蹤功能,可以實時渲染電影級別的畫質。
相信這只是開始,將來離線技術實時化的步伐將會愈來愈快。
近年來圖形學的技術蓬勃發展,圍繞着PBR爲中心的新興技術和理論百花齊放,相信將來也是如此,並且新的理論和技術會成倍加速發展。
這些新興技術涵蓋了基礎物理學、光學原理、數學模型、算法和數據結構、計算機語言、渲染技術、圖形API、GPU架構等等軟件和硬件領域。
將來隨着基礎理論、軟件和硬件的發展,PBR的發展迅猛,將會獲得更普遍地應用。橫向維度將涵蓋各行各業,縱向維度覆蓋高中端層次的設備。
例如,沉浸式4D影院,虛擬與現實混合的遊戲和教學互動(下圖),投影真實人像的幻影會議,電影畫質的移動端遊戲,甚至是電影畫質的街機遊戲。
雖然目前看還有一段差距,但相信在不久的將來,藉助PBR技術,這些預言將或多或少地呈如今你們面前。
總之,PBR技術在將來的發展和前景很是值得期待。
一些輔助工具能夠提高咱們在工做或者研發新技術的效率。
利用數學曲線擬合工具,能夠直觀地看到函數在參數具體化後的結果,並協助咱們獲得想要的數據。
利用曲線擬合工具直觀地展示數據曲線變化。
MathLab:MathLab是老牌數學分析和建模的工具,功能強大,用戶受衆廣,是理工科必備的軟件之一。一樣地能夠用於圖形渲染的曲線擬合。
Mathematica:跟MathLab相似,功能雖然不如MathLab多,但更輕巧,各有側重,很是適合曲線擬合。
Excel:你沒看錯,Excel一樣能夠用於簡單曲線的擬合,參考文獻裏有很多圖就出自Excel。
GeoGebra:功能強大,基本能夠畫出咱們所需的圖例,也可用以曲線擬合。它還有在線版。
幾何畫板:老牌數學繪圖軟件,數學教師的必備工具。
迪斯尼原則發佈的時候,隨同發佈了BRDF Explorer,用於查看BRDF的各類公式參數和材質屬性。
什麼?看完本文還以爲不過癮?
那麼,參考文獻的衆多資料等着你發掘。
騷年,走你~~
大部分圖片來自參考文獻及網絡,侵刪。
感謝並致敬全部參考文獻的做者。
因爲時間倉促、水平有限,紕漏在所不免,懇請指正。
歡迎分享本文連接,但未經容許,禁止轉載!