日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

PBRT 阅读 第一章

發(fā)布時(shí)間:2023/12/14 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PBRT 阅读 第一章 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

分享一下我老師大神的人工智能教程!零基礎(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ù)。

交互式渲染(Interactive):為了高性能和低延遲而犧牲真實(shí)感的渲染。? ?? ?

非真實(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> =
int main(int argc, char *argv{}) {
? ? pbrtInit();
? ? < Process scene description>
? ? pbrtCleanup();
? ? return 0;

}

如果沒(méi)有命令行變量傳入pbrt,?則從標(biāo)準(zhǔn)輸入中讀取場(chǎng)景信息;否則,對(duì)每個(gè)命令行變量所指定的文件進(jìn)行分析:

< Processscene description>=
? ? if(argc == 1)
? ?? ???ParseFile(“-“);? ? // “-“代表標(biāo)準(zhǔn)輸入
? ? else {
? ?? ???for(int i = 0; i< argc; i++)
? ?? ???if(!ParseFile(argv[ i ]))
? ?? ?? ?? ???Error(“Could’t open scene file \”%s\”\n”,argv[ i ]);

? ? }

場(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)上是必須的。
場(chǎng)景類的定義如下:

<SceneDeclarations>=
? ?? ???class COREDLLScene {
? ?? ???public:
? ?? ?? ?? ???<Scene Public Methods>
? ?? ?? ?? ? <Scene Data>
? ?? ???};

每個(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> =
? ?? ???Primitive *aggregate;

每一個(gè)光源由一個(gè)Light對(duì)象表示,?它說(shuō)明光源的形狀和光能量分布。Scene類把所有的光源放在一個(gè)C++標(biāo)準(zhǔn)庫(kù)的Vector對(duì)象中。有些渲染程序讓每個(gè)體素帶有一個(gè)光源列表,這樣可以允許一個(gè)光源只能照到有限的幾個(gè)物體。這個(gè)方法不太適合pbrt的基于物理的渲染,所以我們只支持用于全場(chǎng)景的光源。

<SceneData> +=
? ?? ???vector<Light*> lights;

Camera對(duì)象用來(lái)控制觀察和鏡頭參數(shù),比如相機(jī)位置、朝向、焦點(diǎn)、視野等。Camera類中有一個(gè)Film成員,它被用來(lái)存儲(chǔ)圖像。
當(dāng)圖像生成后,Film對(duì)象負(fù)責(zé)把圖像寸盤(pán)。

<SceneData> +=
? ?? ???Camera *camera;


除了幾何體素,pbrt還支持參與介質(zhì)(participating media)或體積體素(volumetric primitives),pbrt通過(guò)VolumeRegion接口對(duì)這類體素提供支持。跟原始幾何體一樣,?所有的VolumeRegions被放在一個(gè)單一的成員中:

? ?<SceneData> +=
? ?? ?? ? VolumeRegion *volumeRegion;

積分器模擬光在場(chǎng)景中的傳播并計(jì)算有多少光到達(dá)膠片版的圖像采樣位置。之所以稱之為積分器,是因?yàn)樗脭?shù)值方法對(duì)表面上和體積上的光傳輸方程求積分。表面積分器計(jì)算從幾何表面上的反射光,?而體積積分器計(jì)算從體積體中散射出來(lái)的光。

<SceneData> +=
? ?? ???SurfaceIntegrator* surfaceIntegrator;

? ?? ???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> +=
? ?? ???Sampler * sampler;


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> =
void Scene::Render() {
? ?? ???<Allocate and Initialize sample>
? ?? ???<Allow integrators to do pre-processing for the scene>
? ?? ???<Trace rays: The main loop>
? ?? ???<Clean up after rendering and store final image>

}

在渲染開(kāi)始之前,Render()構(gòu)造一個(gè)Sample對(duì)象,?在主循環(huán)過(guò)程中,采樣器將把采樣結(jié)果放在里面。因?yàn)椴蓸拥臄?shù)量和類型多半取決于積分器,所以Sample構(gòu)造器要用到積分器的指針:


<Allocate and Initialize sample> =
? ?? ???Sample *sample = new Sample(surfaceIntegrator,
? ?? ?? ?? ?? ?? ?? ?? ?volumeIntegrator,
? ?? ?? ?? ?? ?? ?? ???this);


