Q116:PBRT-V3场景描述文件.pbrt格式解析

195 篇文章 27 订阅
53 篇文章 22 订阅

对于PBRT-V3场景文件.pbrt格式,小编自己根据自己的理解总结如下:

1,“#”后面的内容为注释

举例1:

#sphere.pbrt
#first we set up the eye

2,每一行的第一个词为“关键字”,在api.cpp中对应着处理函数。

举例2:

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

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

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

Integrator "whitted"

#begin describing scene
WorldBegin

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

在api.cpp中:

关键字“LookAt”对应的处理函数是pbrtLookAt();
关键字“Camera”对应的处理函数是pbrtCamera();
关键字“Film”对应的处理函数是pbrtFilm();
关键字“Integrator”对应的处理函数是pbrtIntegrator();
关键字“WorldBegin”对应的处理函数是pbrtWorldBegin();
关键字“AttributeBegin”对应的处理函数是pbrtAttributeBegin();
关键字“CoordSysTransform”对应的处理函数是pbrtCoordSysTransform();
关键字“LightSource”对应的处理函数是pbrtLightSource();
关键字“AttributeEnd”对应的处理函数是pbrtAttributeEnd();

关键字还有:Translate,Rotate,Scale,Material,Shape等等。

3,AttributeBegin和AttributeEnd是成对顺序出现,最临近的一对包含的内容可是为一个block。

举例3:

AttributeBegin
  Translate 0 -1 0
  Rotate 35 0 1 0

  #define a sphere
  AttributeBegin
    Translate -1 .75 -1.5
    Rotate -90 1 0 0

    Material "matte" "color Kd" [0.1 0.9 0.1]

    Shape "sphere" "float radius" [.75] 
  AttributeEnd

  #define a cone
  AttributeBegin
    Translate 0 0 2.5
    Rotate -90 1 0 0

    #this describes the material properties
    Material "matte" "color Kd" [0.9 0.1 0.1]

    #this is the shape
    Shape "cone" "float radius" [.75] "float height" [2]
  AttributeEnd

AttributeEnd

如上有三对AttributeBegin/AttributeEnd,刚好也有三组仿射变换Translate/Rotate。

第二组Translate/Rotate只作用于第二对AttributeBegin/AttributeEnd包住的sphere;
第三组Translate/Rotate只作用于第三对AttributeBegin/AttributeEnd包住的cone;
由于第一对AttributeBegin/AttributeEnd包住的是sphere和cone,所以,第一组Translate/Rotate对sphere和cone都起作用。

4,对关键字后参数的解析

4.1 关键字后直接跟参数值,直接解析,处理函数形参中顺序对应这些参数。

举例4:

LookAt 1 1 10   0 0 -1  0 1 0 #ex ey ez lx ly lz ux uy uz

处理函数原型:

void pbrtLookAt(Float ex, Float ey, Float ez, Float lx, Float ly, Float lz, Float ux, Float uy, Float uz);

举例5:

  Translate 0 -1 0
  Rotate 35 0 1 0

处理函数原型:

void pbrtTranslate(Float dx, Float dy, Float dz);
void pbrtRotate(Float angle, Float ax, Float ay, Float az);

4.2 关键字后跟:类型+属性,其中“属性”的格式是:“属性类型 属性名称”[该名称属性对应的值]

“属性”的格式类似于:

float num = 3.0;

即:类型+变量+赋值

举例6:

Film "image" "string filename" ["sphere.exr"]
     "integer xresolution" [200] "integer yresolution" [200]

解析:
关键字是Film;
这个Film的类型是image;
这个image类型的Film有三个属性:

属性一:有个string类型的属性叫做filename,这个filename的值是“sphere.exr”
属性二:有个integer类型的属性叫做xresolution,这个xresolution的值为200
属性三:有个integer类型的属性叫做yresolution,这个yresolution的值为200

处理函数原型:

void pbrtFilm(const std::string &type, const ParamSet &params);

举例7:

  LightSource "distant" 
              "point from" [0 0 0] "point to"   [0 0 1]
              "color L"    [3 3 3]

解析:
关键字是LightSource;
这个LightSource的类型是distant;
这个distant类型的LightSource有三个属性:

