天天看點

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

小編在這裡想通過渲染一個簡單的場景(比如,一個球),來看看:

從“系統讀入場景描述檔案”到“系統輸出圖形”的過程中,系統具體是怎麼跑的。(内容适合有一定ray tracing基礎的童鞋)

〇、準備

直接參考:

1,PBRT-V3官方代碼(book版)

2,PBRT-V3原著章節“1.3 pbrt: System Overview”

另外,小編之前零零碎碎寫了一些小總結:

Q106:Linux系統下安裝編譯PBRT-V3

Q107:Linux系統下GDB對PBRT-V3進行debug

Q108:淺析PBRT-V3的代碼結構

Q110:PBRT-V3十大基類對應的繼承關系

對了,首先參考“Q107”将debug版本的pbrt編譯出來。

小編的調試環境是:Ubuntu+GDB

這一章節,用到的場景描述檔案sphere.pbrt内容如下:

#first we set up the eye
LookAt        -     #ex ey ez lx ly lz ux uy uz

#the camera
Camera "perspective" "float fov" []

#name the file
Film "image" "string filename" ["sphere.exr"]
     "integer xresolution" [] "integer yresolution" []

Integrator "whitted"

#begin describing scene
WorldBegin

#light source
AttributeBegin
  CoordSysTransform "camera"
  LightSource "distant" 
              "point from" [  ] "point to"   [  ]
              "color L"    [  ]
AttributeEnd

#transform the world
AttributeBegin
  Translate  - 
  Rotate    

  #define a sphere
  AttributeBegin
    Translate   -
    Rotate -   
    Scale   

    Material "matte" "color Kd" [  ]

    Shape "sphere" "float radius" [] 
  AttributeEnd

AttributeEnd

WorldEnd
           

涉及的“十大基類”中的Camera、Film、Integrator、Light、Material、Shape。

對應輸出的圖形是:

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

接下來,咱用pbrt的debug版本渲染(調試)上面提到的這個場景。

一、main()

對的,系統是從main()開始跑的。

main()在這個位置:

/home/libingzeng/CG/pbrt-v3/src/main/pbrt.cpp:74:int main(int argc, char *argv[])

書上提供的main()文學編碼形式截圖如下:

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

main()函數裡主要幹的就是這麼四件事。

接下來,咱先在這四個位置設定斷點看看。

(本着懷疑的學習态度,别人說是這麼回事,有時候咱不太相信,就想自己看看)

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

(其中第二個斷點是讀完指令行參數,開始打一些類似“歡迎光臨”的log的位置)

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明
Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明
Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

二,準備再次運作

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明
Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明
Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明
Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

再次運作調試,接下來,咱就開始看看ParseFile()處理場景描述檔案具體是怎麼進行的。

三、解析場景檔案

3.1 理論介紹

解析場景檔案的工作是有第三方工具flex、bison完成的。

怎麼指揮flex、bison工作呢?

場景檔案是由各種關鍵字(Camera、Film、Integrator、Light、Material、Shape等等)組成;

是以,隻需要告訴flex、bison在場景檔案中讀到這些關鍵字時應該幹什麼。

pbrtparse.y就是指揮flex、bison工作的。這個檔案的路徑:/home/libingzeng/CG/pbrt-v3/src/core/pbrtparse.y
    pbrtparse.y中的内容是如下格式:

    | SHAPE STRING paramlist
    {
        pbrt::ParamSet params;
        pbrt::InitParamSet(params, pbrt::SpectrumType::Reflectance);
        pbrt::pbrtShape($2, params);
        pbrt::FreeArgs();
    }

    意思是:當在場景描述檔案中讀到“Shape”關鍵字時,調用pbrt系統中的對應函數pbrtShape()。
           

flex、bison會根據pbrtparse.y在同路徑下生成pbrtparse.cpp。pbrtparse.cpp中有一個yyparse()函數。

yyparse()的内容是和pbrtparse.y的内容對應的。
    這個函數是flex、bison提供給pbrt用于觸發flex、bison工作的。
           

這樣一來,為了解析場景描述檔案,pbrt中需要做的是:

1,提供pbrtparse.y(指揮flex、bison工作);
    2,實作場景檔案中關鍵字對應的處理函數(flex、bison根據pbrtparse.y的訓示,讀取場景描述檔案,然後調用對應的處理函數);
    3,調用yyparse()觸發flex、bison開始工作;
           

前面提到的ParseFile()中就調用yyparse().

(ParseFile()的路徑:/home/libingzeng/CG/pbrt-v3/src/core/parser.cpp)