渲染開(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> =
? ?? ?? ? surfaceIntegrator-> Preprocess(this);
? ?? ?? ? volumeIntegrator-> Preprocess(this);


光線追蹤是很緩慢的過(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> =
? ?? ?? ? ProgressReporter progress(sample->TotalSamples(),“Rendering”);
? ?? ?? ? while(sampler->GetNextSample(sample){
? ?? ?? ?? ?? ?? ?<Find camera ray for sample>
? ?? ?? ?? ?? ?? ?<Evaluate radiance along camera ray>
? ?? ?? ?? ?? ?? ?<Add sample contribution to image>
? ?? ?? ?? ?? ?? ?<Free BSDF memory from computing image sample value>
? ?? ?? ?? ?? ?? ?<Report rendering progress>

? ?? ?}


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> =
? ?? ?? ? RayDifferential ray;
? ?? ?? ? float rayWeight = camera->GenerateRay(*sample,&ray);
? ?? ?? ? <Generate ray differentials for camera ray>


為了得到某些紋理函數(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> =
? ?? ???++(sample->imageX);
? ?? ???camera->GenerateRay(*sample, &ray, rx);
? ?? ???--(sample->imageX);
? ?? ???++(sample->imageY);
? ?? ???camera->GenerateRay(*sample, &ray, ry);
? ?? ???ray.hasDifferentials= true;
? ?? ???--(sample->imageY);

現(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> =
? ?? ? float alpha;
? ?? ? Spectrum Ls = 0.f;


? ?? ? if(rayWeight > 0.f)
? ?? ? Ls= rayWeight * Li(ray,sample, α);
? ?? ?<Issue warning if unexpected radiance value returned>


得到光線的貢獻(xiàn)值后,就可用Film::AddSample()更新圖像了(見(jiàn)7.6,8.1,8.2節(jié))。

<Add sample contribution to image> =
? ?? ?? ?Camera->film->AddSample(*sample, ray, Ls,alpha);


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> =
? ?? ?? ?BSDF::FreeAll();


最后,調(diào)用ProgressReporter::Update(),讓ProgressReporter知道完成了一條光線的追蹤。

<Report renderingprogress> =
? ?? ? static StatsCounter cameraRaysTraced(“Camera”,“Camera Rays Traced”);
? ?? ? ++cameraRaysTraced;


在主循環(huán)的最后, Scene::Render()釋放Sample的內(nèi)存,?讓ProgressReporter報(bào)告任務(wù)完成,?并寫(xiě)盤(pán):

<clean up after rendering and store final image> =
? ?? ???delete sample;
? ?? ???progress.Done();
? ?? ???camera->film->WriteImage();


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 {
? ?? ?? ? return aggregate->Intersect(ray,isect);

? ?? ? }


另一個(gè)相似的函數(shù)是Scene::IntersectP(),它只判定是否有交點(diǎn)存在,?并不計(jì)算出所有的交點(diǎn)并返回最近一個(gè),?故效率要快得多,?它被用在陰影光線(shadow?rays)上。

<Scene PublicMethods> +=
? ?? ? bool IntersectP(const Ray &ray) const {
? ?? ?? ?? ???return aggregate->IntersectP(ray);

? ?? ? }


Scene::WorldBound()返回包含場(chǎng)景中所有幾何體的包圍盒,實(shí)際上它是Scene::aggregate的包圍盒。

<Scene Data>+=?

? ?? ? BBox bound;


<SceneConstructor Implementation> =
? ?? ? bound =aggregate->WorldBound();


<SceneMethods> +=
? ?? ?? ?Const BBox &Scene::WorldBound const{
? ?? ?? ?? ?? ?Return bound;
? ?? ?? ?}


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);
? ?? ?? ?? ???Spectrum T = volumeIntegrator->Transmittance(this,ray,sample,alpha);
? ?? ?? ?? ???Spectrum Lv = volumeIntegrator->Li(this,ray,sample, alpha);
? ?? ?? ?? ???return T *Lo +Lv;

? ?? ?? ? }


另外, Scene::Transmittance()定義為對(duì)其中的體積積分器的調(diào)用:

<Scene Methods>+=
? ?? ?? ?Spectrum Scene::Transmittance(const Ray &ray) const {
? ?? ?? ?? ???return volumnIntegrator->Transmittance(this,ray,NULL,NULL);

? ?? ?? ?}


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> =
? ?? ?Spectrumf = bsdf->Sample_f(wo, &wi, BxDFType(BSDF_REFLECTION|BSDF_SPECULAR));
? ?? ?if(!f.Black()){
? ?? ?? ?? ?? ?<Computeray differential rd for specularreflection>
? ?? ?? ?? ?? ? L+= f * scene->Li(rd, sample) * AbsDot(wi, n);
? ?? ?}


? ?? ?f =bsdf->Sample_f(wo, &wi,? ? BxDFType(BSDF_REFLECTION|BSDF_SPECULAR));
? ?? ?if(!f.Black()){
? ?? ?? ?? ?<Computeray differential rd for?
? ?? ?? ?? ?specular transmition>
? ?? ?? ?? ?L+= f * scene->Li(rd, sample) * AbsDot(wi, n);
? ?? ?}


到這里,第一章內(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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。