<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • 由淺入深學習PBR的原理和實現

    目錄

    一. 前言

    1.1 本文動機

    PBR全稱Physically Based Rendering,譯成中文是基于物理的渲染,是當今非常流行的一種擬真渲染技術。

    國內外研究它的人數不勝數,由此出現的書籍、論文、文章等資料也非常多,其中最富盛名的非《Physically Based Rendering From Theory to Implementation》莫屬,它非常系統全面深入地介紹了PBR的底層原理、渲染實現、公式推導、進階主題等內容。


    上圖:《Physically Based Rendering From Theory to Implementation, 3rd Edition》的封面,總頁數1270

    但是,這些資料大多存在一些問題,它們要么太簡單太籠統,不能系統全面地介紹PBR的原理和實現;要么太全面太復雜,動輒上千頁,對剛踏入PBR技術領域的人不夠友好,令人望而卻步。

    1.2 PBR知識體系

    PBR無疑是一種涉及綜合性交叉學科的技術,它涉及的技術有:

    • 數學
      • 基礎數學
      • 空間幾何
      • 線性代數
      • 積分和微積分
      • 統計學
      • 概率論
      • 離散數學
      • 數學建模
      • ...
    • 物理
      • 光學
      • 能量理論
      • 顏色理論
      • 成相理論
      • 物理材料學
      • 原子理論
      • 電磁理論
      • ...
    • 化學
      • 材料學
      • 分子學
    • 生物
      • 人眼構造
      • 視覺系統
      • 大腦感光原理
    • 計算機圖形學
      • 渲染管線
      • Shader編碼
      • 圖形API
      • GPU硬件架構
      • 圖像處理
      • 色域空間
      • ...

    PBR-White-Paper-Knowledge-Architecture-1.0
    知乎牛人毛星云總結出的PBR較完整的知識體系架構圖。如果看不清,請點這里

    對于一個剛接觸PBR技術的新人,一旦接觸到如此龐大的知識體系和如此大量陌生的名詞、理論、公式、實現,多半會在PBR的知識體系中迷失,成為難以逾越的鴻溝,產生畏懼心理,從而放棄PBR的學習。

    恰好筆者在近期研習了大量PBR相關的資料,有些資料反復觀摩了幾遍,對于PBR的漸進式學習有了一定的認知。于是萌生了撰寫此篇文章的念頭,以便讓從未接觸過PBR的人能循序漸進地學習它、應用它、掌握它、實現它。

    1.3 本文內容及特點

    本文有以下特點

    • 章節分層,層層遞進,承上啟下。
    • 引入大量精美配圖,使PBR教學生動有趣。
    • 采用Markdown語法,圖文編排更精美和統一。
    • 采用\(L^AT_EX\)表達數學公式。
    • 語句精煉,淺顯易懂。
    • 豐富多樣的舉例。
    • 緊緊圍繞PBR的核心原理,避免節外生枝。

    本篇文章主要有以下章節內容,分別從不同層次不同側重點描述了PBR的原理、實現、應用,面向的群體也不一樣:

    • 初階:PBR基本認知和應用
      • 內容:
        • 介紹PBR的概念、歷史、應用。
      • 面向:
        • 初級程序員
        • 美術
        • 初級TA(技術美術)
        • 想初步了解PBR基本認知和應用的人
    • 中階:PBR基本原理和實現
      • 內容:
        • 介紹PBR的基本原理和商業引擎的實現。
      • 面向:
        • 中級程序員
        • TA
        • 想了解PBR實現的人
    • 進階:PBR核心理論和原理
      • 內容:
        • 深入介紹PBR的核心理論和渲染原理。
      • 面向:
        • 進階程序員
        • 對PBR底層原理感興趣的人
    • 高階:PBR關聯理論和推導
      • 內容:
        • 介紹跟PBR核心理論相關的理論原理及推導。
      • 面向:
        • 高階程序員
        • 想全面且透徹了解PBR底層原理及理論的人

    從上面可以看出,每個章節都是承上啟下,層層遞進地剖析PBR原理及實現,從而達到由淺入深介紹PBR的目的。讀者可以根據目前的水平,以及想要了解的程度,有針對性有選擇性地閱讀不同的章節。其實本文標題更適合取為《分層漸進式學習PBR的原理和實現》,但,還是現在的標題淺顯易懂。

    需要注意的是,本文將圍繞PBR的核心理論做闡述,以實時渲染領域的Cook-Torrance的BRDF光照模型為實現案例。其它旁系理論不在本文探討的范圍,可以查閱其它文獻。

    二. 初階:PBR基本認知和應用

    本章內容主要介紹PBR的基本概念和衍變歷史,以及其在主流商業引擎的應用。
    面向的群體:

    • 初級程序員
    • 美術
    • 初級TA(技術美術)
    • 想初步了解PBR基本認知和應用的人

    2.1 PBR的基本介紹

    2.1.1 PBR概念

    PBRPhysically Based Rendering)譯成中文是基于物理的渲染。它是利用真實世界的原理和理論,通過各種數學方法推導或簡化或模擬出一系列渲染方程,并依賴計算機硬件和圖形API渲染出擬真畫面的技術。

    2.1.2 與物理渲染的差別

    那它為什么不叫物理渲染Physical Rendering)呢?

    物理渲染(Physical Rendering)是指跟真實世界完全一致的計算機渲染效果。

    為了回答這個問題,先了解一下真實世界的成相原理。

    真實世界的物體有著各自的材質屬性和表面特征,它們受到各種局部燈光和全局環境光的影響,而且它們之間又相互影響,最終這些信息通過光波的形式進入復雜的人眼構造,刺激視神經形成生物信號進入大腦感光皮層,最終讓人產生視覺認知。(下圖)

    有論文指出,絕大多數人的眼睛可以接收相當于**5億10億**個像素的信息量。目前主流的分辨率才百萬千萬級別,加上顯示器亮度范圍和屏幕像素間距的限制,遠遠達不到億級像素的渲染和亮度表示范圍。

    基于現階段的知識水平和硬件水平,還不能渲染跟真實世界完全一致的效果,只能一定程序上模擬接近真實世界的渲染畫面,故而叫基于物理的渲染Physically Based Rendering),而非物理渲染Physical Rendering)。

    2.1.3 PBR的特征

    這節闡述的是PBR呈現的效果特征,而非底層物理原理的特征。
    相比傳統的Lambert著色和Phong著色,PBR著色在效果上有著質的提升,可以表示更多更復雜的材質特征:

    • 表面細節
    • 物體粗糙度
    • 區別明顯的金屬和絕緣體
    • 物體的渾濁程度
    • 菲涅爾現象:不同角度有不同強度的反射光
    • 半透明物體
    • 多層混合材質
    • 清漆效果
    • 其它更復雜的表面特征


    Phong模型著色效果,只能簡單地表現理想模型的漫反射和高光,渲染出的效果跟真實世界相差甚遠。


    PBR材質效果球,它們真實地渲染出各類材質的粗糙、紋理、高光、清漆、邊緣光等等表面細節特征。PBR對渲染效果真實感的提升可見一斑。

    2.2 PBR的衍變歷史

    PBR從最初傳統型的Lambert光照發展至今,已經歷經200多年,期間發生多次迭代衍變和改進,主流光照模型和分支光照模型也遍地開花。下面按照時間順序著重對PBR衍變的關鍵技術節點做闡述。

    2.2.1 Lambert(1760年)

    Lambert模型是Johann Heinrich Lambert在1760年提出的光照模型。是傳統的光照模型。

    它計算的是漫反射。漫反射是光源照射到物體表面后,向四面八方反射,產生的反射效果。這是一種理想的漫反射光照模型。

    漫反射光的強度近似地服從于Lambert定律,即漫反射光的光強僅與入射光的方向和反射點處表面法向夾角的余弦成正比。


    Lambert模型著色效果,模擬了理想環境下的漫反射效果。

    2.2.2 Smith(1967年)

    Smith將Cook-Torrance的DFG部分的G幾何項有效地結合起來,使得幾何函數的近似法得到了有效地提升,后面章節將會闡述更多細節。

    2.2.3 Phong(1973年)

    Phong模型由美國越南裔學者裴祥風(Bùi T??ng Phong)發明,于1973年的博士論文首度發表。它也是一種傳統的理想的光照模型。

    相較Lambert,Phong增加了鏡面反射部分,使得物體渲染效果更接近真實世界(下圖)。

    2.2.4 Cook-Torrance(1982年)

    Cook-Torrance是Cook和Torrance于1982年聯合提出的光反射模型。

    該模型考慮在同一景物中不同材料和不同光源的相對亮度。它描述反射光線在方向上的分布和當反射隨入射角而改變時顏色的變化,并能求得從具體的實際材料制成的物體反射出來的光線的光譜能量分布,并根據這種光譜能量分布精確地再現顏色。

    簡而言之,Cook-Torrance增加了幾何項G、Fresnel項、粗糙度項D等信息。利用該模型渲染出的圖像真實感有了較大跨度的提升。

    Cook-Torrance光照模型渲染效果。它較好地渲染出模型的表面特征和光照效果。

    2.2.5 Oren Nayarh(1994年)

    Lambert模型由于是理想環境下的光照模擬,不能正確體現物體(特別是粗糙物體)表面的光照效果。

    Oren Nayarh模型對此做出了改進,主要對粗糙表面的物體建模,比如石膏、沙石、陶瓷等。用了一系列的Lambert微平面,考慮了微小平面之間的相互遮擋(shadowing and masking)和互相反射照明。它能一定程度上模擬真實物體的表面粗糙度,使物體更有質感。

    左:真實照片,中:Lambert模型效果,右:Oren Nayarh模型效果

    2.2.6 Schlick(1994年)

    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運算高。

    2.2.7 GGX(2007年)

    GGX模型所解決的問題是,如何將微平面反射模型推廣到表面粗糙的半透明材質,從而能夠模擬類似于毛玻璃的粗糙表面的透射效果。同時,它也提出了一種新的微平面分布函數 。

    上圖:GGX非常逼真地模擬半透明物體的效果。

    雖然它提出時被用于半透明物體的模擬,但它作為一種描述微平面法線方向分布的函數,同樣適用于渲染表面粗糙的不透明物體。

    GGX同樣可以非常逼真地模擬不透明物體的效果

    GGX已經廣泛應用于各種主流游戲引擎中,同時也是效果最好的。

    2.2.8 迪斯尼原則的BRDF(Disney principled BRDF, 2012年)

    在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抽象出的參數。

    2.2.9 現階段的BxDF(2019年)

    基于物理的光照模型已經發展了數十年,期間衍生的關鍵技術和變種技術非常多,它們各有適用場景或解決的各個具體應用場景的問題。

    近今年,PBR的技術主要朝著更逼真、更復雜、效能更好的方向,或是結合若干種模型的綜合性技術邁進。代表性技術有:

    • PBR Diffuse for GGX + Smith (2017)
    • MultiScattering Diffuse (2018)
    • Layers Material(分層材質)
    • Mixed Material(混合材質)
    • Mixed BxDF(混合BxDF)
    • Advanced Rendering(進階渲染)


    UE4渲染出的虛擬人Siren。綜合了分層材質、混合材質、混合BxDF、眼球毛發和皮膚渲染等新興技術。


    虛擬人Siren的皮膚細節。與數碼相機攝制的相片如出一轍,逼真程度令人咂舌。如果不特意提醒,很難相信這是游戲引擎實時渲染出來的畫面。

    2.3 PBR的應用領域

    PBR經過長時間的發展,技術上和渲染的效果突飛猛進,是計算機圖形學的下一代渲染技術。它在實時渲染和離線渲染領域都有著非常廣泛且深入的應用,主要有:

    • 電影和動漫。使用PBR技術渲染的真人電影,擬真電影,以及各類動漫電影數量非常多,比如早些年的《阿凡達》《飛屋環游記》,近期的《戰斗天使》《流浪地球》《馴龍高手3》等。

      電影《阿凡達》的人物畫面。

      電影《戰斗天使》的畫面。主角阿麗塔是計算機通過PBR技術渲染出來的虛擬角色,她與真人演員和真實環境無縫地融合在了一起。

      電影《流浪地球》的虛擬場景。特效制作公司利用PBR技術模擬出恐怖的身臨其境的畫面。

    • 實時游戲。PBR的身影流傳于PC游戲,在線游戲,移動游戲,主機游戲等游戲細分領域。相信接觸過游戲的人大多體驗過次世代效果的魅力。

      PC網游《逆水寒》的角色次世代效果。

      移動游戲《絕地求生·刺激戰場》的次世代場景。

      單機游戲《極品飛車20》的動感瞬間。

    • 計算機輔助設計與制造(CAD/CAM)。計算機圖形學剛起步時,便應用于此領域,PBR的引入,更加真實地幫助設計人員設計出與實物相差無幾的產品。

      電路板設計預渲染效果圖。

      跑車概念設計效果圖。

      室內家裝設計效果圖。

    • 計算機輔助教學(CAI)。通過逼真的PBR技術,渲染出教學內容所需的虛擬場景,佐以動畫技術,使得教學更加形象生動有趣。

    • 虛擬現實(VR/AR/MR)。虛擬技術通常需要佩戴眼鏡或頭盔等顯示設備,較多地用于軍事,教學,模擬訓練,醫學等領域。而VR引入PBR技術,能更逼真地模擬現實世界,讓參與者身臨其境。

      Magic Leap制作的VR概念圖。

    • 科學計算可視化。氣象、地震、天體物理、分子生物學、醫學等科學領域采用PBR技術將更真實地模擬自然規律,有助于科學家新發現,有助于高校師生教學。

      計算機模擬出的DNA雙螺旋結構圖。

    2.4 PBR在游戲引擎的應用

    迪斯尼自2012年提出迪斯尼原則的PBR理論后,在游戲和電影界引起轟動,隨后各大主流游戲引擎和渲染器及建模軟件紛紛實現基于斯尼原則的PBR技術。

    下面是主流游戲引擎支持迪斯尼原則的PBR時間表:

    • Unreal Engine 4:《Real Shading in Unreal Engine 4》,SIGGRAPH 2013
    • Unity 5:《Physically Based Shading in Unity》,GDC 2014
    • Frostbite(寒霜): 《Moving Frostbite to PBR》,SIGGRAPH 2014
    • Cry Engine 3.6:《Physically Based Shading in Cry Engine》,2015

    UE4和Unity在算法上的實現略有差別,但本章先不討論算法的實現問題,主要闡述材質上的參數。

    2.4.1 Unreal Engine 4的PBR

    UE4的PBR相對其它迪斯尼原則的PBR實現,在參數方面做了精簡,涉及的參數主要有:

    • 基礎色(Base Color):為材質提供基礎紋理色,是Vector3(RGB),它們的值都限定在0~1之間。

      利用UE4的材質編輯器處理Base Color。

    下表是經過測量后得出的非金屬材質的基礎色強度(非金屬材質只有單色,即強度):

    材質(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模擬的部分材質效果見下圖。

    上排從左到右:木炭、生混凝土、舊瀝青;下排從左到右:銅、鐵、金、鋁、銀、鎳、鈦。

    2.4.2 Unity的PBR

    Unity的PBR已經納入內建的標準著色器(Standard Shader),它的實現準則是用戶友好的(user-friendly),故而在材質編輯器里呈現給用戶是有限的參數,而且跟傳統的各類貼圖信息統一在了一起。

    Unity內部實現機制遵循了PBR的基本準則,支持金屬度,表面粗糙度,能量守恒,菲涅爾反射,表面陰影遮蔽等特性。

    Unity的Standard Shader編輯界面。

    其中跟PBR相關的參數:

    • Albedo:基礎色,相當于UE4的Base Color。可用紋理貼圖指定,也可用一個顏色值代替。
    • Metallic:金屬度,意義跟UE4的一致。但它可以用金屬貼圖代替,此時Smoothness參數會消失。

      Unity指定了Metallic貼圖后的效果,Smoothness參數消失。
    • Smoothness:光滑度,跟UE的粗糙度取值剛好相反,但都是表示材質表面的粗糙程度。

      Unity的Smoothness參數從0~1的變化。
      • Smoothness Source:指定存儲光滑度數據的紋理通道,可選擇金屬度、鏡面貼圖的Alpha通道或基礎色貼圖的Alpha通道。
    • Occlusion:遮蔽圖。用于指定材質接受間接光(如環境光)的光照強度和反射強度。

      Unity中使用遮蔽圖為人物陰暗面(臉部,脖子)屏蔽環境光的影響。
    • Fresnel:隨著物體表面法線與視線的角度增大,物體的反射能力增大,這種現象稱之為菲涅爾效應。在Unity中,無法直接調節菲涅爾效應的參數,但內部實現機制會自動處理。越光滑的表面具有越強的菲涅爾效應,相反,越粗糙的表面具有越弱的菲涅爾效應。

      上圖展示了菲涅爾效應從弱到強的漸變。

    三. 中階:PBR基本原理和實現

    上章主要介紹了PBR的歷史和逼真的效果特征。這章將重點介紹PBR的核心部分的基本原理及主流的實現方案,使讀者對PBR的核心理論有一定了解,并能掌握相關的編碼。

    主要面向:

    • 中級程序員
    • TA
    • 想了解PBR基本原理和實現的人

    在分析比較了大量資料之后,本章選取了LearnOpenGL的PBR教程作為依托,闡述PBR的基本原理和實現。

    3.1 PBR基礎理論和推導

    本節的理論和推導盡量簡化和精簡,更深入的原理和理論將在下一章闡述。

    滿足以下條件的光照模型才能稱之為PBR光照模型:

    • 基于微平面模型(Be based on the microfacet surface model)。
    • 能量守恒(Be energy conserving)。
    • 使用基于物理的BRDF(Use a physically based BRDF)。

    3.1.1 微平面(Microfacet)

    大多數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.2 能量守恒(Energy Conservation)

    在微平面理論中,采用近似的能量守恒:出射光的總能量不超過入射光的總能量(自發光材質除外)。3.1.1的粗糙度變化圖可以看出,材質粗糙度越大,反射的范圍越大,但整體亮度變暗。

    那么PBR是如何實現近似的能量守恒呢?

    為了回答這個問題,先弄清楚鏡面反射(specular)和漫反射(diffuse)的區別。

    一束光照到材質表面上,通常會分成反射(reflection)部分和折射(refraction)部分。反射部分直接從表面反射出去,而不進入物體內部,由此產生了鏡面反射光。折射部分會進入物體內部,被吸收或者散射產生漫反射。

    折射進物體內部的光如果沒有被立即吸收,將會持續前進,與物體內部的微粒產生碰撞,每次碰撞有一部分能量損耗轉化成熱能,直至光線能量全部消耗。有些折射光線在跟微粒發生若干次碰撞之后,從物體表面射出,便會形成漫反射光。

    照射在平面的光被分成鏡面反射和折射光,折射光在跟物體微粒發生若干次碰撞之后,有可能發射出表面,成為漫反射。

    通常情況下,PBR會簡化折射光,將平面上所有折射光都視為被完全吸收而不會散開。而有一些被稱為次表面散射(Subsurface Scattering)技術的著色器技術會計算折射光散開后的模擬,它們可以顯著提升一些材質(如皮膚、大理石或蠟質)的視覺效果,不過性能也會隨著下降。

    金屬(Metallic)材質會立即吸收所有折射光,故而金屬只有鏡面反射,而沒有折射光引起的漫反射。

    回到能量守恒話題。反射光與折射光它們二者之間是互斥的,被表面反射出去的光無法再被材質吸收。故而,進入材質內部的折射光就是入射光減去反射光后余下的能量。

    根據上面的能量守恒關系,可以先計算鏡面反射部分,此部分等于入射光線被反射的能量所占的百分比。而折射部分可以由鏡面反射部分計算得出。

    float kS = calculateSpecularComponent(...); // 反射/鏡面部分
    float kD = 1.0 - kS;                        // 折射/漫反射部分
    

    通過以上代碼可以看出,鏡面反射部分與漫反射部分的和肯定不會超過1.0,從而近似達到能量守恒的目的。

    3.1.3 反射方程(Reflectance Equation)

    渲染方程(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),它的作用是基于表面材質屬性來對入射輻射度進行縮放或者加權。

    3.1.4 雙向反射分布函數(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\)符號各自近似模擬了特定部分的表面反射屬性:

    • \(D\)(Normal Distribution Function,NDF):法線分布函數,估算在受到表面粗糙度的影響下,取向方向與中間向量一致的微平面的數量。這是用來估算微平面的主要函數。
    • \(F\)(Fresnel equation):菲涅爾方程,描述的是在不同的表面角下表面反射的光線所占的比率。
    • \(G\)(Geometry function):幾何函數,描述了微平面自成陰影的屬性。當一個平面相對比較粗糙的時候,平面表面上的微平面有可能擋住其他的微平面從而減少表面所反射的光線。

    以上的每一種函數都是用來估算相應的物理參數的,而且你會發現用來實現相應物理機制的每種函數都有不止一種形式。它們有的非常真實,有的則性能高效。你可以按照自己的需求任意選擇自己想要的函數的實現方法。

    Epic Games公司的Brian Karis對于這些函數的多種近似實現方式進行了大量的研究。這里將采用Epic Games在Unreal Engine 4中所使用的函數,其中\(D\)使用Trowbridge-Reitz GGX,\(F\)使用Fresnel-Schlick近似法(Approximation),而\(G\)使用Smith's Schlick-GGX。

    3.1.4.1 \(D\)(Normal Distribution Function,NDF)

    法線分布函數,從統計學上近似的表示了與某些(如中間)向量\(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;
    }
    

    3.1.4.2 \(F\)(Fresnel equation)

    菲涅爾方程定義的是在不同觀察方向上,表面上被反射的光除以被折射的光的比例。在一束光擊中了表面的一瞬間,菲涅爾根據表面與觀察方向之間的夾角,計算得到光被反射的百分比。根據這個比例和能量守恒定律我們可以直接知道剩余的能量就是會被折射的能量。

    當我們垂直觀察每個表面或者材質時都有一個基礎反射率,當我們以任意一個角度觀察表面時所有的反射現象都會變得更明顯(反射率高于基礎反射率)。你可以從你身邊的任意一件物體上觀察到這個現象,當你以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\)的點乘的結果。

    3.1.4.3 \(G\)(Geometry function)

    幾何函數模擬微平面相互遮擋導致光線的能量減少或丟失的現象。

    類似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;
    }
    

    3.1.4.4 Cook-Torrance反射方程(Cook-Torrance reflectance equation)

    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)。

    3.1.5 制作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.2 PBR的光照實現

    3.1章節闡述了Cook-Torrance反射方程的理論和公式意義。這節將探討如何將前面講到的理論轉化成一個基于直接光照的渲染器:比如點光源,方向光和聚光燈。

    3.2.1 輻照度計算

    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將環境光加入計算的時候我們才會需要用到積分,因為環境光可能來自任何方向。

    3.2.2 PBR表面模型( PBR surface model)

    我們先從寫一個能滿足前面講到的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);
        [...]
    }
    

    3.2.2.1 直接光照(Direct lighting)

    在這個教程的示例中,我們將會有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,除非我們通過金屬度屬性在F0albedo之間進行線性插值,才能得到一個不同的非金屬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;  
    

    3.2.2.2 線性和HDR渲染( Linear and HDR rendering)

    以上我們假設所有計算都在線性空間,為了使用這個結果我們還需要在著色器的最后進行伽馬校正(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渲染管線來說,線性空間跟高動態范圍有著超乎尋常的重要性,沒有這些就不可能繪制出不同燈光強度下的高光低光細節,錯誤的計算結果會產生難看的渲染效果。

    3.2.2.3 完整的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.3 使用紋理的PBR(Textured PBR)

    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你也可以法線光照看起來真實了很多。

    3.3 基于圖像的光照(Image Based Lighting,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\)都可能存在輻射,這些輻射對解決積分有著重要的作用。為解決積分有兩個要求:

    • 需要用某種方法獲得給定任意方向向量\(\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 \]

    拆分后,可分開處理漫反射和鏡面反射的積分。先從漫反射積分開始。

    3.3.1 漫反射輻照度(Diffuse irradiance)

    仔細分析上面方程的漫反射積分部分,發現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像素卷積的結果,輻照度圖有點像環境的平均顏色或光照顯示。從這個環境圖采樣任意方向,可獲得這個方向的場景輻照度。

    3.3.1.1 球體圖(Equirectangular map)

    球體圖(Equirectangular map)有些文獻翻譯成全景圖,它與cubemap不一樣的是:cubemap需要6張圖,而球體圖只需要一張,并且存儲的貼圖有一定形變:

    cubemap是可以通過一定算法轉成球體圖的,詳見這里

    3.3.1.2 從球體圖到立方體圖

    直接從球體圖采樣出環境光照信息是可能的,但它的開銷遠大于直接采樣立方體圖(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校正。

    渲染的效果如下圖:

    3.3.1.3 PBR和非直接輻射度光照(indirect irradiance lighting)

    輻射度圖提供了漫反射部分的積分,該積分表示來自非直接的所有方向的環境光輻射之和。由于輻射度圖被當成是無方向性的光源,所以可以將漫反射鏡面反射合成環境光。

    首先,得聲明預計算出的輻射度圖的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.2 鏡面的IBL(Specular 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) 
    

    3.3.3 完整的IBL

    首先是聲明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核心理論和原理

    上章主要介紹了PBR中Cook-Torrance的BRDF的兩個部分:直接光照和IBL。

    這章將深入介紹PBR核心部分的底層理論和原理,使讀者對PBR的底層原理有更徹底的理解。本章部分內容在上一章已經有所涉及,但會更加深入。

    主要面向:

    • 進階程序員
    • 對PBR底層原理感興趣的人

    4.1 再論PBR核心理論

    上章講述了符合PBR必須滿足以下3個條件:

    • 基于微平面模型(Be based on the microfacet surface model)。該模型將物體表面建模成無數微觀尺度上有隨機朝向的理想鏡面反射的小平面(microfacet)。微觀幾何(microgeometry)是在不同微表面改變其法線,從而改變反射和折射光的方向。常用統計方法處理微觀幾何現象,將表面視為具有微觀結構法線的隨機分布,在宏觀表面視為在每個點處多個方向上反射(和折射)光的總和。
    • 能量守恒 (Energy Conservation)。出射光線的能量永遠不能大于入射光線的能量。隨著表面粗糙度的增加,鏡面反射區域的面積會增加,但平均亮度則會下降。
    • 使用基于物理的BRDF(Use a physically based BRDF)。Cook-Torance的BRDF是實時渲染領域最普遍的PBR光照模型,上章詳述了其原理和實現。它是數學和物理領域里諸多知識的綜合體。

    若是將上面3點進一步詳細論述,將涉及以下知識點:

    • 光學
      • 光的性質
      • 光的理論
      • 光的能量
      • 幾何光學
        • 反射
          • 鏡面反射
          • 菲涅爾反射
        • 折射
          • 散射
          • 吸收
    • 物質
      • 微觀表面
      • 宏觀表面
      • 分類:金屬、電介質、半導體
      • 與光學的交互
    • 能量
      • 光能
      • 電能
      • 動能
      • 熱能
      • 能量守恒
      • 能量轉化
    • PBR綜合
      • 材質分類
      • 屬性模擬
      • 光照模型

    總結起來,PBR就是光學原理和物體結構交互作用的抽象和模擬。下面先從光的性質說起。

    4.2 光的性質

    4.2.1 光是什么?

    有人說光是粒子,有人說光是電磁,有人說光是一種波,有人說光是一種能量,還有人說光是量子,那么光到底是什么?

    狹義上說,光是電磁輻射的某一部分內人眼可見的電磁頻譜,即可見光,它是人眼可感知的可見光譜,是造成視覺的原因。

    可見光通常被定義為具有波長在400-700納米(nm)的范圍內,不可見的有紅外線(具有更長的波長)和紫外線(具有更短的波長)。

    廣義上說,光指的是任何波長的電磁輻射,無論是否可見。包括伽馬射線、X射線、微波和無線電波。而可見光(400-700納米)只是所有波長區域的一小部分:

    4.2.2 電磁頻譜和可見光(Electromagnetic spectrum and visible light)

    電磁輻射(Electromagnetic Radiation,EMR)按波長從長到短分為:無線電波、微波、紅外線、可見光、紫外線、X射線和伽瑪射線。

    EMR的行為取決于其波長。較高頻率具有較短波長,較低頻率具有較長波長。不同波長的電磁輻射攜帶著不同的能量。當EMR與單個原子和分子相互作用時,其行為取決于它攜帶的每個量子的能量。

    不同波長的可見光代表著不同的顏色。太陽光、日光燈等可見光是一組不同波長的電磁輻射的集合,在三棱鏡下可以被分離出不同的顏色:

    不同來源對可見光的定義略有不同,有的將可見光定義為狹窄的420-680nm,有的寬達380-800nm。在理想的實驗室條件下,人們可以看到至少1050納米的紅外線; 兒童和年輕人可能會感知波長低至約310-313納米的紫外線。

    4.2.3 人眼感知可見光原理

    上節闡述了可見光的范圍和簡單的感知理論,本小節將深入闡述人類為什么會感知并且只感知波長為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 位雙鍵)發生光響應,并實現信息的有效傳遞。

    所以,人類感知當前波段的可見光,是億萬年不斷進化的結果。換個角度說,感知其它波段的人類祖先已經被淘汰了,他們的基因無法遺傳傳承下來。

    可謂:物競天擇,適者生存

    4.2.4 光的來源

    眾所皆知,光是電磁波,而物質是由原子組成,原子是由原子核與核外運轉著的電子組成。那么,物質原子中的電磁波是哪里來的?電磁波難道會無中生有?

    奧斯特實驗發現了直流導線的周圍產生磁場,因為電子的運動伴生著磁場。電子的運動分為線性運動振動

    • 線性運動:電子的線性運動是核外電子的繞核運動及在導電時電子的流動,它所伴生電磁波的宏觀表現是磁場。電子的線性運動不是產生光的原因。
    • 振動:電子的振動與發光息息相關,它會使電磁脫離場源形成電磁波,也就是產生了光,而不是所謂的光子。引起電子振動有兩種原因:
      • 一是高溫物質核外電子的躍遷引發的振動,這種振動需要物質的溫度大大高于環境溫度,運轉速率很高的核外電子躍遷輻射才能達到可見光的頻率。這種高溫物質核外電子的躍遷輻射所形成發光的光源叫熱光源。巖漿、鐵水、火焰、燈絲等高溫物質的發光屬于熱光源。
      • 二是電子在磁場或電場的作用下引發的受激振動,這樣的電子振動與溫度無關、與核外電子運轉速率無關。這種不需要高溫而使電子振動所形成輻射的光源叫冷光源。日光燈、節能燈、極光、螢火蟲的發光、半導體發光(LED)等屬于冷光源。

    本小節開頭的問題有了答案:光源中的光來自于電子的振動,電子振動所伴生的電磁波輻射形成了光波,電子振動的頻率構成了光波的頻率,大量電子振動所伴生的電磁波輻射形成了光源。

    4.2.5 光的理論

    光的研究和理論經過數百年的發展,至今出了很多理論學說,每種理論都是為了解釋部分光的物理現象。

    目前,光存在的理論主要有:粒子理論、波動理論、電磁理論、量子理論及波粒二象性等。

    4.2.5.1 光的粒子理論(Particle theory)

    光的粒子說又稱光的微粒說,這種理論認為光的本質與通過它反射而可見的實體物質一樣,是一種粒子(下圖)。

    光的粒子性示意圖

    法國數學家皮埃爾·加森迪(Pierre Gassendi)于1660年提出了一種光的粒子理論。 Isaac Newton在Gassendi的理論基礎上做了擴展:光是由來自各個方向或從各個方向發射的微粒(物質粒子)組成的。

    牛頓隨后對于加森迪的這種觀點進行研究,他根據光的直線傳播規律、光的偏振現象,最終于1675年提出假設,認為光是從光源發出的一種物質微粒,在均勻媒質中以一定的速度傳播。

    微粒說很容易解釋光的直進性,也很容易解釋光的反射,因為粒子與光滑平面發生碰撞的反射定律與光的反射定律相同。

    然而微粒說在解釋一束光射到兩種介質分界面處會同時發生反射和折射,以及幾束光交叉相遇后彼此毫不妨礙的繼續向前傳播等現象時,卻發生了很大困難。

    4.2.5.2 光的波動理論(Wave theory)

    在1660年代,胡克(Robert Hooke)發表了他的光波動理論。他認為光線在一個名為光以太(Luminiferous ether)的介質中以波的形式四射,并且由于波并不受重力影響,光在進入高密度介質時會減速。

    光的波理論預言了干涉現象以及光的偏振性。

    歐拉是波動學說的支持者之一,他認為波理論更容易解釋衍射現象。

    菲涅耳也支持并獨立完成了他的波動理論。在1821年,菲涅爾使用數學方法使光的偏振在波動理論上得到了唯一解釋。

    上圖:光的偏振現象,回旋光波先后經過四分一波偏振板和線性偏振板的情形。

    但是,波動理論的弱點在于,波類似于聲波,傳播需要介質。雖然曾有過光以太介質的假想,但因為19世紀邁克耳孫-莫雷實驗陷入了強烈的質疑。

    牛頓推測光速在高密度下變高,惠更斯和其他人覺得正相反,但當時并沒有準確測量光速的條件。直到1850年,萊昂·傅科(Léon Foucault)的實驗得到了和波動理論同樣的結果。之后,經典粒子理論才真正被拋棄。

    4.2.5.3 光的電磁理論(Electromagnetic theory)

    光的電磁理論是關于光的本性的一種現代學說,19世紀60年代由麥克斯韋提出。把光看成是頻率在某一范圍的電磁波。能解釋光的傳播、干涉、衍射、散射、偏振等現象,以及光與物質相互作用的規律。

    電磁理論還認為,電磁波具有互相垂直的電場與磁場,電場與磁場的頻率、振幅、波長、傳播方向是一致的。

    但由于光還具有粒子性,所以它不能解釋光電效應、康普頓效應等物理現象。

    4.2.5.4 光的量子理論(Quantum theory)

    光的量子理論是以輻射的量子理論研究光的產生、傳輸、檢測及光與物質相互作用的學科。

    量子光學示意圖

    1900年,普朗克在研究黑體輻射時,為了從理論上推導出得到的與實際相符甚好的經驗公式,他大膽地提出了與經典概念迥然不同的假設,即“組成黑體的振子的能量不能連續變化,只能取一份份的分立值”。

    1905年,愛因斯坦在研究光電效應時推廣了普朗克的上述量子論,進而提出了光子的概念。他認為光能并不像電磁波理論所描述的那樣分布在波陣面上,而是集中在所謂光子的微粒上。在光電效應中,當光子照射到金屬表面時,一次為金屬中的電子全部吸收,而無需電磁理論所預計的那種累積能量的時間,電子把這能量的一部分用于克服金屬表面對它的吸力即作逸出功,余下的就變成電子離開金屬表面后的動能。

    1923年,亞瑟·霍利康普頓表明,當從電子散射的低強度X射線(所謂的康普頓散射)中看到的波長漂移可以通過X射線的粒子理論來解釋,而不是波動理論。

    1926年Gilbert N. Lewis將這些光量子粒子命名為光子

    2018年2月,科學家首次報道了一種可能涉及極化子的新型光的發現,這可能對量子計算機的發展有用。

    量子力學作為一門“很數學”化的物理體系,已經像經典力學那樣成熟了,并成為洞悉微觀世界的重要工具。

    但量子力學也給留下了許多物理上的困惑,如粒子運動的波粒二象性問題、幾率波問題、粒子糾纏問題、波函數崩塌問題等等。

    4.2.5.5 光的波粒二象性(Wave-particle duality)

    歷史上關于光是粒子還是波動的爭論,已有兩千多年(下圖)。

    光的種種現象和性質表明它既有粒子的特征又有波動的特征,處于兩個派別立場的研究者各執一詞,互不相讓。

    直到1905年,愛因斯坦在德國《物理年報》上發表了題為《關于光的產生和轉化的一個推測性觀點》的論文。他認為對于時間的平均值,光表現為波動;對于時間的瞬間值,光表現為粒子性。這是歷史上第一次揭示微觀客體波動性和粒子性的統一,即波粒二象性。這一科學理論最終得到了學術界的廣泛接受。

    在新的事實與理論面前,光的波動說與粒子說之爭以“光具有波粒二象性”而落下了帷幕。

    即:光粒子的運動軌跡是呈周期性的波

    Wikipedia提供了一個視頻,形象地描述了光在各種理論下的特征。

    4.2.6 光的能量

    光是能量的一種傳播方式。光能量也被稱為光子能量(按粒子性)或電磁輻射(按波動性)。每個光子都具有一定量的能量,頻率越高,能量也越高。

    光的度量跟能量或輻射測量類似,常被用于太陽能、加熱、照明、電信、計算機圖形學等領域。

    光能量作為能量,可被測量,單位是焦耳(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}\) 通過單位面積單位立體角的輻射通量

    4.3 光學原理(Optics theory)

    光學(Optics)是物理學的一個分支,研究光的行為和性質,包括它與物質的相互作用以及使用或檢測它的儀器的結構。

    光學通常描述可見光、紫外光和紅外光的行為。由于光是電磁波,其它波段的電磁輻射(如X射線、微波和無線電波)表現出類似的特性。

    光學按照不同角度、不同粒度和不同側重點大致可以分為以下幾類:

    • 電磁光學。將光分為大多數光學現象可以使用光的經典電磁描述來解釋。然而,光的完整電磁描述通常難以應用于實踐中,需要借助其它光學類型。
    • 幾何光學。幾何光學系統將光線視為一組光線,它們以直線傳播,并在通過或從表面反射時彎曲。是物理應用中簡化的一種模型。由于PBR的BRDF幾乎都是基于幾何光學,后面章節會側重地介紹幾何光學。
    • 物理光學。物理光學是一種更全面的光模型,包括衍射和干涉等波效應幾何光學中無法解釋的。歷史上,首先開發基于射線的光模型,然后是波的光模型。19世紀電磁理論的進步才發現光波實際上是電磁輻射。
    • 運動物理光學。主要研究天體運動的光速差、光漂移、多普勒效應等。當前已經發展成一支龐大的獨立的物理分支。
    • 量子光學。一些現象取決于光具有波狀和粒子狀特性的事實。這些效應的解釋需要量子力學。當考慮光的粒子特性時,光被建模為稱為“光子” 的粒子集合。量子光學涉及量子力學在光學系統中的應用。

    光學與許多相關學科聯合進行研究,包括天文學、工程領域、攝影、計算機和醫學等等。光學的應用存在于各種日常物品中,包括鏡子、透鏡、望遠鏡、顯微鏡、激光器和光纖等等。

    4.3.1 光的反射(Reflection)

    光的反射是當光在兩種物質分界面上改變傳播方向又返回原來物質中的現象。

    上圖:光的反射現象。

    產生反射的原理:光是電磁波,射在物體上的光波引起單個原子中的極化振蕩(或電子在金屬中的振蕩),致使每個粒子在各個方向上輻射小的二次波,如偶極天線( dipole antenna)。根據惠更斯-菲涅耳原理,所有這些波加起來就產生反射和折射。

    光的反射細分為以下幾種:

    • 鏡面反射(Specular reflection):平行光線射到光滑表面上時反射光線也是平行的現象。表面平滑的物體,易形成光的鏡面反射,形成刺目的強光,反而看不清楚物體。
    • 漫反射(Diffuse reflection):平行光線射到凹凸不平的表面上,反射光線射向各個方向的現象。
    • 方向反射(Directional reflection):是介于漫反射和鏡面反射之間反射,也稱非朗伯反射。其表現為各向都有反射,且各向反射強度不均一。
    • 回射(Retroreflection):入射光射在介質表面上,反射光的方向與入射光一致的現象。飛機在飛越被陽光照射的云層時,在飛機陰影周圍看到的區域將顯得更亮,從草地上的露水可以看到類似的效果。

    4.3.2 光的折射(Refraction)

    光的折射是指光從一種介質斜射入另一種介質時,傳播方向發生改變,從而使光線在不同介交界處發生的偏折。

    上圖:光的折射現象。

    折射的原理與反射類似:光波是一種特定頻段的電磁波,光在傳播過程中有兩個垂直于傳播方向的分量:電場分量和磁場分量。當電場分量與介質中的原子發生相互作用,引起電子極化,形成電子云和原子荷重心發生相對位移。導致光的一部分能量被吸收,同時光在介質中的速度被減慢,方向發生變化,引發了折射。

    上圖:光從一種物質進入另一種物質后發生了折射,波長、方向、速率都發生了改變。

    近代物理學指出,光是一種沒有靜質量、體積非常小、運動速度比較高的物質。光和其它物質有相同的性質。光和物質間的相互作用力使光的運動方向發生改變即折射。

    4.3.3 光的散射(Scattering)

    光通過不均勻媒質時,部分光束將偏離原來方向而分散傳播,從側向也可以看到光的現象。

    散射發生的原理:當光子與分子或原子相互接近時,由于雙方具有很強的相互斥力,迫使它們在接觸前就偏離了原來的運動方向而分開。

    散射是觀察和辨別物體的主要現象,是自然中最普遍存在的現象。漫反射其實也是散射的一種。

    4.3.4 光的色散(Dispersion)

    光的色散指的是復色光分解為單色光的現象。

    色散現象說明光在介質中的速度\(v=\frac{c}{n}\)\(n\)為介質的折射率)隨光的頻率\(f\)而變。光的色散可以用三棱鏡、衍射光柵、干涉儀等來實現。

    光的色散說明了光具有波動性。因為色散是光的成分(不同色光)折射率不同引起的,而折射率由波的頻率決定。

    對同一種介質,光的頻率越高,介質對這種光的折射率就越大。在可見光中,紫光的頻率最高,紅光頻率最小。當白光通過三棱鏡時,棱鏡對紫光的折射率最大,光通過棱鏡后,紫光的偏折程度最大,紅光偏折程度最小。這樣,三棱鏡將不同頻率的光分開,就產生了光的色散。

    為什么在同一介質中,不同波長的光,其速度和折射率會不同呢?

    由于光有粒子性,與介質的原子、分子有相互作用力。對于波長越短的光,其攜帶的能量越大、運動越強,與介質的原子、分子的相互作用力越大,致使其速度越小、折射率越大。(這個回答是筆者根據經典物理學結合波動論的推測,未找到確切的依據和論據,有待考證!)

    4.3.5 光的吸收(Absorption)

    電磁理論認為,光的吸收是光(電磁輻射)通過材料時,與材料發生相互作用,電磁輻射能量被部分地轉化為其他能量形式的物理過程。

    當被吸收的光能量以熱能的形式被釋放,即形成了光熱轉化;當未被吸收的光能量被物體反射、散射或透射,便影響著我們看到的物體的色彩。

    量子理論認為,光的吸收是指分子或原子在光波輻射場(光照)下,會吸收光子的能量由低能態躍遷到高能態的現象。這種躍遷也等效于一個具有一定固有頻率的振子。

    電磁理論證明,當物體對某種頻率光的吸收系數很大時,它對該頻率光的反射率也大。若干電介質具有很強的吸收帶,故它們對于吸收帶附近頻率的光也有很強的反射,這稱為選擇反射

    半導體材料在不同的程度上具備電介質和金屬材料的全部光學特性。當半導體材料從外界以某種形式(如光、電等)吸收能量,則其電子將從基態被激發到激發態,即光吸收。而處于激發態的電子會自發或受激再從激發態躍遷到基態,并將吸收的能量以光的形式輻射出來(輻射復合),即發光;當然也可以無輻射的形式如發熱將吸收的能量發散出來(無輻射復合)

    金屬的光吸收要同時考慮束縛電子與自由電子的作用。對于紅外線或更低頻率的輻射,自由電子起主要作用;而對于紫外線及更高頻率的輻射,則束縛電子的作用比較顯著,這時金屬實際上表現出與電介質相似的光學性質。

    4.3.6 光的衍射(Diffraction)

    衍射是指當光波遇到障礙物或狹縫時發生的各種現象。它被定義為圍繞障礙物或孔的角落的波浪彎曲到障礙物的幾何陰影區域中。

    由于光具有波動性,所以也會產生衍射。

    光波穿過單波長的縫隙后發生了衍射現象。

    當光波穿過具有變化的折射率的介質時,也會發生衍射的效果。所有波都會發生衍射,包括聲波、水波和電磁波(可見光、X射線和無線電波)。

    光的衍射產生的原理可以從兩方面解釋:

    • 波動光學:根據惠更斯 - 菲涅耳原理和波疊加原理,衍射是由于波傳播的方式而產生的。通過將波前傳輸介質的每個粒子視為二次球面波的點源,可以可視化波的傳播,任何后續點的波位移是這些二次波的總和。當這些波被加在一起時,它們的總和由相對相位以及各個波的幅度確定,使得波的總和幅度可以具有零和各個幅度之和之間的任何值
    • 量子光學:在通過狹縫傳播光時,每個光子具有波函數,波函數描述了從發射器通過狹縫到屏幕的路徑。波函數(光子將采取的路徑)由物理環境決定,例如狹縫形狀、屏幕距離和光子創建時的初始條件。

    4.3.7 光的疊加和干涉(Superposition and interference)

    若干個光波組合在一起形成的復合效果便是光的疊加。

    更準確地說,在沒有非線性效應的情況下,疊加原理可用于通過簡單地添加干涉來預測相互作用波形的形狀。產生光波的復合圖案的相互作用通常被稱為干涉,干涉可能導致不同的結果。

    光波在不同相位產生的疊加效果。

    光產生疊加和干涉現象的原理與衍射類型,便不再累述。

    4.3.8 光的偏振(Polarization)

    偏振是波的一般屬性,描述了它們的振蕩方向。由于光具有波動性,所以也會偏振。

    偏振為橫向波(如許多電磁波)描述了垂直于行進波方向的平面中的振蕩方向。振蕩可以在單個方向上(線性偏振),或者隨著波行進方向而旋轉(圓形或橢圓形偏振)。

    左:線性偏振;中:圓形偏振;右:橢圓偏振。

    4.4 幾何光學(Geometry optics)

    幾何光學是將光的波長視作無限小,以致可以將光當成直線來研究的一門物理分支。它以光線為基礎,研究光的傳播和成像規律。

    在幾何光學中,把組成物體的物點看作是幾何點,把它所發出的光束看作是無數幾何光線的集合,光線的方向代表光能的傳播方向。

    幾何光學中光線的概念與光的波動性質相違背。因為從能量和光的波動性現象(如衍射)來看,這種幾何光線都是不可能存在的。幾何光學只是波動光學的近似,把所有光當成波長極小的情況處理,小到光線被當成了直線。但是,簡化后的幾何光學可以不涉及光的物理本性,而能以其簡便的方法解決光學儀器中的光學技術問題和計算機圖形渲染的復雜度問題。

    在幾何光學中,特別是在計算機圖形學中,光線處理做了以下簡化或遵循以下基本定律:

    • 直線傳播:光在均勻介質中沿直線傳播。直線意味著將光波長視作極小。
    • 獨立傳播:兩束光在傳播途中相遇時互不干擾,仍按各自的途徑繼續傳播。而當兩束光會聚于同一點時,在該點上的光能量是簡單的相加。
    • 反射和折射:光在傳播途中遇到兩種不同介質的光滑分界面時,一部分光線反射,它們的傳播方向遵循反射定律;另外另一部分折射,它們的傳播方向遵循折射定律。
    • 路徑可逆性:一束光線從一點出發經過無論多少次反射和折射,如在最后遇到與光束成直角的界面反射,光束必然準確地循原路返回出發點。
    • 介質各向同性:將光線傳播的介質簡化成均勻的,即各向同性(isotropic)。所謂各向同性是指介質任意一點的所有方向的物理性質(如密度、彈性、摩擦系數等等)和化學性質是一樣的。
    • 分界面簡化:將光線經過兩個介質的分界面處的點當做平面上的點處理。
    • 單色光源:真實的發光源基本是復合色光源,但在幾何光學中,簡化成了單色光,避開色散、傅里葉積分、能量積分等復雜問題。
    • 麥克斯韋方程的特化:由于很多參數作了特例化和簡化,所以幾何光學的光照計算實際上使用的是麥克斯韋方程簡化和特化后的版本。

    由于PBR的相關技術及諸多理論跟幾何光學相關,所以本節將深入地探討幾何光學的內容。

    4.4.1 反射定律(Law of Reflection)

    反射定律描述了反射光的角度:入射光的角度與反射光的角度相同。

    如上圖所示,入射光線P射在介質點O上,反射光線是Q,點O的法線是normal,則根據反射定律:

    \[\theta_i = \theta_r \]

    即入射角\(\theta_i\)和反射角\(\theta_r\)相同。它們的另外一種等效表達形式:

    \[\theta_r = \pi - \theta_i \]

    需要注意的是,反射定律描述的是反射角問題,并不涉及能量分配。

    反射還涉及到反射率的問題。反射率是反射波的功率與入射波的功率之比。每種材料的反射率不一樣,并且跟入射光與介質的夾角有關,這種現象叫菲涅爾反射效應,與之相關的方程是菲涅爾方程(Fresnel equations)

    上圖:菲涅爾反射效應,球體的反射率從中心到邊緣以某種曲線提升。

    實際上,當光照射到介質表面時,光可能產生的結果:

    • 被吸收:部分光被介質吸收,轉化其它形式的能力。
    • 被折射:部分折射的光如果透過介質進入另外的介質,就會形成透射效果。
    • 被反射:部分光被反射,其中反射分成了鏡面反射和漫反射。

    4.4.2 折射定律(Law of Refraction)

    折射定律也叫斯涅爾定律(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 \]

    4.4.3 幾何光學的其它定律

    在幾何光學中,除了反射定律和折射定律之外,還有以下定律:

    • 拉格朗日積分不變式。
    • 費馬原理。
    • 馬呂斯和杜平定律。

    由于這些定律跟PBR技術關聯不大,本文不詳述,有興趣的可以另外找資料。

    4.5 物質理論

    4.5.1 物質是什么?

    從經典物理學上,物質是任何具有質量并且有體積占據空間的東西。

    從現代物理學上,物質是構成宇宙間一切物體的實物和能量場(光、電場、磁場、聲等),還包括反物質和暗物質。

    從宏觀上,物質是所有看得見摸得著感受得到的東西。

    從微觀上,物質是由原子構成的所有東西,而原子又是由相互作用的亞原子粒子構成。實體粒子包含但不限于:原子、中子、質子、電子、夸克、輕子、重子、費米子等等;能量粒子包含但不限于:光量子、聲波等。

    從哲學上,物質除了客觀實體,還有包含了主觀意識。

    雖然物質的概念和構成非常復雜,但計算機圖形學的PBR領域,只要研究經典物理學的物質和能量場的光波即可。

    4.5.2 物質結構

    物質的構成千姿百態,結構也形態各異。

    從不同的微觀尺寸觀察,物體的結構描述如下:

    • \(10^0m\):1米維度,這個維度就是人類最常接觸也最熟悉的尺度,人體、動物、生物、靜態物體大多數是這個維度。
    • \(10^{-3}m\):1毫米維度,仔細觀察或借助普通的放大鏡,人類還是能清晰看到很多東西,如毛孔、微型動物、微型昆蟲、巨型細菌、血管、毛發、皮膚細節等。
    • \(10^{-6}m\):1微米維度,在此維度下的物質有細菌、病毒、DNA、血小板、紅細胞、白細胞、淋巴細胞等等。
    • \(10^{-9}m\):1納米維度,這個維度已經到達了原子級別,可以分析氫原子、X射線的特性。
    • \(10^{-12}m\):1皮米維度,這個維度已經深入到原子核級別,可描述的原子、質子、中子的尺寸。
    • \(10^{-15}m\):1飛米維度,這個維度只能描述電荷、強子、費米子的尺寸。
    • \(10^{-18}m\):1阿米維度,這個維度只能描述電子、夸克的尺寸。
    • \(10^{-35}m\):1普朗克長度,這個維度只能描述量子泡沫、量子弦。

      不同尺寸維度下的物質構成。從上到下:\(10^{-35}m\)\(10^{27}m\)

    4.5.3 物質形態

    物質的形態(Phase,也叫相態)常見的有:固態、氣態、液態、非晶態、液晶態,另外還有奇特的形態:等離子態、超固態、中子態、超導態、超流態、玻色–愛因斯坦凝聚、費米子凝聚態。

    這些狀態都是物質在不同的密度、溫度、壓強、輻射下的形態,當所處的環境發生改變時,會從一種形態轉成另外一種形態。

    物質在固態、液態、氣態、等離子態的轉化圖。

    4.5.4 物質屬性

    物質的屬性有很多,從不同角度有不同的屬性,但常見的物理和化學屬性有:體積、尺寸、質量、密度、硬度、導電性、導磁性、磁性、范性(可塑性)、透光性(透明度)、比熱容、彈性、可燃性、助燃性、酸堿性等等。

    下面只闡述跟PBR相關的物質屬性。

    4.5.4.1 導電性

    導電性是指物質內載電荷粒子的運動。含有電荷的粒子稱為電荷載子,它們的運動形成了電流

    電流產生的原因有兩種:

    • 受到電場的作用。
    • 電荷載子分布不均勻引發的擴散機制。

    根據導電性,可以將物質分為:

    • 絕緣體:也稱電介質,常見的絕緣體包含干燥的木材、塑料、橡膠、紙張等。
    • 半導體:處于導體和絕緣體之間,半導體的電傳導是由電場作用和擴散這兩種物理機制共同引發。常見的半導體有硅、鍺、砷化鎵等。
    • 導體:導電性強,常見的導體有金屬、電解質、液體等。
    • 超導體:導電性非常強,沒有電阻,通常在絕對零度左右的極限條件下才能出現。

    4.5.4.2 粗糙度

    粗糙度是反映物質微觀表面的輪廓紊亂的程度。

    根據粗糙程度可以將物體分成以下幾類:

    • 光滑物體:粗糙度非常小,幾乎為0,摩擦系數很小;微觀表面輪廓幾乎是一條直線,易產生鏡面反射。
    • 半光滑物體:粗糙度較小,摩擦系數較小;微觀表面輪廓是較平整,產生介于鏡面和漫反射之間的反射。
    • 粗糙物體:粗糙度很大,接近1.0,摩擦系數很大;微觀表面輪廓非常不平坦,產生漫反射。


    有很多估算物體粗糙度的方法:

    • \(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\)

    4.5.4.3 透光性

    透光性亦即透明度,描述物質透過光線的程度。

    物體的透光性主要取決于物質內部結構對外來光子的吸收和散射。

    金屬物質在可見光波段的電子軌道密集(有能帶),能強烈地吸收對應能量的光子并發射(相同能量或較小能量的)光子,即其表面可以強烈地反射光,是不透明的。

    物質內部對光的散射,主要取決于其內部缺陷的多少,缺陷多的物質,散射率高。普通陶瓷材料,其內部充斥著大量的微氣孔等缺陷,氣孔會對經過的光產生強烈的散射,所以普通陶瓷是不透明的;而氣孔率保持極低的陶瓷材料是可以像玻璃一樣透明的。單晶體(如天然水晶)和液體(如水)由于內部排列規則,缺陷極少,是透明的。

    4.5.4.4 各向性

    各向性是描述物質任意一點的物理和化學等屬性跟方向是否相關,如果與方向無關叫各向同性(isortropy),否則叫各向異性(anisortropy)

    比如清澈平靜的水是各向同性,因為水的每個部分的屬性(密度、壓力、溫度、折射率質量等等)都與方向無關;而飄忽不定的煙或霧則是各向異性,很明顯同個部位不同方向有著不一樣的密度或外力。

    在計算機圖形學,特別是實時渲染領域,通常將物體簡化成均勻的,即各向同性的。幾何光學也通常將光線傳播的介質看成各向同性。

    4.6 能量理論

    4.6.1 能量是什么?

    能量是必須轉移到物體以便對物體進行影響或加熱的定量屬性。能量是物質運動轉換的量度,是表征物理系統做功本領的量度。

    能量的單位與功的單位相同,在國際單位制中是焦耳(J);在微觀研究領域,常用電子伏(eV)作為單位。

    4.6.2 能量類型

    能量存在的形式多種多樣,在不同學科不同分支不同角度有著不同的類別,主要有:

    • 機械能:物體運動(平移、旋轉)宏觀的能量(動能、勢能)的總和。
    • 電能:由電場引起或存儲的勢能。
    • 磁能:由磁場引起或存儲的勢能。
    • 重力勢能:由重力場引起或存儲的勢能。
    • 化學能:由化學鍵引起的勢能。
    • 電離能:由電子與其原子或分子結合引起的勢能。
    • 核能:結合原子形成原子核和核反應的勢能。
    • 膠子結合能:結合夸克形成強子的勢能。
    • 彈性能:由于材料(或其容器)的變形而出現恢復力的勢能。
    • 機械波能:由于機械波的傳播引發的彈性材料中的動能和勢能。
    • 聲波能:聲波是機械波的一種。
    • 輻射能:存儲在電磁輻射傳播的場中的勢能,包括光。
    • 靜止能:由物體的靜止質量引發的勢能。
    • 熱能:粒子微觀運動的動能,是一種無序等效的機械能。

    4.6.3 能量轉化

    能量的類型多鐘多樣,而且它們之間是可以相互轉化的。

    能量的轉化在生活中隨處可見。比如,“利用太陽能發的電煮開了水,水蒸氣一直往上冒”,蘊含了很多能量轉化的過程:

    • 太陽內部的核能通過核反應轉變成太陽光的輻射能;
    • 太陽發電站的太陽板吸收輻射能形成了電能;
    • 利用煮水電器將電能轉化成了水的熱能;
    • 水蒸氣向上冒的過程轉化成了重力勢能。

    4.6.4 質能等值

    愛因斯坦在二十世紀初提出了著名的質能轉化公式:

    \[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 \]

    其能量相當于:

    • 250億千瓦時(\(2000GW\cdot h\));
    • 三峽水電站(全球發電量最大)一整個季度的發電量;
    • 21.5萬億千卡(\(21Pcal\));
    • 21萬噸TNT;
    • 26.3億升汽油。

    由此可見,物質轉化成能量后,是非常恐怖的。一塊小石頭,足以毀掉一個小星球。

    幸好,目前尚沒有很便捷地將物質轉化成能量的方法。然而,雖然核爆(原子彈、中子彈、氫彈)不是利用質能轉化定理,但是核能表現出的威力已經足夠令人望而生畏了。

    4.6.5 能量守恒(Conservation of energy)

    傳統物理學上,能量守恒定律表明在封閉的系統中,能量不會憑空出現或消失,只會從一種形式轉成另外一種形式,能量的總量保持不變。

    現代物理學上,由于質量和能量可以相互轉化,能量守恒擴展到能量和質量的總和保持不變。

    諾埃德定理(Noether's theorem)可以嚴格證明能量守恒,它也表明了永動機是不可行的。

    比如,上圖所示,光線照到物質表面上時,光能可能一部分被鏡面反射(黃色),一部分被散射(深藍色),一部分被吸收(褐色),還有一部分被透射(圖中未標識)。與之對應的能量分別是反射部分依舊是光能,吸收部分轉化成熱能、電能等形式,透射部分依舊還是光能,并且:

    \[E_{in} = E_{specular} + E_{diffuse} + E_{absorb} + E_{transmit} \]

    亦即入射光能與反射(包含鏡面反射和散射)、吸收、透射部分的總光能相等,遵循了能量守恒定律。

    PBR的BRDF也遵循了這一守恒定律,引入粗糙度、反射率等概念,使得原理上更加物理正確,渲染效果上更加真實。

    4.7 PBR與光學

    本節將闡述本章前幾節涉及的光學理論與PBR結合的理論,特別是物質和光的交互原理及理論。主要參考了Naty Hoffman在2013~2015年(特別是2015年)的SIGGRAPH公開課中演講的主題:《Physics and Math of Shading》

    4.7.1 光譜能量分布(SPD)

    回顧一下[4.2.5.3 光的電磁理論(Electromagnetic theory)](#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 人眼感知可見光原理](#4.2.3 人眼感知可見光原理))匹配,所以人類并沒有發現它們之間的差別。也就是說,人類的視覺是有損的,將無限維度的SPD簡化成了三維視覺空間。

    4.7.2 物質與光交互

    在納米級別,當一束電磁光波和原子或分子相遇后,會發生什么呢?

    答案是,光波會引發原子、分子偏振,并且使它們的正負電荷分離,形成偶極子(dipole)。這就意味著進入的光波被吸收了。

    吸收了能量并且極化后的原子、分子會迅速恢復,重新向外輻射,形成二次光波。當然被吸收的部分能量可能轉化成了熱運動,即熱能。

    對均勻介質(Homogeneous Medium)來說,光線是沿著直線傳播。由于所有物質在原子維度上看是不可能完全均勻的,所以均勻介質是在實踐中抽象出來的概念。

    在渲染技術中,會使用宏觀的統計和組合為材質提供參數。這個參數就是折射率(Index of refraction,IOR),它由兩個部分組成:

    • 一部分描述光在介質中的速率。
    • 另外一部分描述光在介質被吸收的比例。

    對于介質中局部不均勻的部分將被建模成粒子(原子核等),折射率不連續的物質會散射入射光,散射出的光方向向著四面八方。

    雖然這跟前面討論的單一分子或原子的極化類似,但可以將這些微粒組合起來,形成宏觀模型。

    下圖是吸收系數和散射系數組合成的宏觀維度的材質表現圖。

    橫軸散射系數,從左到右可以看出物質從清澈到混濁的程度;縱軸吸收系數,從下到上表示物質從透明(光被全透射而表現出跟入射光幾乎一樣的顏色)到不透明(光被全吸收而表現出與入射光不一樣的顏色)的程度。比如牛奶,它是吸收系數低而散射系數高的物質,所以它表現出入射光一致的白色,并且是混濁的。有色液體能很輕易吸某些波段的光,而其它波段的就沒那么容易。

    從光學角度上看,最重要的事情是所有材質的表面都是粗糙的。沒有表面是完全平坦的,至少在原子維度上是不規則的。原子間排布距離如果跟光波差不多或更小,就會引起一種現象,它就是之前章節涉及過的衍射

    惠根斯-菲涅爾原理(Huygens-Fresnel Principle)可以解釋這個現象。當光波遇到跟它波長相仿的障礙時,會“繞彎”傳播,繞到了障礙后面:

    在納米維度,當光波傳播到光學平坦的表面時,惠根斯-菲涅爾原理同樣適用。當與入射光碰撞后,每個粒子都會發射球面波,有些強有些弱。這些不平行的球面波結合起來,就會形成復雜的波形,在很多方向發射不一樣的光:

    納米幾何體(Namogeometry)越小,越少光波被衍射。入射光與單個原子碰撞后,少部分會被衍射。

    現在聊回幾何和射線光學,它們都是更簡化的并且廣泛應用于計算機圖形領域。其中一種簡化方法是忽略納米級別的不規則和衍射,將光學平坦的表面當成完全平坦。由此可以應用幾何光學的反射和折射定律。

    很多材質表面看起來是平滑的,但實際并非如此,對于微觀幾何體(Microgeometry),它們一樣凹凸不平:

    上圖的上部分由于物體更加平滑,所以在微觀的表面看起來更加規則,反射的光也相對規則,宏觀表現就是被反射的光更加清晰,反射的畫面更容易表達出被反射物體的輪廓;而下部分由于微觀更加不規則,反射的光線取向更紊亂,所以宏觀表現就是高光變模糊了,被反射的物體看不清楚。

    對于折射進入介質的光,會發生什么呢?

    對這個問題,需要對物質的導電性進行分類,然后分開探討:

    • 導體(Conductor):導體是金屬、電解質等導電性強的物質,由于它們的微粒組合特性,會立即吸收折射光形成熱運動。

    • 絕緣體(Dielectric):即電介質,指沒有導電性的物質。如前面章節描述的一樣,折射光進入除了透明的電介質后,小部分被吸收,相當一部分在介質內部被多次散射并重新射出表面形成漫反射。

      重新被散射出介質表面的光線形成了不同的散射距離。散射距離的分布取決于散射微粒的密度和屬性。

      如下圖所示,如果像素尺寸(綠色標識區域)大于散射距離(黃色線段),就可以無視次表面散射效果。

      由于忽略次表面散射的效果,所以入射光附近的區域可以當成一個點來處理(下圖),采用經典的關照計算方式,比如Lambert或Phong光照模型。

      當成單點處理后,便可以將光照分成兩個部分:鏡面反射和漫反射(包含了折射、吸收、散射和重新折射回表面的光),見下圖:

      對于下圖中所示的,散射范圍(黃色線段)大于像素尺寸(綠色小圓區域),就不能采用上面的簡化模型,而需要采取次表面散射(Subsurface scattering)渲染技術。

    • 半導體(Semiconductor):由于半導體與光交互的特性介于金屬和非金屬之間,在實際渲染中,常用迪尼斯原則的金屬度系數來模擬半導體特性。

    4.7.3 BxDF

    上節講述了物質和光的不同情況的交互原理,本節將講述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核心原理相關的理論,并對其中一些公式做推導或詳細闡述,使讀者對PBR的關聯技術和原理有更徹底的理解。

    主要面向:

    • 高階程序員
    • 想全面且透徹了解PBR底層原理及理論的人

    數學物理( Mathematical Physics)是一門數學和物理相結合的學科,意圖將物理現象和理論用數學公式或理論表達出來。本章主要是跟數學物理相關的內容。

    5.1 微積分(Calculus)

    由于微積分可以解決很多物理學上的現象或問題,比如光照輻射度量、電磁場、量場等等,所以很有必要重溫一下微積分的基礎知識。

    微積分是數學的一門基礎學科,是高等數學中研究函數的一個分支。它包含的主要內容有:

    • 微分學(Differentiation):它是一套描述函數變化率的理論及求導數的運算,將函數、速度、加速度和曲線斜率歸集起來,以便用一套統一通用的符號進行討論和計算。包含的內容:
      • 極限理論(Limit Theory)
      • 導數(Derivative)
      • 微分(Differentiation)
    • 積分學(Integration):為定義和計算面積、體積等提供一套通用的方法。包含的內容:
      • 定積分(Definite integration)
      • 不定積分(Indefinite integration)

    5.1.1 微分(Differentiation)

    5.1.1.1 函數極限(Functional Limit)

    • 極限的概念:設\(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)}\)\(\lim{g(x)}\)均存在,\(C\)為常數):

      • \(\lim C = C\)

      • \(\lim_{x \to a}x = a\)

      • \(\lim Cf(x) = Cf(x)\)

      • \(\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\frac{f(x)}{ g(x)} = \frac{\lim f(x)}{ \lim g(x)}\)\(\lim g(x) \ne 0\)

      • \(\lim_{x \to a}f(x) = A \iff f(a+0) = f(a-0) = A\)(極限與左、右極限的關系)

    • 重要極限

      • 重要極限1,跟三角函數相關:

      \[\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 \]

      ? 利用這一重要極限,可以求得一系列涉及三角函數的極限。

      • 重要極限2,跟自然常數\(e\)相關,有兩種表達形式:

      \[\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) \\ \]

      \end{eqnarray*}

      \[ \]

    5.1.1.2 導數(Derivative)

    • 導數的定義

      \(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*} \]

    • 求導步驟

      1. 對給定的\(\triangle x\),求出\(\triangle y = f(x_0 + \triangle x)-f(x_0)\)
      2. 計算\(\frac{\triangle y}{\triangle x}\)并化簡。
      3. \(\lim_{\triangle x\to 0}\frac{\triangle y}{\triangle x}\)

      比如,求\(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'''\)

      二階及以上的導數統稱為高階導數

    5.1.1.3 微分(Differentiation)

    • 微分的定義

      設函數\(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 \]

      此性質就是一階微分形式不變性

    5.1.2 積分(Integration)

    5.1.2.1 不定積分(Indefinite integration)

    • 原函數:如果在區間\(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 0 dx = C\)

      • \(\int x^n dx = \frac{1}{n+1}x^{n+1} + C \; (n \ne -1)\)

      • $\int \frac{1}{x}dx = \ln |x| + C $

      • \(\int \frac{1}{1+x^2}dx = arctan x + C\)

      • \(\int \frac{1}{\sqrt{1-x^2}}dx = \arcsin x + C\)

      • \(\int \sin x dx = -\cos x + C\)

      • \(\int \cos x dx = \sin x + C\)

      • \(\int \sec^2 x dx = \ tan x + C\)

      • \(\int \csc^2 xdx = -\cot x + C\)

      • \(\int \tan x \sec x dx = \sec x + C\)

      • \(\int \cot x \csc x dx = -\csc x + C\)

      • \(\int e^x dx = e^x + C\)

      • \(\int a^x dx = \frac{1}{\ln a} a^x + C\)

      • \(\int \tan x dx = - \ln |\cos x| + C\)

      • \(\int \cot x dx = \ln|\sin x| + C\)

      • \(\int \sec x dx = \ln |\sec x + \tan x| + C\)

      • \(\int \csc x dx = \ln |\csc x - \cot x| + C\)

      • \(\int \frac{dx}{a^2 + x^2} = \frac{1}{a} \arctan|\frac{x}{a} |+ C\)

      • \(\int \frac{dx}{\sqrt{a^2 - x^2}} = \arcsin\frac{x}{a} + C\)

      • $\int \frac{dx}{x^2 - a^2} = \frac{1}{2a}\ln\big|\frac{x-a}{x+a}\big| + C $

      • \(\int \frac{dx}{\sqrt{x^2 - a^2}} = \ln|x+\sqrt{x^2-a^2}| + C\)

      • \(\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 \]

        若是分部的一部分有困難時,可以嘗試另外一部分可能相對容易,這就是分部積分法的作用。

    5.1.2.2 定積分(Definite integration)

    定積分是積分學的另一個重要概念,自然科學與生產實踐中的許多問題,如平面圖形的面積、曲線的弧長、水壓力、變力所做的功等都可以歸結為定積分問題。

    計算機圖形學的很多計算問題也歸結于定積分問題,如輻射度量、采樣、卷積、預計算等等。

    • 定積分的定義

      \(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]\)上的增量,它為定積分的計算提供了一個簡單而有效的方法。

    • 定積分的方法

      換元法分部積分法,跟不定積分類似,唯一的區別是加了區間限制,不再累述。

    微積分就介紹到這里了,其它更多高級的概念和性質,如多元微積分、多重微積分、無窮級數、冪級數等等,可以另外找資料,也可以在參考文獻里尋得。

    5.2 輻射度量(Radiometry,Radiation Measure)

    章節[3.1.3 反射方程](#3.1.3 反射方程(Reflectance Equation))和[4.2.6 光的能量](#4.2.6 光的能量)已經列出了輻射度量的基本概念、符合、公式,本小節將做一些補充。

    5.2.1 立體角(Solid Angle)

    二維平面幾何中,弧度(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 \]

    5.2.2 輻射強度(Radiant Intensity)

    輻射強度指通過單位立體角的輻射通量。用符號\(I\)表示,單位W\(/sr\),微分公式:

    \[I = \frac{d\Phi}{d\omega} \]

    既然已有了輻照度和輻射度,為什么還要引入輻射強度呢?

    原因是在計算輻射時,有時會考慮某個點的通量的密度,但一個點的面積是0,無法用輻照度和輻射度的公式,故而引入跟面積無關的輻射強度。而輻射強度之所以跟面積無關,是因為立體角只跟角度相關,跟球體的半徑、距離、面積無關。

    也就是說,由于立體角不會隨距離變化而變化,輻射強度不會隨距離變化而變化,不像點光源的輻照度會隨距離增大而衰減。

    5.2.3 輻射率(Radiance)

    輻射率是測量微小方向照到微小表面的通量,即每單位面積每單位立體角的輻射通量密度。用公式表達:

    \[L = \frac{d\Phi}{d\omega dA^{\bot}} \]

    輻射率實際上就是材質的顏色,在基于物理著色時,計算表面一點的顏色就是計算它的輻射率。

    輻射率不會隨距離變化而衰減,這和真實世界的物理原理一致:在沒有空氣干擾的情況下,我們看到的物體顏色并不會隨距離變化而變化。

    5.3 公式推導

    5.3.1 麥克斯韋方程組(Maxwell's equations)

    既然光的很多現象,包括反射、折射定律都可以用麥克斯韋方程組解釋,那我們就有必要揭開它的神秘面紗。

    麥克斯韋方程組是描述了電場、磁場與電荷密度、電流密度之間關系的偏微分方程。利用麥克斯韋方程組,可以推論出電磁波在真空中以光速傳播,并進而做出光是電磁波的猜想。

    實際上,麥克斯韋方程組雖然由英國物理數學家詹姆斯·克拉克·麥克斯韋(James Clerk Maxwell)提出,但最初提出來時有20個方程和20個變量。后來由英國物理學家奧利弗·赫維賽德(Oliver Heaviside)和美國物理數學家約西亞·威拉德·吉布斯(Josiah Willard Gibbs)以矢量分析的形式重新表達,也就是現在我們使用的形式。

    麥克斯韋方程組和洛倫茲力方程是經典電磁學的基礎方程。從這些基礎方程的相關理論,發展出現代的電力科技與電子科技。

    它由4個方程組成:

    • 高斯定律(Gauss' law):描述電場與空間中電荷分布的關系。
    • 高斯磁定律(Gauss's law for magnetism):表明磁場的散度等于零,也就是說進入任何區域的磁場線,必需從該區域離開。
    • 法拉第定律(Faraday's law):描述時變磁場怎樣感應出電場。
    • 麥克斯韋-安培定律(Ampère's law with Maxwell's addition):闡明磁場的產生方式有兩種:一種是靠傳導電流;另一種是靠時變電場,或稱位移電流。

    5.3.1.1 高斯定律(Gauss' law)

    高斯定律表明在靜電場中,穿過任一封閉曲面的電場通量只與封閉曲面內的電荷的代數和有關,且等于封閉曲面的電荷的代數和除以真空中的電容率。用積分形式表達的公式:

    \[\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\)表示電場。此公式只適應與均勻、各向同性、非分散的線性物質。

    5.3.1.2 高斯磁定律(Gauss's law for magnetism)

    高斯磁定律表明磁場的散度等于零,因此磁場是一個螺線矢量場,還可以推斷磁單極子不存在(下圖)。磁的基本實體是磁偶極子,而不是磁荷。

    用微分形式的公式:

    \[\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 \]

    5.3.1.3 法拉第定律(Faraday's law)

    法拉第定律描述時變磁場怎樣感應出電場。在積分形式,它表明在閉環移動電荷所需的每單位電荷的工作量等于通過封閉表面的磁通量的減少速率。微分形式:

    \[\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 \]

    5.3.1.4 麥克斯韋-安培定律(Ampère's law with Maxwell's addition)

    麥克斯韋-安培定律表明,磁場的產生有兩種:

    • 靠傳導電流,也就是原本的安培定律;
    • 靠時變電場,也被稱作位移電流,這點是麥克斯韋修正項所提出。

    微分形式的公式:

    \[\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\)無法表示閉合曲面的符號,可能跟維基百科的有些出入,具體參看維基百科的麥克斯韋方程組

    5.3.2 幾何光學基本定律的推導

    幾何光學有三條基本定律:

    • 第一定律:入射波、反射波、折射波的波矢,與界面的法線共同包含于入射平面(下圖)。
    • 第二定律:反射角等于入射角。這定律稱為反射定律
    • 第三定律:\(n_{1}\sin \theta _{1}=n_{2}\sin \theta _{2}\),也叫斯涅爾定律折射定律

    它們可以由麥克斯韋方程組推導出來。

    光波是電磁輻射,必須滿足麥克斯韋方程組與伴隨的邊界條件,其中一條邊界條件為,在邊界的臨近區域,電場平行于邊界的分量必須具有連續性。假設邊界為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

    5.3.3 Cook-Torrance BRDF推導

    本節參考了基于物理著色: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} \]

    這里需要特別強調幾個夾角:

    • \(\theta_h\)是入射光線\(\omega_i\)與朝向為\(\omega_h\)的微表面法線的夾角
    • \(\theta_i\)是入射光線\(\omega_i\)與宏觀表面法線的夾角
    • \(\theta_o\)是反射光線\(\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} \]

    5.4 預計算技術

    在第三章闡述PBR的Cook-Torrance原理和實現的時候,提及過很多預渲染技術,諸如:Cubemap、HDR環境光等。本章節主要是講解這些預計算或預卷積的技術,為將耗時的部分提前渲染,以便減輕實時光照時的渲染消耗。

    5.4.1 立方體圖卷積(Cubemap convolution)

    立方體圖卷積是以離線的方式預先為場景的輻照度求解所有漫反射間接光照的積分。為了解決積分問題,必須對每個片元在半球\(\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 從球體圖到立方體圖](#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)是為了彌補較高仰角的半球區域中面積較小的樣本區域。

    5.4.2 預過濾HDR環境圖(Pre-filtering HDR environment map)

    預過濾環境圖與預卷積輻照圖非常相似。不同之處在于,需要考慮粗糙度并在預過濾環境圖的不同mip級別中按順序地存儲更粗糙的反射。

    通過使用球面坐標生成均勻分布在半球\(\Omega\)上的樣本向量來對環境貼圖進行復雜處理的方法,雖然這個方法適用于輻照度,但對于鏡面反射效果較差。當涉及鏡面反射時,基于表面的粗糙度,光在通過法線\(n\)附近的反射就越粗糙,范圍越大:

    光線反射后所有可能的出射光形成的形狀被稱為鏡面波瓣。隨著粗糙度的增加,鏡面波瓣的大小增加; 并且鏡面波瓣的形狀在變化的入射光方向上變化。因此,鏡面波瓣高度取決于材質。

    當談到微表面模型時,可以將鏡面波瓣想象為給定一些入射光方向的微平面中間向量的反射方向。當看到的大多數光線最終反射在微平面中間矢量周圍的鏡面波瓣中,這樣的方法生成的樣本向量才是有意義的,這個處理過程就是重要性采樣(Importance sampling)

    5.4.2.1 蒙特卡洛(Monte Carlo)積分和重要性采樣(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)。當涉及光的鏡面反射時,反射光向量被約束在鏡面波瓣中,其尺寸由表面的粗糙度決定。看到鏡面外的任何(準)隨機生成的樣本與鏡面積分無關,將樣本生成集中在鏡面波瓣內是有意義的,代價是蒙特卡羅估計有偏差。

    重要性采樣是這樣的:在一些區域內生成樣本向量,該區域受到圍繞微平面中間向量的粗糙度的約束。通過將準蒙特卡羅采樣與低差異序列相結合并使用重要性采樣偏置采樣向量,可以獲得高收斂率。因為以更快的速度到達解決方案,所以只需要更少的樣本來達到足夠的近似值。因此,該組合甚至允許圖形應用程序實時解決鏡面反射積分,盡管它仍然比預先計算結果慢得多。

    5.4.2.2 低差異序列(Low-discrepancy sequence)

    這里,將通過基于準蒙特卡羅方法的隨機低差異序列,使用重要性采樣預先計算間接反射方程的鏡面反射部分。本小節使用的序列稱為哈默斯利序列(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

    5.4.2.3 GGX重要性采樣(GGX Importance sampling)

    我們將基于表面粗糙度生成偏向于微表面中間矢量的一般反射方向的樣本矢量來取代統一或隨機(蒙特卡羅)地在積分半球\(\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)](#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.01.0)中變化,并將結果存儲在prefilteredColor中。得到的預過濾顏色除以總樣品權重,其中對最終結果影響較小的樣品(對于小NdotL)對最終重量的權重較小。

    5.4.2.3 預過濾卷積瑕疵

    雖然上述的預過濾圖在大多數情況下都能正常,但總會遇到一些瑕疵。下面列出最常見的,包括如何解決它們。

    • Cubemap高粗糙度的接縫

    在具有粗糙表面的表面上對預濾鏡圖進行采樣意味著在其一些較低的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);
    

    這種效果非常好,并且可以在粗糙表面上的預過濾圖中刪除大多數點。

    5.4.3 預計算BRDF

    在預過濾環境啟動和運行的情況下,我們可以關注分裂和近似的第二部分: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卷積的輸入(NdotVroughness)。卷積代碼很大程度上類似于預過濾卷積,不同之處在于它現在根據我們的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.5 PBR的優化

    5.5.1 離線渲染優化

    [5.4 預計算技術](#5.4 預計算技術)章節提到了一些離線渲染的加速技術,除此之外,常見的離線技術還有:

    • 局部靜態光照烘焙
    • 全局光照烘焙

    還可以從以下小節中闡述的方法加速離線渲染部分。

    5.5.1.1 積分公式優化

    主要是利用[5.1 微積分(Calculus)](#5.1 微積分(Calculus))描述的性質和定理對渲染公式進行優化:

    • 常量移出積分項外
    • 增加等效積分項
    • 分離積分項
    • 利用近似法替代復雜項

    具體例子可以參看[5.4 預計算技術](#5.4 預計算技術)。

    5.5.1.2 硬件集成

    將渲染通用的邏輯集成硬件指令或內建接口,可以充分利用硬件的性能,從而為渲染加速。

    例如,將光線追蹤算法集成進GPU顯卡,而nVidia新一代RTX20系顯卡已經集成了光線追蹤技術,使得渲染效率更上一層樓。Unreal Engine 4.22的版本也集成了這一特性。

    5.5.1.3 并行渲染

    通過多線程、多進程、多設備的架構分攤消耗的幀渲染,使得每幀的渲染時間大大降低。這種技術在實時渲染領域也逐漸被普及。

    5.5.1.4 分布式渲染

    不同于并行渲染的小規模架構,分布式渲染通常以圖形工作站、集群式渲染簇等中大型硬件架構為依托,以滿足電影級別的離線渲染加速需求。

    下圖是《A MultiAgent System for Physically based Rendering Optimization》提出的一種多代理的加速渲染架構:

    5.5.2 實時渲染優化

    5.5.2.1 光照模型優化

    • GGX倫勃朗光照計算
    • Schlick的\(F_0\)近似法
    • Smith幾何遮蔽函數混合
    • 迪斯尼原則的金屬度線性插值

    以上都是本文前面章節描述過的加速算法,這對于性能敏感的實時渲染領域是非常有必要的。

    5.5.2.2 資源優化

    • 若干貼圖合成一張蒙板圖。將若干獨立的PBR屬性蒙板貼圖合成一張:

      使用同一張蒙板貼圖同時控制PBR的顏色、金屬度、粗糙度等屬性。

    • 減少PBR標準參數的使用。例如,金屬材質的漫反射大部分是黑色,所以無需額外的漫反射貼圖。

    • 其它資源優化:材質、模型、渲染參數、紋理、PBR參數等等幾乎都有優化的余地。

    5.5.2.3 其它實時優化

    實時渲染領域還有很多優化方法值得嘗試和應用,比如:

    5.5.3 移動端優化

    由于移動設備普遍的性能與PC機有一定的差距,所以要將PBR應用到移動端,性能優化的需求更加迫切。

    上一小節提到的實時渲染優化同樣適用于移動端,此外,還可針對移動端做一些特殊的優化:

    • 簡化光照模型。采用更少的樣本采樣數量,更簡化的光照計算公式。
    • 簡化shader。通過少量的shader指令或簡化的數學運算可達到優化的目的。
    • 啟用引擎Mobile版本的資源和設置。Unity和Unreal Engine都提供了移動版本的材質庫和特殊的配置,在無特別需求下,盡量使用它們。
    • 分級策略。針對不同分級的設備啟用不同復雜度的材質和資源,可以有效解決高中低畫質的兼容問題。

    更多請參看《Optimizing PBR》,還可參看筆者的另外一篇原創技術文章:《移動游戲性能優化通用技法》

    5.6 PBR的未來

    當今階段,由于硬件、技術、理論等種種原因的限制,PBR技術在很多時候只能是采取近似模擬的方法,特別是在實時渲染領域,甚至在很多中低端PC或移動端設備還無法運行PBR技術。

    但是,這不妨礙我們想象PBR技術未來的趨勢和前景。

    5.6.1 基于納米級別原理

    目前大多數PBR都是基于微平面(microfacet)的光照模型,micro即微米(\(10^{-6}m\)),并且將反射模型簡化成了幾何光學,忽略了衍射、干擾、色散、光譜能量分布等等精確物理模型。

    近兩年,有人提出了基于納米(nano,\(10^{-9}m\))級別的光照模型,可以先看看它和微米級別的區別:

    微米幾何(Microgeometry) 納米幾何(Nanogeometry)
    波瓣形狀取決于表面統計(微米級別的NDF) 波瓣形狀取決于表面統計(納米級別的光譜能量分布)
    忽略光波波長 強依賴于光波波長
    入射角通過可見性概率影響表面 入射角通過透視收縮(foreshortening)概率影響表面

    也就是說納米級別理論引入了SPD、光波波長,先進的表面統計,使得光照渲染更加物理正確真實了。

    納米幾何將引入光波長、SPD等,會考慮光的衍射、干擾、色散等現象,顯得更加物理真實。

    當然,這種技術目前只能用于電影級別的離線渲染,未來還有很長一段時間才能進入實時渲染領域的視野。

    5.6.2 更精確的光照模型

    當前的PBR光照模型,包括Cook-Torrance及BSSRDF,大多是基于一維的曲線擬合。

    從上圖可以看出,由于是一維曲線,所以它們都是從中心向周邊散開的圓形形狀,只是圓的過渡稍有不同。

    若是引入考慮光波波長的納米級別的SPD(光譜能量分布),則可以引入更加復雜的二維光照模型曲線圖:

    上圖可以看出,光的分布曲線不再是圓形形狀,而是變成復雜的類似棉花絮狀的二維圖。這種才是更接近真實世界的光照曲線擬合。

    雖然目前這種技術開銷非常昂貴,但相信未來不久,這種技術會逐漸成為主流。

    5.6.3 離線技術實時化

    [5.4 預計算技術](#5.4 預計算技術)中提到了很多預渲染、預卷積技術,這些都是為了減輕實時渲染的負擔。而且實時渲染部分采用大量近似、簡化的手段,使得光照不那么物理正確和真實。

    未來若干年,離線渲染技術將會逐漸引入到實時渲染領域,使得實時渲染能夠獲得更加真實的渲染效果。

    這里所指的離線技術包括但不限于:光照追蹤、路徑追蹤、全局光照、環境光、局部靜態光、烘焙光。

    若是能將這些技術引入到實時渲染領域,那將是振奮人心的。近期發布的Unreal Engine 4.22的版本已經支持實時光線追蹤技術:

    短片《Troll》展現虛幻引擎4.22的全新光線追蹤功能,能夠實時渲染電影級別的畫質。

    相信這只是開始,未來離線技術實時化的步伐將會越來越快。

    5.6.4 新興理論和技術

    近年來圖形學的技術蓬勃發展,圍繞著PBR為中心的新興技術和理論百花齊放,相信未來也是如此,而且新的理論和技術會成倍加速發展。

    這些新興技術涵蓋了基礎物理學、光學原理、數學模型、算法和數據結構、計算機語言、渲染技術、圖形API、GPU架構等等軟件和硬件領域。

    5.6.5 更多應用領域

    未來隨著基礎理論、軟件和硬件的發展,PBR的發展迅猛,將會得到更廣泛地應用。橫向維度將涵蓋各行各業,縱向維度覆蓋高中端層次的設備。

    例如,沉浸式4D影院,虛擬與現實混合的游戲和教學互動(下圖),投影真實人像的幻影會議,電影畫質的移動端游戲,甚至是電影畫質的街機游戲。

    雖然目前看還有一段差距,但相信在不久的未來,借助PBR技術,這些預言將或多或少地呈現在大家面前。

    總之,PBR技術在未來的發展和前景非常值得期待。

    六. 后記

    6.1 輔助工具

    一些輔助工具可以提升我們在工作或者研發新技術的效率。

    利用數學曲線擬合工具,可以直觀地看到函數在參數具體化后的結果,并協助我們得到想要的數據。

    利用曲線擬合工具直觀地展現數據曲線變化。

    6.1.1 曲線工具

    • MathLab:MathLab是老牌數學分析和建模的工具,功能強大,用戶受眾廣,是理工科必備的軟件之一。同樣地可以用于圖形渲染的曲線擬合。

    • Mathematica:跟MathLab類似,功能雖然不如MathLab多,但更輕巧,各有側重,非常適合曲線擬合。

    • Excel:你沒看錯,Excel同樣可以用于簡單曲線的擬合,參考文獻里有不少圖就出自Excel。

    6.1.2 作圖工具

    • GeoGebra:功能強大,基本可以畫出我們所需的圖例,也可用以曲線擬合。它還有在線版

    • 幾何畫板:老牌數學繪圖軟件,數學教師的必備工具。

    6.1.3 寫作工具

    • Markdown:一種無需我們關注格式的標準語法,可用簡單的標記即可表達復雜多變的圖文混合編排,使得寫作者可以從繁瑣的格式編排中抽身出來,更加專注于內容創作。本文就是基于Markdown格式撰寫。
    • \(L^AT_EX\):其多變復雜的編排可以滿足各種變態的混排需求,特別適合用于數學公式的格式表達。本文的公式都是基于\(L^AT_EX\)實現的。
    • Typora:一種支持Markdown編寫的文本IDE,小巧、靈活、功能較全,支持巨量文字撰寫,性能較好,值得推薦。

    6.1.4 BRDF Explorer

    迪斯尼原則發布的時候,隨同發布了BRDF Explorer,用于查看BRDF的各種公式參數和材質屬性。

    6.2 更多資料

    什么?看完本文還覺得不過癮?

    那么,參考文獻的眾多資料等著你發掘。

    騷年,走你~~

    特別說明

    • 大部分圖片來自參考文獻及網絡,侵刪。

    • 感謝并致敬所有參考文獻的作者。

    • 由于時間倉促、水平有限,紕漏在所難免,懇請指正。

    • 歡迎分享本文鏈接,但未經允許,禁止轉載

    參考文獻

    posted @ 2019-04-25 11:11  0向往0  閱讀(67348)  評論(22編輯  收藏  舉報
    国产美女a做受大片观看