另外,所有“場景檔案中關鍵字對應的處理函數”是在/home/libingzeng/CG/pbrt-v3/src/core/api.cpp中實作的。

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

3.2 設定斷點進行調試

前面,咱提到sphere.pbrt中對應的關鍵字有:

Camera、Film、Integrator、Light、Material、Shape

接下來,咱在其建立對象對象的函數中設定斷點,看看具體是怎麼建立這些對象的。

設定六個斷點:(下方貼圖是後面改的,是以不要關注斷點号)

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

然後,運作,檢視callstack。

接下來,按照停的順序貼出對應的callstack,如下:

1,Light(sphere.pbrt中對應的是distant light)

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明
Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

2,Shape(sphere.pbrt中對應的是sphere)

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明
Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

3,Material(sphere.pbrt中對應的是matte)

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

4,Film

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

5,Camera(sphere.pbrt中對應的是perspective)

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

6,Integrator(sphere.pbrt中對應的是whitted)

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

3.3 解析檔案總結

書上說,完成解析檔案時得到兩樣東西:一個Integrator執行個體、一個Scene執行個體。

其他所有參數都儲存在RenderOptions結構體中。

(RenderOptions是個百寶箱結構體,關于場景的資訊,啥都有)

RenderOptions長這個樣子:

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

場景描述檔案中的資訊和RenderOptions結構體中的資料是一一對應的(除了預設設定)

pbrtparse.y指揮flex、bison解析場景檔案,針對不同關鍵字調用api.cpp中對應的pbrtXXXX()将場景檔案中的資訊讀入并儲存到RenderOptions中。

理論上,解析檔案階段隻是将場景檔案中的資料填到RenderOptions中。

但是,有的pbrtXXXX()在填資料時,還做了其他事情(參考3.2中的callstack):

pbrtLightSource()中還調用MakeLight()建立了對應的Light對象;
    pbrtShape()中不但建立了對應的Shape對象,還建立了相關的Material對象;
           

前面已經提到,解析檔案階段要得到“Integrator執行個體”和“Scene執行個體”。

關鍵字Integrator在解析檔案的函數中對應pbrtIntegrator(),但是,這個函數隻是将Integrator的資料填入RenderOptions中。

建立Integrator執行個體是在處理關鍵字WorldEnd的函數pbrtWorldEnd()中。

讀到WorldEnd,意味着場景檔案解析要結束了。此時,需要建立Integrator執行個體和Scene執行個體。

三、pbrtWorldEnd()

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

MakeIntegrator()

從3.2中的callstack可以看出:

MakeIntegrator()根據RenderOptions中的資料先建立的Camera(同時會建立Film),然後建立了Integrator。

MakeScene()

從3.2中的callstack可以知道:

Light執行個體已經在對應解析函數pbrtLightSource()中建立;

Shape執行個體、Material執行個體已經在pbrtShape()中建立;

Shape+Material=Primitive,是以,Primitive執行個體也就建立了。

直接貼出MakeScene()函數,看看它幹了什麼:

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

下面貼上Scene聲明的代碼:

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

Render()

四、SamplerIntegrator::Render()

Render()是渲染圖形的主循環。

sphere.pbrt中對應的是Integrator是Whitted。

是以,自然是跑Whitted的Render()啦。

又,Whitted是SamplerIntegrator的子類:SamplerIntegrator中唯一的虛函數是Li(),其所有子類之間的差異是對Li()的實作;其所有子類的Render()是直接繼承SamplerIntegrator的Render()。

是以,此處跑的“SamplerIntegrator的Render()”的代碼。

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

第一循環:

将圖形分成若幹塊并行渲染,一塊一塊循環。

剛進入循環,首先要做的事情就是:

計算目前圖形塊的邊界,進而計算出目前圖形塊上所有像素點的位置。

第二循環:

針對具體圖形塊上,一個像素點一個像素點循環。

剛進入循環,首先要做的事情就是:

啟動采樣次數(相當于為第三循環啟動計數器)。

第三循環:

針對每個像素點,進行多次采樣,一次采樣一次采樣循環。

剛進入循環,首先要做的事情就是:

計算目前這次采樣對應的權值(用于對此次渲染結果進行權重)

然後,在第三循環内部計算目前像素點上的此次采樣點上顔色值(或者說輻射率):

if (rayWeight > ) L = Li(ray, scene, *tileSampler, arena);
           

前面已經提到,SamplerIntegrator的Li()是虛函數,其子類必須實作。

是以,此處跑的代碼是:Whitted::Li()。

五、Whitted::Li()

5.1 整體描述

關于Whitted Integrator,貼一段書上的原話:

Here we will present an integrator based on Whitted's ray-tracing algorithm. 
This integrator accurately computes 
reflected and transmitted light from specular surfaces like glass, mirrors, and water, 
although it doesn’t account for 
other types of indirect lighting effects like light 
bouncing off a wall and illuminating a room. 
           

簡單說來,間接光照部分隻考慮鏡面反射光和鏡面折射光。

另外,在貼出書上提到的渲染方程:

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

其中:

第一項為發光物體發出的光(表示撞擊點就在光源上);

第二項為撞擊點處反射進入相機的光。這部分光包含:撞擊點處直接光照反射的光;撞擊點處間接光照反射的光。當然,不管是直接光照還是間接光照,反射光都是通過“反射函數”來計算的(“反射函數”指的就是那些什麼BRDF、BTDF、BSDF、BSSRDF等等之類)

大概了解了Whitted模型的算法,再來看看Whitted::Li()的代碼。

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

5.2 SurfaceInteraction

隆重介紹一下SurfaceInteraction結構體。

(SurfaceInteraction是個百寶箱結構體,關于撞擊點的資訊,啥都有)

(當然,像bsdf之類的可能需要該結構體包含的函數(isect.ComputeScatteringFunctions(ray, arena))再算一下)

SurfaceInteraction長這個樣子:

(SurfaceInteraction是繼承于Interaction,是以,先貼出Interaction的代碼。)

// Interaction Declarations
struct Interaction {
    // Interaction Public Methods
    Interaction() : time() {}
    Interaction(const Point3f &p, const Normal3f &n, const Vector3f &pError,
                const Vector3f &wo, Float time,
                const MediumInterface &mediumInterface)
        : p(p),
          time(time),
          pError(pError),
          wo(Normalize(wo)),
          n(n),
          mediumInterface(mediumInterface) {}
    bool IsSurfaceInteraction() const { return n != Normal3f(); }
    Ray SpawnRay(const Vector3f &d) const {
        Point3f o = OffsetRayOrigin(p, pError, n, d);
        return Ray(o, d, Infinity, time, GetMedium(d));
    }
    Ray SpawnRayTo(const Point3f &p2) const {
        Point3f origin = OffsetRayOrigin(p, pError, n, p2 - p);
        Vector3f d = p2 - p;
        return Ray(origin, d,  - ShadowEpsilon, time, GetMedium(d));
    }
    Ray SpawnRayTo(const Interaction &it) const {
        Point3f origin = OffsetRayOrigin(p, pError, n, it.p - p);
        Point3f target = OffsetRayOrigin(it.p, it.pError, it.n, origin - it.p);
        Vector3f d = target - origin;
        return Ray(origin, d,  - ShadowEpsilon, time, GetMedium(d));
    }
    Interaction(const Point3f &p, const Vector3f &wo, Float time,
                const MediumInterface &mediumInterface)
        : p(p), time(time), wo(wo), mediumInterface(mediumInterface) {}
    Interaction(const Point3f &p, Float time,
                const MediumInterface &mediumInterface)
        : p(p), time(time), mediumInterface(mediumInterface) {}
    bool IsMediumInteraction() const { return !IsSurfaceInteraction(); }
    const Medium *GetMedium(const Vector3f &w) const {
        return Dot(w, n) >  ? mediumInterface.outside : mediumInterface.inside;
    }
    const Medium *GetMedium() const {
        CHECK_EQ(mediumInterface.inside, mediumInterface.outside);
        return mediumInterface.inside;
    }

    // Interaction Public Data
    Point3f p;
    Float time;
    Vector3f pError;
    Vector3f wo;
    Normal3f n;
    MediumInterface mediumInterface;
};
           
// SurfaceInteraction Declarations
class SurfaceInteraction : public Interaction {
  public:
    // SurfaceInteraction Public Methods
    SurfaceInteraction() {}
    SurfaceInteraction(const Point3f &p, const Vector3f &pError,
                       const Point2f &uv, const Vector3f &wo,
                       const Vector3f &dpdu, const Vector3f &dpdv,
                       const Normal3f &dndu, const Normal3f &dndv, Float time,
                       const Shape *sh);
    void SetShadingGeometry(const Vector3f &dpdu, const Vector3f &dpdv,
                            const Normal3f &dndu, const Normal3f &dndv,
                            bool orientationIsAuthoritative);
    void ComputeScatteringFunctions(
        const RayDifferential &ray, MemoryArena &arena,
        bool allowMultipleLobes = false,
        TransportMode mode = TransportMode::Radiance);
    void ComputeDifferentials(const RayDifferential &r) const;
    Spectrum Le(const Vector3f &w) const;