属性一:有个point类型的属性叫做from,这个from的值为[0 0 0]
属性二:有个point类型的属性叫做to,这个to的值为[0 0 1]
属性三:有个color类型的属性叫做L,这个L的值为[3 3 3]

处理函数原型:

void pbrtLightSource(const std::string &name, const ParamSet &params);

举例8:

    Material "uber" "color Kd" [0.1 0.1 0.9] "color Kr" [0.9 0.9 0.9] "color Ks" [0.1 0.1 0.1] "float roughness" [0.9] "float index" [1.34]

解析:
关键字是Material;
这个Material的类型是uber;
这个uber类型的Material有五个属性:

属性一:有个color类型的属性叫做Kd,这个Kd的值为[0.1 0.1 0.9]
属性二:有个color类型的属性叫做Kr,这个Kr的值为[0.9 0.9 0.9]
属性三:有个color类型的属性叫做Ks,这个Ks的值为[0.1 0.1 0.1]
属性四:有个float类型的属性叫做roughness,这个roughness的值为0.9
属性五:有个float类型的属性叫做index,这个index的值为1.34

处理函数原型:

void pbrtMaterial(const std::string &name, const ParamSet &params);

4.3 关键字后跟:类型+X+X+……+属性

这种情况和4.2比较类似,只是在“类型”、“属性”之间加了若干其他信息。

举例9:

Texture "grid" "color" "imagemap" "string filename" ["textures/lines.png"]

处理函数原型:

void pbrtTexture(const std::string &name, const std::string &type,
                 const std::string &texname, const ParamSet &params);

其实,所有情况都可以归到“X+X+X+属性”。
只是,
有的只有X+X+X;
有的X多;
有的X少;

不管是哪种情况,最终都是取决于“处理函数原型”。

———————————————-

“.pbrt格式解析”完毕!!!!!
接下来的内容是“程序对.pbrt文件的处理过程”

———————————————-

参考:Q111:PBRT-V3系统概述

5,程序中解析的过程

PBRT-V3是用第三方程序flex、bison来解析场景描述文件的。

接下来,小编以解析LightSource为例说明一下从开始解析到存储数据的过程。

举例10:

  LightSource "distant" 
              "point from" [0 0 0] "point to"   [0 0 1]
              "color L"    [3 3 3]

在pbrtparse.y中有:

| LIGHTSOURCE STRING paramlist
{
    pbrt::ParamSet params;
    pbrt::InitParamSet(params, pbrt::SpectrumType::Illuminant);
    pbrt::pbrtLightSource($2, params);
    pbrt::FreeArgs();
}

这个是告诉flex、bison在读到关键字“LightSource”时要做的事情。

先看这句:

    pbrt::pbrtLightSource($2, params);

这句是最终调用处理函数pbrtLightSource(),给该函数传了两个参数:
$2,存放的是LightSource的类型名,此处即为“distant”;
params,存放的是这个distant类型的LightSource的所有属性。

5.1 初始化params

存放属性的params是一个ParamSet对象。ParamSet是在src/core/paramset.h中定义的。

