PBRT 阅读 第一章
分享一下我老師大神的人工智能教程!零基礎(chǔ),通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉(zhuǎn)載本篇文章。分享知識(shí),造福人民,實(shí)現(xiàn)我們中華民族偉大復(fù)興!
參見(jiàn)http://www.opengpu.org/forum.php?mod=forumdisplay&fid=8
【題外話:這里只是記錄我學(xué)習(xí)該書(shū)的體會(huì),既不是直譯,也不是意譯,而是順著作者的思路,把自己理解的東西再講述出來(lái),對(duì)于可能出現(xiàn)的誤解會(huì)用原文說(shuō)明。】
第一章介紹
關(guān)于渲染,有很多方式。大致有三類:
基于物理學(xué)的渲染(Physically based):著力于模擬現(xiàn)實(shí)。就是說(shuō),用物理學(xué)的原理搭建關(guān)于光和物質(zhì)交互的模型,追求真實(shí)感是該類方法的首要任務(wù)。
非真實(shí)感的渲染(Nonphotorealistc)。這是為藝術(shù)的自由表達(dá)而作的渲染。
該書(shū)所描述的pbrt是基于光線追蹤算法的物理學(xué)渲染系統(tǒng)。其它相關(guān)的書(shū)籍只是介紹原理,算法,或許還夾雜些少許源代碼。該書(shū)則不同,因?yàn)樗鼛Я艘粋€(gè)完全能工作的完備的渲染系統(tǒng)。(正是這個(gè)原因,有很多人用這個(gè)系統(tǒng)為藍(lán)本作研究,甚至有LexRender這樣相當(dāng)高級(jí)的系統(tǒng)出現(xiàn))。
1.1?文學(xué)編程(Literate Programming)
【該書(shū)開(kāi)篇講了文學(xué)編程,這是本書(shū)的組織方法,其理念和用法貫穿全書(shū),故不得不學(xué)。文學(xué)編程是軟件老泰Donald Knuth(老泰:老泰斗之縮寫(xiě),他是誰(shuí)就不用說(shuō)了吧)的創(chuàng)造。該書(shū)作者M(jìn)att和Greg想必是他老人家的忠實(shí)信徒,也來(lái)一把“文學(xué)編程”.】
Knuth老泰寫(xiě)Tex系統(tǒng)的時(shí)候,闡述了一個(gè)簡(jiǎn)單而具革命性的思想:程序更應(yīng)該寫(xiě)給人讀的,而不僅僅是給計(jì)算機(jī)的,名之為文學(xué)編程。該書(shū)就被作者號(hào)稱為一部長(zhǎng)長(zhǎng)的文學(xué)程序(Literate program)。文學(xué)程序是用一種元語(yǔ)言(metalanguage)寫(xiě)成,該元語(yǔ)言把一種文檔格式化語(yǔ)言(document?formatting language,?例如TeX, HTML)和一種編程語(yǔ)言(例如C++)混合使用。它提供兩種功能:1)把文章跟源程序混在一起,使得對(duì)程序的描述跟實(shí)際的源代碼一樣重要,這樣可鼓勵(lì)仔細(xì)的設(shè)計(jì)和文檔編寫(xiě)。2)跟提交給編譯器的方式相比,它提供給程序讀者全然不同的展現(xiàn)方式,這樣使得程序的描述邏緝性很強(qiáng)。每段代碼都加以名字,稱為片斷(fragment).每個(gè)片斷可以用名字引用其它片斷。
舉例說(shuō)明:有下面一段程序:
void InitGlobals(void) {
? ?? ???num_marbles = 25.7;
? ?? ???shoe_size = 13;
? ?? ???dialectric = true;
? ?? ???my_senator = REPUBLICAN;
}
如果沒(méi)有上下文的話,它很是費(fèi)解。你得搜索整個(gè)程序來(lái)查看每個(gè)變量的定義和它們的目的。這種結(jié)構(gòu)對(duì)編譯器沒(méi)有任何問(wèn)題,而對(duì)讀者而言,讀者更希望看到每個(gè)變量的初始化代碼能在靠近聲明和使用它的地方單獨(dú)表達(dá)出來(lái)。(有點(diǎn)繞口,原文:a human reader would much rather see the initialization code for each variable presented separately,?near the code that actually declares and uses the variable).
在文學(xué)程序中,可以這樣寫(xiě):
<Function Definitions>=
? ?? ???void InitGlobals() {
? ?? ?? ?? ? <Initialize Global Variables??3>
}
(“3”是書(shū)上的頁(yè)碼)
這就是一個(gè)片斷,?名字是<Function Definitions>,?它定義了InitGlobals()函數(shù),并引用在第3頁(yè)的另一個(gè)片斷<Initialize?Global Variables〉。
當(dāng)我們引入全局變量shoe_size時(shí),我們可以寫(xiě):
<Initialize Global Variables〉=
? ?? ???shoe_size = 13;
當(dāng)我們?cè)僖肴肿兞縟irectric時(shí),我們可以寫(xiě):
<Initialize Global Variables〉?+=
? ?? ???dialectric = true;符號(hào)?
+=(包括上面的“=”,原書(shū)是三條橫線,因無(wú)法輸入,用“=”代替)表示我們要對(duì)片斷添加新的聲明。
可以看出,我們可以把很復(fù)雜的函數(shù)化解成不同的邏輯部分,每一部分都很容易理解。(整部書(shū)都是按照這個(gè)步調(diào)有條不紊地,由簡(jiǎn)入繁地解釋書(shū)中個(gè)個(gè)要點(diǎn))。
1.3 pbrt:?系統(tǒng)概述 pbrt用的是插件式架構(gòu)。pbrt執(zhí)行文件包含了系統(tǒng)主要控制流程的核心代碼,但并不包括象球體,聚光燈這樣的具體元素的代碼。核心渲染器是用抽象類寫(xiě)成的,這些抽象類定義了插件類型的接口。在系統(tǒng)運(yùn)行時(shí),用于場(chǎng)景渲染的子類模塊被加載進(jìn)來(lái)。這種組織方法很適合于系統(tǒng)的擴(kuò)展:僅僅寫(xiě)個(gè)新插件就可以擴(kuò)展出新的功能。當(dāng)然,?我們無(wú)法預(yù)知開(kāi)發(fā)者擴(kuò)展系統(tǒng)的方式,有時(shí)修改核心渲染器還是很有必要的。 1.3.1?程序執(zhí)行的各個(gè)階段 pbrt分三個(gè)執(zhí)行階段: 1.?分析用戶提供的場(chǎng)景描述文件。該文件是一個(gè)文本文件,說(shuō)明了場(chǎng)景中所有幾何形體及其材質(zhì),?光源,?相機(jī),所有算法的參數(shù)。分析的結(jié)果是創(chuàng)建出一個(gè)Scene類的實(shí)例。 2.?渲染主循環(huán) 下一步是進(jìn)入主要的渲染流程。這個(gè)階段是最耗時(shí)的了,實(shí)現(xiàn)代碼在Scene::Render(),我們將在1.3.3節(jié)介紹它。 3.?圖像后處理并存盤(pán),信息統(tǒng)計(jì),內(nèi)存釋放等善后工作。 1.3.2?場(chǎng)景的表達(dá) pbrt的main()函數(shù)非常簡(jiǎn)單:首先調(diào)用pbrtInit()來(lái)做系統(tǒng)初始化,然后分析所傳入的每個(gè)場(chǎng)景文件,并對(duì)它們逐個(gè)進(jìn)行渲染,最后調(diào)用pbrtCleanup()做最后的清理工作: <main program> = } 如果沒(méi)有命令行變量傳入pbrt,?則從標(biāo)準(zhǔn)輸入中讀取場(chǎng)景信息;否則,對(duì)每個(gè)命令行變量所指定的文件進(jìn)行分析: < Processscene description>= ? ? } 場(chǎng)景文件的分析器是用lex和yacc寫(xiě)成,lex和yacc文件分別在core/pbrtlex.l?和?core/pbrtparse.y。經(jīng)過(guò)對(duì)場(chǎng)景文件的分析,表示相機(jī)、光源、幾何體素(geometric?primitives)的對(duì)象就被創(chuàng)建出來(lái),再加上管理渲染過(guò)程的其它對(duì)象,就組合成了Scene對(duì)象,這個(gè)對(duì)象是由RenderOptions::MakeScene()來(lái)創(chuàng)建的。Scene類的定義在文件core/scene.h和core/scene.cpp。注意其中的類定義中用了COREDLL宏定義,這用來(lái)表明這些類是核心渲染庫(kù)所導(dǎo)出(export)的類,這在Windows平臺(tái)上是必須的。 <SceneDeclarations>= 每個(gè)幾何物體由一個(gè)Primitive對(duì)象來(lái)表示, Primitive包括兩個(gè)對(duì)象:一個(gè)Shape對(duì)象(用來(lái)說(shuō)明幾何形狀)和一個(gè)Material對(duì)象(用來(lái)說(shuō)明材質(zhì))。所有的這些幾何體素加在一起用一個(gè)單獨(dú)的Primitive來(lái)表示,被稱為Scene::aggregate。?它是一種特殊的Primitive,因?yàn)樗槐3謱?duì)其它許多Primitive的引用。 <SceneData> = 每一個(gè)光源由一個(gè)Light對(duì)象表示,?它說(shuō)明光源的形狀和光能量分布。Scene類把所有的光源放在一個(gè)C++標(biāo)準(zhǔn)庫(kù)的Vector對(duì)象中。有些渲染程序讓每個(gè)體素帶有一個(gè)光源列表,這樣可以允許一個(gè)光源只能照到有限的幾個(gè)物體。這個(gè)方法不太適合pbrt的基于物理的渲染,所以我們只支持用于全場(chǎng)景的光源。 <SceneData> += Camera對(duì)象用來(lái)控制觀察和鏡頭參數(shù),比如相機(jī)位置、朝向、焦點(diǎn)、視野等。Camera類中有一個(gè)Film成員,它被用來(lái)存儲(chǔ)圖像。 <SceneData> += 除了幾何體素,pbrt還支持參與介質(zhì)(participating media)或體積體素(volumetric primitives),pbrt通過(guò)VolumeRegion接口對(duì)這類體素提供支持。跟原始幾何體一樣,?所有的VolumeRegions被放在一個(gè)單一的成員中: ? ?<SceneData> += 積分器模擬光在場(chǎng)景中的傳播并計(jì)算有多少光到達(dá)膠片版的圖像采樣位置。之所以稱之為積分器,是因?yàn)樗脭?shù)值方法對(duì)表面上和體積上的光傳輸方程求積分。表面積分器計(jì)算從幾何表面上的反射光,?而體積積分器計(jì)算從體積體中散射出來(lái)的光。 <SceneData> += ? ?? ???VolumeIntegrator?* volumeIntegrator; 每個(gè)Scene還包括一個(gè)Sampler類的對(duì)象。采樣器的功能很微妙,?因?yàn)樗膶?shí)現(xiàn)極大地影響圖像的質(zhì)量。首先,?采樣器負(fù)責(zé)選擇圖像平面上的點(diǎn),用來(lái)生成被追蹤的光線。其次,?它負(fù)責(zé)提供采樣位置給積分器,用于光傳輸計(jì)算。比如,有些采樣器隨機(jī)地在面光源上選擇點(diǎn),來(lái)計(jì)算面光源的照明。 <SceneData> += 1.3.3?渲染主循環(huán) 當(dāng)我們創(chuàng)建并初始化好Scene對(duì)象后,就可以調(diào)用Scene::Render(),開(kāi)始pbrt的第二階段的執(zhí)行: 渲染主循環(huán)。對(duì)圖像平面上的每一個(gè)位置,用Camera和Sampler生成射入場(chǎng)景中的光線,然后用SurfaceIntegrator和VolumeIntegrator決定有多少光沿著光線路徑到達(dá)圖像平面。這個(gè)值被傳給Film而被記錄下來(lái)。 (圖1.15) <SceneMethods> = } 在渲染開(kāi)始之前,Render()構(gòu)造一個(gè)Sample對(duì)象,?在主循環(huán)過(guò)程中,采樣器將把采樣結(jié)果放在里面。因?yàn)椴蓸拥臄?shù)量和類型多半取決于積分器,所以Sample構(gòu)造器要用到積分器的指針: <Allocate and Initialize sample> = 渲染開(kāi)始之前的另一項(xiàng)工作是調(diào)用積分器的Preprocess()方法,?進(jìn)行跟場(chǎng)景相關(guān)的初始化。比如說(shuō),16.5節(jié)的PhotonIntegrator會(huì)創(chuàng)建關(guān)于光照分布的數(shù)據(jù)結(jié)構(gòu)。 <Allowintegrators to do pre-processing for the scene> = 光線追蹤是很緩慢的過(guò)程,?特別是對(duì)有復(fù)雜光照和場(chǎng)景的情況更是如此。?ProgressReporter對(duì)象為用戶提供一個(gè)直觀的關(guān)于pbrt進(jìn)程的反饋。 渲染主循環(huán)終于開(kāi)場(chǎng)了:?在每次循環(huán)中,?我們調(diào)用Sampler::GetNextSample(),用下一個(gè)圖像采樣值初始化sample,?直到?jīng)]有采樣返回為止。在循環(huán)體中的片斷(fragments)找到相對(duì)應(yīng)的相機(jī)光線,并將它傳給積分器,計(jì)算沿光線路徑到達(dá)膠片平面上的光輻射亮度(radiance)。最后,把結(jié)果放到圖像中,?釋放內(nèi)存資源,?更新進(jìn)程報(bào)告(ProgressReporter)。 <Trace rays: The main loop> = ? ?? ?} Camera類中的Camera::GenerateRay()生成給定圖像采樣位置的光線。它根據(jù)采樣的內(nèi)容初始化ray的每個(gè)成員。所生成的光線的方向向量是規(guī)格化的(單位長(zhǎng)度為1)。 相機(jī)還把一個(gè)浮點(diǎn)數(shù)權(quán)值賦給光線。對(duì)于簡(jiǎn)單的相機(jī)模型而言,所有光線的權(quán)值是相同的。但對(duì)于更復(fù)雜的模型,相機(jī)可以生成更有貢獻(xiàn)性的光線。比如,?對(duì)于真實(shí)相機(jī),到達(dá)膠片平面邊緣的光要少于中間的位置的光,即所謂的漸暈效應(yīng)(vignetting)。?Camera::GenerateRay()返回這個(gè)權(quán)值,用于控制光線對(duì)圖像的貢獻(xiàn)值。 <Find camera rayfor sample> = 為了得到某些紋理函數(shù)的更佳效果(第11章),?有必要生成在圖像平面x和y方向上相距一個(gè)象素遠(yuǎn)的額外光線。這些額外的光線可以用來(lái)計(jì)算紋理關(guān)于象素間距的變化,這是紋理反走樣的關(guān)鍵一環(huán)。類Ray只記錄光線的原點(diǎn)和方向,?RayDifferential繼承了Ray,并加上兩條額外的Ray成員rx,ry來(lái)記錄它的相鄰光線。 <Generate ray differentials for camera ray> = 現(xiàn)在我們有了一條光線,?下一個(gè)任務(wù)是確定有多少光(單位是光輻射亮度Radiance)沿這這條光線到達(dá)圖像平面,Scene:: Li()就是用來(lái)計(jì)算該值的。光輻射亮度值由Spectrum類來(lái)表示,這是pbrt對(duì)關(guān)于波長(zhǎng)的能量分布的抽象—換句話說(shuō),就是顏色。 除了返回radiance值,Scene:: Li()還設(shè)置alpha值,即光線的透明度。如果光線碰到不透明的物體,則alpha值設(shè)為1;如果光線穿過(guò)象霧這樣的半透明體,且沒(méi)有碰上任何不透明體,則alpha在0和1之間。如果光線沒(méi)有碰到任何東西,?則alpha為0。Alpha值可用于很多的后處理效果。比如,?把一個(gè)被渲染的物體合成到一幅照片上。 <Evaluate radiance along camera ray> =
得到光線的貢獻(xiàn)值后,就可用Film::AddSample()更新圖像了(見(jiàn)7.6,8.1,8.2節(jié))。 <Add sample contribution to image> = pbrt用BSDF類來(lái)描述表面上點(diǎn)的材質(zhì)。在渲染過(guò)程中,?有必要為每個(gè)采樣存儲(chǔ)BSDF值。為了避免對(duì)系統(tǒng)內(nèi)存申請(qǐng)函數(shù)的重復(fù)調(diào)用,我們用MemoryArena類管理BSDF內(nèi)存池。一旦對(duì)一個(gè)采樣的貢獻(xiàn)值計(jì)算完畢,要通知BSDF類不再需要其相關(guān)的內(nèi)存了。 <Free BSDF memory from computing image sample value> = 最后,調(diào)用ProgressReporter::Update(),讓ProgressReporter知道完成了一條光線的追蹤。 <Report renderingprogress> = 在主循環(huán)的最后, Scene::Render()釋放Sample的內(nèi)存,?讓ProgressReporter報(bào)告任務(wù)完成,?并寫(xiě)盤(pán): <clean up after rendering and store final image> = 1.3.4?場(chǎng)景的成員函數(shù) 除了Render()以外, Scene類還有其它幾個(gè)很有用的函數(shù)。?Scene::Intersect用來(lái)測(cè)試光線是否和場(chǎng)景中的物體相交。如果相交,?則在Intersection結(jié)構(gòu)中添入沿著光線的最近交點(diǎn)。 <Scene PublicMethods> = ? ?? ?? ? bool Intersect(const Ray &ray, Intersection *isect)const { ? ?? ? } 另一個(gè)相似的函數(shù)是Scene::IntersectP(),它只判定是否有交點(diǎn)存在,?并不計(jì)算出所有的交點(diǎn)并返回最近一個(gè),?故效率要快得多,?它被用在陰影光線(shadow?rays)上。 <Scene PublicMethods> += ? ?? ? } Scene::WorldBound()返回包含場(chǎng)景中所有幾何體的包圍盒,實(shí)際上它是Scene::aggregate的包圍盒。 <Scene Data>+=? ? ?? ? BBox bound; <SceneConstructor Implementation> = <SceneMethods> += Scene:: Li()函數(shù)返回給定光線的輻射亮度。它首先調(diào)用SurfaceIntegrator:: Li()計(jì)算光線跟第一個(gè)相交的表面所產(chǎn)生的出射輻射亮度Lo。然后,調(diào)用VolumeIntegrator::Transmittance()計(jì)算光源T因參與介質(zhì)而產(chǎn)生的光的消弱程度。最后,調(diào)用VolumeIntegrator::?Li()來(lái)計(jì)算因參與介質(zhì)而使輻射亮度得到加強(qiáng)的那部分Lv。最終結(jié)果應(yīng)該是TLo?+ Lv。 <SceneMethods> += ? ?? ?? ?? ???Spectrum Scene :: Li(const RayDifferential &ray, const Sample *sample, float*alpha) const { ? ?? ?? ?? ???Spectrum Lo = surfaceIntegrator->Li(this, ray, sample, alpha); ? ?? ?? ? } 另外, Scene::Transmittance()定義為對(duì)其中的體積積分器的調(diào)用: <Scene Methods>+= ? ?? ?? ?} 1.3.5一個(gè)Whitted風(fēng)格的光線追蹤積分器 第16和17章介紹了很多表面和體積積分器的實(shí)現(xiàn),?它們所基于的算法的精確度不同.?這里介紹一個(gè)基于Whitted光線追蹤算法的表面積分器.?這個(gè)積分器精確地計(jì)算從平滑表面(玻璃,鏡子,水面等)發(fā)出的反射光和透射光,并不考慮間接照明效果.更復(fù)雜的積分器也是建筑在這個(gè)積分器的基本思想上的. <WhittedIntegrator Declarations> = ? ?? ?? ?? ?? ?Class WhittedInteger : public SurfaceIntegrator { ? ?? ?? ?? ?? ?Public: ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? <WhittedIntegrator Public Methods> ? ?? ?? ?? ?? ?Private: ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? <WhittedIntegrator Private Data> ? ?? ?? ?? ?? ?}; 積分器的核心部分是Integrator::Li(),?它返回沿著光線的光輻射亮度.?下圖總結(jié)了在表面積分的過(guò)程中主要類之間的數(shù)據(jù)流程: (圖1.16) <WhittedIntegrator Mothod Definitions> = ? ?? ?? ?? ?? ?Spectrum WittedIntegrator::Li(const Scene *scene, ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? const Raydifferential &ray, const Sample *sample, ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? Float *alpha) const ? ?? ?? ?? ?? ?{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? Intersection isect; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? Spectrum L(0.); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? bool??hitSomething; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? hitSomething = scene->Intersect(ray, &isect); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? if(!hitSomething) ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???<Handle ray with nointersection>? ?? ?? ??? ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? else ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???{ ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?<Initialize alpha for ray hit> ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?<Compute emitted and reflected light at ray intersection point> ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???} ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? return L; ? ?? ?? ?? ?? ? } 積分器首先要用Scene::Intersect()求交點(diǎn).?如果沒(méi)有找到交點(diǎn),?我們把光線的alpha值設(shè)置為0.然而,?有些類型的光源沒(méi)有幾何信息但仍對(duì)那些沒(méi)有交點(diǎn)的光線產(chǎn)生貢獻(xiàn)值.?比如,天空會(huì)對(duì)地球表面產(chǎn)生藍(lán)色光照作用,?而天空沒(méi)有什么幾何信息.?所以,我們?nèi)孕枵{(diào)用Light::Le()來(lái)支持這種情形(雖然大多數(shù)光源并不會(huì)對(duì)這條光線有貢獻(xiàn)值).?第13.5節(jié)會(huì)介紹一種光源,它直接照到膠片平面上,這時(shí),我們要把a(bǔ)lpha值設(shè)置為1(不透明). <Handle ray with no intersection> = ? ?? ?? ?? ?? ?if (alpha)??*alpha = 0; ? ?? ?? ?? ?? ?for ( u_int i = 0;??i < scene->lights.size();??++i) ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? L??+=??scene->lights[ i ]->Le(ray); ? ?? ?? ?? ?? ?if(alpha && !L.Black()) *alpha = 1.; ? ?? ?? ?? ?? ?return L; Whitted積分器沿著光線反射和折射的方向遞歸地求值,所以要記錄遞歸深度,并當(dāng)其到達(dá)預(yù)先設(shè)定的最大深度值時(shí),停止遞歸過(guò)程,?以防止過(guò)程無(wú)限地進(jìn)行下去(充滿鏡子的房間就會(huì)產(chǎn)生這種現(xiàn)象). <WhittedIntegrator Private Data> = ? ?? ?? ?? ?? ?int maxDepth; ? ?? ?? ?? ?? ?mutable int rayDepth; 如果我們找到了交點(diǎn),首先要做的是將輸出變量初始化為1: <Initialize alpha for ray hit> = ? ?? ?? ?? ?? ?if (alpha) *alpha = 1.; 現(xiàn)在我們到達(dá)了Whitted積分器的核心:?累加每個(gè)光源的貢獻(xiàn)值并模擬全反射和折射: <Compute emitted and reflected light at rayintersection point> = ? ?? ?? ?? ?? ?<Evaluate BSDF at hit point> ? ?? ?? ?? ?? ?<Initialize common variables for Whitted integrator> ? ?? ?? ?? ?? ?<Compute emitted light if ray hit an area light source> ? ?? ?? ?? ?? ?<Add contribution of each light source> ? ?? ?? ?? ?? ?if (rayDepth++ <??maxDepth) { ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? <Trace rays for specular reflection and refraction> ? ?? ?? ?? ?? ?} ? ?? ?? ?? ?? ?--rayDepth; 在pbrt中,雙向散射分布函數(shù)由BSDF類表示.pbrt有幾種標(biāo)準(zhǔn)散射函數(shù)的實(shí)現(xiàn),?包括Lambert反射,Torrance-Sparrow微表面模型(第9章).BSDF接口能用來(lái)對(duì)一個(gè)給定的表面上的點(diǎn)著色,但是表面上每個(gè)點(diǎn)的BSDF屬性可能不盡相同.?比如木頭和大理石,?即使木頭被模型化為全漫反射,但其表面上每個(gè)點(diǎn)上的顏色仍取決于木頭的紋理.這種著色參數(shù)的空間變化有Textures類表示,?Textures既可以是過(guò)程型的,也可以存儲(chǔ)在用圖像里. 我們用Intersection::GetBSDF()來(lái)取得交點(diǎn)處的BSDF值: <Evaluate BSDF at hit point> = ? ?? ?? ?? ?? ?BSDF *bsdf = isect.GetBSDF(ray); 下面一段代碼初始化交點(diǎn)位置p,表面法向量n,?從交點(diǎn)到光線原點(diǎn)的規(guī)則化向量ωo: <Initialize common variables for Whittedintegrator> = ? ?? ?? ?? ?? ?const Point &p = bsdf->dgShading.p; ? ?? ?? ?? ?? ?const Normal &n = bsdf->dgShading.nn; ? ?? ?? ?? ?? ?Vector wo = -ray.d; 如果交點(diǎn)所在的幾何體是發(fā)光的(如面光源),?積分器用Intersection::Le()返回所發(fā)出光的輻射亮度: <Compute emiited light if ray hit an area lightsource> = ? ?? ?? ?? ?? ?L += isect.Le(wo); 對(duì)于每個(gè)光源,積分器調(diào)用里L(fēng)ight::Sample_L()來(lái)計(jì)算其對(duì)著色點(diǎn)的貢獻(xiàn)值,?同時(shí)返回從點(diǎn)到光源的方向向量,存在變量wi中.?這個(gè)函數(shù)并不考慮光源被其他物體遮擋的情況,而是返回一個(gè)VisibilityTester對(duì)象,而這個(gè)對(duì)象可以探測(cè)出是否有物體當(dāng)在光源和著色點(diǎn)之間.?正如前面將過(guò)的,是用陰影光線(shadow?ray)的方法解決這個(gè)問(wèn)題. 如果到達(dá)該點(diǎn)的光輻射亮度非零,則BSDF給出關(guān)于方向?qū)?ωo, ωi)的貢獻(xiàn)值,?積分器把光輻射亮度值Li乘以BSDF,cosine項(xiàng),和光線與交點(diǎn)之間的透射比(Transmittance)T: <Add contribution of each light source> ? ?? ?? ?? ?? ?Vector wi; ? ?? ?? ?? ???for(u_int i = 0; i < scene->lights.size(); ++i) { ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? VisibilityTester visibility; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? Spectrum Li = scene->lights[ i ]->Sample_L(p, &wi, &visibility); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?if(Li.Black()) continue; ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? Specturm f = bsdf->f(wo, wi); ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? if(!f.Black() && visibility.Unoccluded(scene)) ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???L += f * Li * AbsDot(wi, n) * visibility.Transmittance(scene); ? ?? ?? ?? ?? ?} 積分器還處理全反射的表面(鏡子,玻璃等).?根據(jù)鏡像原理,積分器很容易地求得反射光的方向,并遞歸地對(duì)之追蹤. BSDF::Sample_f()對(duì)給定的散射模式和出射方向返回一個(gè)入射光的方向,這是蒙特卡羅光傳輸算法的基礎(chǔ)之一(本書(shū)最后幾章有詳細(xì)介紹)。這里,我們只用它得到相對(duì)于全反射和折射的出射方向,并且用一個(gè)標(biāo)志來(lái)指示BSDF::Sample_f()要忽略其它類型的反射。雖然BSDF::Sample_f()采樣離開(kāi)表面的隨機(jī)方向(用于概率積分算法),其隨機(jī)性要受BSDF的散射性質(zhì)的限制。在全反射情況下,只有一個(gè)方向是可能的,所以就根本沒(méi)有隨機(jī)性了。 下面的片斷有兩個(gè)對(duì)BSDF::Sample_f()的調(diào)用,把wi初始化為選定的方向,并給出關(guān)于方向?qū)?ωo, ωi)的BSDF值。如果BSDF值非零,積分器就用Scene::Li()來(lái)得到沿著ωi方向的入射輻射亮度,最后WhittedIntegrator::Li()再將被調(diào)用。為了計(jì)算反射積分的cosine項(xiàng),積分器調(diào)用AbsDot(),由于向量wi和n都是正規(guī)化的,其返回的值正是cosine值。 還有用光線微分作紋理反走樣的內(nèi)容,見(jiàn)第11.1.3?節(jié)。 <Trace rays for specular reflection and refraction> =
到這里,第一章內(nèi)容大致地過(guò)了一遍。還有剩兩個(gè)短短的小節(jié)沒(méi)有涉及: 1.4?如何讀這本書(shū) 1.5?如何用本書(shū)的代碼 這里就略掉不提了。 |
給我老師的人工智能教程打call!http://blog.csdn.net/jiangjunshow
總結(jié)
以上是生活随笔為你收集整理的PBRT 阅读 第一章的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 思科与华为设备OSPF配置命令对比
- 下一篇: Hive 窗口函数lead、lag