    // SurfaceInteraction Public Data
    Point2f uv;
    Vector3f dpdu, dpdv;
    Normal3f dndu, dndv;
    const Shape *shape = nullptr;
    struct {
        Normal3f n;
        Vector3f dpdu, dpdv;
        Normal3f dndu, dndv;
    } shading;
    const Primitive *primitive = nullptr;
    BSDF *bsdf = nullptr;
    BSSRDF *bssrdf = nullptr;
    mutable Vector3f dpdx, dpdy;
    mutable Float dudx = , dvdx = , dudy = , dvdy = ;
};
           

小編之前一直有個疑問,SurfaceInteraction的這些資訊是什麼時候,怎麼填入的呢?

在咱們這個sphere.pbrt場景裡,ray-scene相交函數最終肯定是call到了Sphere::Intersect()中。

咱先看看Sphere::Intersect()函數的callstack:

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

咱再看看Sphere::Intersect()、GeometricPrimitive::Intersect()、BVHAccel::Intersect()是怎麼填資料的。

(資料主要是前兩者填的)

Sphere::Intersect():

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

主要包含:撞擊點坐标、到相機的距離、法向量、紋理uv等等。

(當然還包含shape本身……this,但是這個會在GeometricPrimitive覆寫)

GeometricPrimitive::Intersect():

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

主要包含:primitive本身……this(這個等于shape+material)、空間中的媒體medium

5.3 ComputeScatteringFunctions()

咱這sphere.pbrt對應的材質是matte,是以對應的這個函數是:

MatteMaterial::ComputeScatteringFunctions()

設個斷點,看看是怎麼call到這裡來的。

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

5.4 撞擊點處的直接光照

(略)

5.5 撞擊點處的間接光照

這個“間接光照”是什麼意思呢?

比如,對于鏡面材質物體,能看到其他物體在鏡面材質物體上的像。

這個像是怎麼形成的呢?

來自其他物體的反射的光線打到撞擊點,由于被撞擊的物體是鏡面材質,是以相機能在撞擊點處看到其他物體的像。

這裡的“其他物體反射的光線”就不是直接來自光源的,是以稱為“間接光照”。

那麼,問題來了:

咱這裡渲染的sphere.pbrt對應的材質是matte啊,怎麼也跑到間接光照函數裡面去啦?

Q111:PBRT-V3系統概述〇、準備一、main()二,準備再次運作三、解析場景檔案三、pbrtWorldEnd()四、SamplerIntegrator::Render()五、Whitted::Li()六,權重求和單次采樣點的輻射率七,合并圖形塊八,輸出圖形九,說明

(小編在陸陸續續更新過程中,換了編譯環境,現在直接使用Xcode了)

specular pdf值為0意味着什麼?

意味着,對于matte材質,對應的間接光照的鏡面反射光部分的輻射率為0。

那麼,鏡面折射光呢?情況和鏡面反射光類似。

在Whitted::Li()目前的代碼架構下,

不管是什麼材質,都會跑入直接光照和間接光照(鏡面反射&鏡面折射)的函數,然後依據對應的pdf來計算各個部分的輻射率。

pdf為0表示相應部分的輻射率為0.

六,權重求和單次采樣點的輻射率

參考“四,Render()”

對于其中“第三循環”(即:針對每個像素點,進行多次采樣,一次采樣一次采樣循環)

當每次采樣結束時,将每次采樣點的輻射率的值權重求和。

filmTile->AddSample(cameraSample.pFilm, L, rayWeight);
           

七,合并圖形塊

參考“四,Render()”

對于其中“第二循環”(即:針對具體圖形塊上,一個像素點一個像素點循環)

當一個圖形塊渲染結束時,将該圖形塊合并到整體塊上。

camera->film->MergeFilmTile(std::move(filmTile));
           

八,輸出圖形

參考“四,Render()”

對于其中“第一循環”(即:将圖形分成若幹塊并行渲染,一塊一塊循環)

當所有圖形塊都渲染完成,輸出圖形。

camera->film->WriteImage();
           

這個時候,也将退出Render()

然後,就是各級的清場善後處理啦。

依次退出pbrtWorldEnd()、yyparse()、ParseFile(),

最後,退出main()。

Done!!!

九,說明

文中刻意回避多談“直接光照”、“間接光照”、“鏡面反射”、“鏡面折射”,還有對應的pdf、bsdf、bssrdf等等原理性的東西。

其實,這些才是PBRT的核心内容,也是原著的重中之重。

<完>

感謝閱讀!!!

發個彩蛋:

Physically Based Rendering, Third Edition 第三版電子書

繼續閱讀