class ParamSet {
  public:
    // ParamSet Public Methods
    ParamSet() {}
    void AddFloat(const std::string &, std::unique_ptr<Float[]> v,
                  int nValues = 1);
    void AddInt(const std::string &, std::unique_ptr<int[]> v, int nValues);
    void AddBool(const std::string &, std::unique_ptr<bool[]> v, int nValues);
    void AddPoint2f(const std::string &, std::unique_ptr<Point2f[]> v,
                    int nValues);
    void AddVector2f(const std::string &, std::unique_ptr<Vector2f[]> v,
                     int nValues);
    void AddPoint3f(const std::string &, std::unique_ptr<Point3f[]> v,
                    int nValues);
    void AddVector3f(const std::string &, std::unique_ptr<Vector3f[]> v,
                     int nValues);
    void AddNormal3f(const std::string &, std::unique_ptr<Normal3f[]> v,
                     int nValues);
    void AddString(const std::string &, std::unique_ptr<std::string[]> v,
                   int nValues);
    void AddTexture(const std::string &, const std::string &);
    void AddRGBSpectrum(const std::string &, std::unique_ptr<Float[]> v,
                        int nValues);
    void AddXYZSpectrum(const std::string &, std::unique_ptr<Float[]> v,
                        int nValues);
    void AddBlackbodySpectrum(const std::string &, std::unique_ptr<Float[]> v,
                              int nValues);
    void AddSampledSpectrumFiles(const std::string &, const char **,
                                 int nValues);
    void AddSampledSpectrum(const std::string &, std::unique_ptr<Float[]> v,
                            int nValues);
    bool EraseInt(const std::string &);
    bool EraseBool(const std::string &);
    bool EraseFloat(const std::string &);
    bool ErasePoint2f(const std::string &);
    bool EraseVector2f(const std::string &);
    bool ErasePoint3f(const std::string &);
    bool EraseVector3f(const std::string &);
    bool EraseNormal3f(const std::string &);
    bool EraseSpectrum(const std::string &);
    bool EraseString(const std::string &);
    bool EraseTexture(const std::string &);
    Float FindOneFloat(const std::string &, Float d) const;
    int FindOneInt(const std::string &, int d) const;
    bool FindOneBool(const std::string &, bool d) const;
    Point2f FindOnePoint2f(const std::string &, const Point2f &d) const;
    Vector2f FindOneVector2f(const std::string &, const Vector2f &d) const;
    Point3f FindOnePoint3f(const std::string &, const Point3f &d) const;
    Vector3f FindOneVector3f(const std::string &, const Vector3f &d) const;
    Normal3f FindOneNormal3f(const std::string &, const Normal3f &d) const;
    Spectrum FindOneSpectrum(const std::string &, const Spectrum &d) const;
    std::string FindOneString(const std::string &, const std::string &d) const;
    std::string FindOneFilename(const std::string &,
                                const std::string &d) const;
    std::string FindTexture(const std::string &) const;
    const Float *FindFloat(const std::string &, int *n) const;
    const int *FindInt(const std::string &, int *nValues) const;
    const bool *FindBool(const std::string &, int *nValues) const;
    const Point2f *FindPoint2f(const std::string &, int *nValues) const;
    const Vector2f *FindVector2f(const std::string &, int *nValues) const;
    const Point3f *FindPoint3f(const std::string &, int *nValues) const;
    const Vector3f *FindVector3f(const std::string &, int *nValues) const;
    const Normal3f *FindNormal3f(const std::string &, int *nValues) const;
    const Spectrum *FindSpectrum(const std::string &, int *nValues) const;
    const std::string *FindString(const std::string &, int *nValues) const;
    void ReportUnused() const;
    void Clear();
    std::string ToString() const;
    void Print(int indent) const;

  private:
    // ParamSet Private Data
    std::vector<std::shared_ptr<ParamSetItem<bool>>> bools;
    std::vector<std::shared_ptr<ParamSetItem<int>>> ints;
    std::vector<std::shared_ptr<ParamSetItem<Float>>> floats;
    std::vector<std::shared_ptr<ParamSetItem<Point2f>>> point2fs;
    std::vector<std::shared_ptr<ParamSetItem<Vector2f>>> vector2fs;
    std::vector<std::shared_ptr<ParamSetItem<Point3f>>> point3fs;
    std::vector<std::shared_ptr<ParamSetItem<Vector3f>>> vector3fs;
    std::vector<std::shared_ptr<ParamSetItem<Normal3f>>> normals;
    std::vector<std::shared_ptr<ParamSetItem<Spectrum>>> spectra;
    std::vector<std::shared_ptr<ParamSetItem<std::string>>> strings;
    std::vector<std::shared_ptr<ParamSetItem<std::string>>> textures;
    static std::map<std::string, Spectrum> cachedSpectra;
};

ParamSet的定义看起来蛮复杂的,其实就干四件事(有点类似于数据库及对数据的CRUD):

> 其一:定义了存放各种属性类型数据的容器;比如:bools, ints, floats, spectra, strings等等。
> 其二:对各类容器进行添加数据;
> 其三:对各类容器进行删除数据;
> 其四:在各类容器查找数据;(这里注意一下,是根据“属性名称”进行查找的)

接下来,看看具体是怎么为params填充数据的,调用的是这个函数:

    pbrt::InitParamSet(params, pbrt::SpectrumType::Illuminant);

在partparse.y中有这么一段code,“定义”了所有“属性类型”:

         TRY_DECODING_TYPE("float",     PARAM_TYPE_FLOAT)
    else TRY_DECODING_TYPE("integer",   PARAM_TYPE_INT)
    else TRY_DECODING_TYPE("bool",      PARAM_TYPE_BOOL)
    else TRY_DECODING_TYPE("point2",    PARAM_TYPE_POINT2)
    else TRY_DECODING_TYPE("vector2",   PARAM_TYPE_VECTOR2)
    else TRY_DECODING_TYPE("point3",    PARAM_TYPE_POINT3)
    else TRY_DECODING_TYPE("vector3",   PARAM_TYPE_VECTOR3)
    else TRY_DECODING_TYPE("point",     PARAM_TYPE_POINT3)
    else TRY_DECODING_TYPE("vector",    PARAM_TYPE_VECTOR3)
    else TRY_DECODING_TYPE("normal",    PARAM_TYPE_NORMAL)
    else TRY_DECODING_TYPE("string",    PARAM_TYPE_STRING)
    else TRY_DECODING_TYPE("texture",   PARAM_TYPE_TEXTURE)
    else TRY_DECODING_TYPE("color",     PARAM_TYPE_RGB)
    else TRY_DECODING_TYPE("rgb",       PARAM_TYPE_RGB)
    else TRY_DECODING_TYPE("xyz",       PARAM_TYPE_XYZ)
    else TRY_DECODING_TYPE("blackbody", PARAM_TYPE_BLACKBODY)
    else TRY_DECODING_TYPE("spectrum",  PARAM_TYPE_SPECTRUM)

然后,在InitParamSet()中会根据属性类型调用ParamSet对应的成员函数来给params填充数据。

对于:

  LightSource "distant" 
              "point from" [0 0 0] "point to"   [0 0 1]
              "color L"    [3 3 3]

就会调用到:

    void AddPoint3f(const std::string &, std::unique_ptr<Point3f[]> v, int nValues);
    void AddRGBSpectrum(const std::string &, std::unique_ptr<Float[]> v, int nValues);

具体实现在paramset.cpp中:

void ParamSet::AddPoint3f(const std::string &name,
                          std::unique_ptr<Point3f[]> values, int nValues) {
    ErasePoint3f(name);
    ADD_PARAM_TYPE(Point3f, point3fs);
}
void ParamSet::AddRGBSpectrum(const std::string &name,
                              std::unique_ptr<Float[]> values, int nValues) {
    EraseSpectrum(name);
    CHECK_EQ(nValues % 3, 0);
    nValues /= 3;
    std::unique_ptr<Spectrum[]> s(new Spectrum[nValues]);
    for (int i = 0; i < nValues; ++i) s[i] = Spectrum::FromRGB(&values[3 * i]);
    std::shared_ptr<ParamSetItem<Spectrum>> psi(
        new ParamSetItem<Spectrum>(name, std::move(s), nValues));
    spectra.push_back(psi);
}

5.2 pbrtLightSource()

pbrtLightSource将数据写入渲染内存中。

void pbrtLightSource(const std::string &name, const ParamSet &params) {
    VERIFY_WORLD("LightSource");
    WARN_IF_ANIMATED_TRANSFORM("LightSource");
    MediumInterface mi = graphicsState.CreateMediumInterface();
    std::shared_ptr<Light> lt = MakeLight(name, params, curTransform[0], mi);
    if (!lt)
        Error("LightSource: light type \"%s\" unknown.", name.c_str());
    else
        renderOptions->lights.push_back(lt);
    if (PbrtOptions.cat || PbrtOptions.toPly) {
        printf("%*sLightSource \"%s\" ", catIndentCount, "", name.c_str());
        params.Print(catIndentCount);
        printf("\n");
    }
}

这个函数在src/core/api.cpp中。
完成将LightSource的数据写入renderOptions->lights中。

渲染相关的数据都是存在renderOptions中的。

renderOptions的类是长这个样子:

struct RenderOptions {
    // RenderOptions Public Methods
    Integrator *MakeIntegrator() const;
    Scene *MakeScene();
    Camera *MakeCamera() const;

    // RenderOptions Public Data
    Float transformStartTime = 0, transformEndTime = 1;
    std::string FilterName = "box";
    ParamSet FilterParams;
    std::string FilmName = "image";
    ParamSet FilmParams;
    std::string SamplerName = "halton";
    ParamSet SamplerParams;
    std::string AcceleratorName = "bvh";
    ParamSet AcceleratorParams;
    std::string IntegratorName = "path";
    ParamSet IntegratorParams;
    std::string CameraName = "perspective";
    ParamSet CameraParams;
    TransformSet CameraToWorld;
    std::map<std::string, std::shared_ptr<Medium>> namedMedia;
    std::vector<std::shared_ptr<Light>> lights;
    std::vector<std::shared_ptr<Primitive>> primitives;
    std::map<std::string, std::vector<std::shared_ptr<Primitive>>> instances;
    std::vector<std::shared_ptr<Primitive>> *currentInstance = nullptr;
    bool haveScatteringMedia = false;
};

简单说来,
程序外,描述场景的是.pbrt格式的文件;
程序内,描述场景的是RenderOptions对象;

另外,注意一下,对于LightSource,pbrtLightSource()直接调用了MakeLight()创建了LightSource对象。
所以,renderOptions->lights中保存的是LightSource对象的指针。

而像RenderOptions中的这些:

    std::string FilterName = "box";
    ParamSet FilterParams;
    std::string FilmName = "image";
    ParamSet FilmParams;
    std::string SamplerName = "halton";
    ParamSet SamplerParams;
    std::string AcceleratorName = "bvh";
    ParamSet AcceleratorParams;
    std::string IntegratorName = "path";
    ParamSet IntegratorParams;
    std::string CameraName = "perspective";
    ParamSet CameraParams;
    TransformSet CameraToWorld;

对应的“pbrt关键字”函数中只是将相关属性参数存入RenderOptions的对应成员变量中。

注意到:
在RenderOptions类定义的前面有几个成员方法:

    Integrator *MakeIntegrator() const;
    Scene *MakeScene();
    Camera *MakeCamera() const;

这个就是用来创建那些还没有在对应“pbrt关键字”函数中创建对象的类的对象的。

6,pbrtWorldEnd()

最后处理关键字“WorldEnd”。
在pbrtWorldEnd()有非常重要的三行代码:

        std::unique_ptr<Integrator> integrator(renderOptions->MakeIntegrator());
        std::unique_ptr<Scene> scene(renderOptions->MakeScene());



        if (scene && integrator) integrator->Render(*scene);

1,MakeIntegrator()
2,MakeScene()
3,integrator->Render(*scene)

即:
创建积分器;创建场景;用积分器渲染场景;

重点提一下MakeScene()。

创建Scene对象。
之前小编一直搞不清RenderOption对象和Scene对象的关系:
RenderOption侧重数据,直接和场景描述对应;
Scene是将RenderOption的数据再封装了一下,同时加入了和渲染相关的方法。

贴出Scene的定义代码:

// Scene Declarations
class Scene {
  public:
    // Scene Public Methods
    Scene(std::shared_ptr<Primitive> aggregate,
          const std::vector<std::shared_ptr<Light>> &lights)
        : lights(lights), aggregate(aggregate) {
        // Scene Constructor Implementation
        worldBound = aggregate->WorldBound();
        for (const auto &light : lights) {
            light->Preprocess(*this);
            if (light->flags & (int)LightFlags::Infinite)
                infiniteLights.push_back(light);
        }
    }
    const Bounds3f &WorldBound() const { return worldBound; }
    bool Intersect(const Ray &ray, SurfaceInteraction *isect) const;
    bool IntersectP(const Ray &ray) const;
    bool IntersectTr(Ray ray, Sampler &sampler, SurfaceInteraction *isect,
                     Spectrum *transmittance) const;

    // Scene Public Data
    std::vector<std::shared_ptr<Light>> lights;
    // Store infinite light sources separately for cases where we only want
    // to loop over them.
    std::vector<std::shared_ptr<Light>> infiniteLights;

  private:
    // Scene Private Data
    std::shared_ptr<Primitive> aggregate;
    Bounds3f worldBound;
};

场景描述文件解析结束时得到两样东西:
一个积分器对象;
一个场景对象;

然后,开始用“积分器对象”渲染“场景对象”。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值