1. 程式人生 > >PBRT_V2 總結記錄 BSDFSample 和 BSDFSampleOffsets 和 BSDF::Sample_f() 和BSDF:Pdf

PBRT_V2 總結記錄 BSDFSample 和 BSDFSampleOffsets 和 BSDF::Sample_f() 和BSDF:Pdf

BSDFSample

struct BSDFSample {
   // BSDFSample Public Methods
   BSDFSample(float up0, float up1, float ucomp) {
       Assert(up0 >= 0.f && up0 < 1.f);
       Assert(up1 >= 0.f && up1 < 1.f);
       Assert(ucomp >= 0.f && ucomp < 1.f);
       uDir[0] = up0;
       uDir[1] = up1;
       uComponent = ucomp;
   }
   BSDFSample(RNG &rng) {
      uDir[0] = rng.RandomFloat();
      uDir[1] = rng.RandomFloat();
      uComponent = rng.RandomFloat();
   }
   BSDFSample(const Sample *sample, const BSDFSampleOffsets &offsets, uint32_t num);
   BSDFSample() { }
   float uDir[2], uComponent;
};

作用:

(BSDF::Sample_f() 需要3個隨機數,1個用來 在 BSDF中 隨機選擇 某一個BxDF,2個用來 傳入 BxDF->Sample_f 的函式中,因為BSDF::Sample_f 是隨機取樣,所以會隨機取樣其中1個 它自己儲存的BxDF,那麼這3個隨機數 直接 封裝到 BSDFSample 中。)

The BSDF::Sample_f() method takes three random variables to drive this process; one is
used to select among the BxDFs, and the other two to pass along to the particular sampling
method chosen. These sample values are encapsulated by the BSDFSample structure.

 

1. float uDir[2], uComponent;

作用:

uDir[2] : 在 BSDF::Sample_f() 中,是直接給 BxDF::Sample_f() 使用。

uComponent :這個就是用來 隨機取樣 BSDF 中某一個 BxDF 用的。

 

2. 建構函式

   BSDFSample(float up0, float up1, float ucomp) {
       Assert(up0 >= 0.f && up0 < 1.f);
       Assert(up1 >= 0.f && up1 < 1.f);
       Assert(ucomp >= 0.f && ucomp < 1.f);
       uDir[0] = up0;
       uDir[1] = up1;
       uComponent = ucomp;
   }
   BSDFSample(RNG &rng) {
      uDir[0] = rng.RandomFloat();
      uDir[1] = rng.RandomFloat();
      uComponent = rng.RandomFloat();
   }

作用:

(BSDFSample 的建構函式,1個建構函式是直接傳入3個隨機數,1個建構函式是直接 通過 rng 來生成隨機數)

The BSDFSample can be initialized by directly passing in values for the three samples.

Alternatively, a convenience method takes a pseudo-random number generator and initializes
the samples using it.

 

3. 常用的建構函式

BSDFSample::BSDFSample(const Sample *sample,
                       const BSDFSampleOffsets &offsets, uint32_t n) {
    Assert(n < sample->n2D[offsets.dirOffset]);
    Assert(n < sample->n1D[offsets.componentOffset]);
    uDir[0] = sample->twoD[offsets.dirOffset][2*n];
    uDir[1] = sample->twoD[offsets.dirOffset][2*n+1];
    uComponent = sample->oneD[offsets.componentOffset][n];
    Assert(uDir[0] >= 0.f && uDir[0] < 1.f);
    Assert(uDir[1] >= 0.f && uDir[1] < 1.f);
    Assert(uComponent >= 0.f && uComponent < 1.f);
}

作用:

(通過一個BSDFSampleOffsets 結構體來進行 初始化 BSDFSample , 這裡簡單理解就是,BSDFSampleOffsets  儲存了 sample 中 oneD,twoD 陣列的索引,也就是說 BSDFSample 是用過 BSDFSampleOffsets 結構體,取 sample 中的oneD,twoD的隨機資料)

Most commonly, we’ll want to initialize the BSDFSample with values from a Sample object
filled in by the Sampler. The BSDFSampleOffsets structure helps with the bookkeeping for
this case.

 

BSDFSampleOffsets

struct BSDFSampleOffsets {
    BSDFSampleOffsets() { }
    BSDFSampleOffsets(int count, Sample *sample);
    int nSamples, componentOffset, dirOffset;
};

BSDFSampleOffsets::BSDFSampleOffsets(int count, Sample *sample) {
    nSamples = count;
    componentOffset = sample->Add1D(nSamples);
    dirOffset = sample->Add2D(nSamples);
}

作用: 看 上面的程式碼可以發現, componentOffset, dirOffset 其實儲存的就是  sample->Add1D(nSamples),sample->Add2D(nSamples) 的返回值,就是儲存的就是 sample 對應 oneD,twoD 的陣列索引。

 

整理思路 BSDFSampleOffsets  和  BSDFSample 關係:

1. 在 《PBRT_V2 總結記錄 CameraSample 和 Sample》 中可以知道,Sample 的建構函式會 呼叫  SurfaceIntegrator->RequestSamples,就拿 DirectLightingIntegrator::RequestSamples 舉個例子:

void DirectLightingIntegrator::RequestSamples(Sampler *sampler,
        Sample *sample, const Scene *scene) {
    if (strategy == SAMPLE_ALL_UNIFORM) {
        // Allocate and request samples for sampling all lights
        uint32_t nLights = scene->lights.size();
        lightSampleOffsets = new LightSampleOffsets[nLights];
        bsdfSampleOffsets = new BSDFSampleOffsets[nLights];
        for (uint32_t i = 0; i < nLights; ++i) {
            const Light *light = scene->lights[i];
            int nSamples = light->nSamples;
            if (sampler) nSamples = sampler->RoundSize(nSamples);
            lightSampleOffsets[i] = LightSampleOffsets(nSamples, sample);
            bsdfSampleOffsets[i] = BSDFSampleOffsets(nSamples, sample);
        }
        lightNumOffset = -1;
    }
    else {
        // Allocate and request samples for sampling one light
        lightSampleOffsets = new LightSampleOffsets[1];
        lightSampleOffsets[0] = LightSampleOffsets(1, sample);
        lightNumOffset = sample->Add1D(1);
        bsdfSampleOffsets = new BSDFSampleOffsets[1];
        bsdfSampleOffsets[0] = BSDFSampleOffsets(1, sample);
    }
}


LightSampleOffsets *lightSampleOffsets;
BSDFSampleOffsets *bsdfSampleOffsets;

DirectLightingIntegrator::RequestSamples 中 直接 構建 BSDFSampleOffsets 陣列,在 BSDFSampleOffsets 的建構函式中,其實就是直接呼叫  sample->Add1D(nSamples),sample->Add2D(nSamples), 儲存了 sample oneD,twoD 的索引,供以後使用。

 

2. DirectLightingIntegrator::Li 中,直接呼叫 

 L += UniformSampleAllLights(scene, renderer, arena, p, n, wo,
                    isect.rayEpsilon, ray.time, bsdf, sample, rng,
                    lightSampleOffsets, bsdfSampleOffsets);

這裡是傳入了 bsdfSampleOffsets 陣列。

 

3. UniformSampleAllLights

Spectrum UniformSampleAllLights(const Scene *scene,
        const Renderer *renderer, MemoryArena &arena, const Point &p,
        const Normal &n, const Vector &wo, float rayEpsilon,
        float time, BSDF *bsdf, const Sample *sample, RNG &rng,
        const LightSampleOffsets *lightSampleOffsets,
        const BSDFSampleOffsets *bsdfSampleOffsets) {
    Spectrum L(0.);
    for (uint32_t i = 0; i < scene->lights.size(); ++i) {
        Light *light = scene->lights[i];
        int nSamples = lightSampleOffsets ?
                       lightSampleOffsets[i].nSamples : 1;
        // Estimate direct lighting from _light_ samples
        Spectrum Ld(0.);
        for (int j = 0; j < nSamples; ++j) {
            // Find light and BSDF sample values for direct lighting estimate
            LightSample lightSample;
            BSDFSample bsdfSample;
            if (lightSampleOffsets != NULL && bsdfSampleOffsets != NULL) {
                // i 是 光源的索引,j 是取樣點的索引
                lightSample = LightSample(sample, lightSampleOffsets[i], j);
                bsdfSample = BSDFSample(sample, bsdfSampleOffsets[i], j);
            }
            else {
                lightSample = LightSample(rng);
                bsdfSample = BSDFSample(rng);
            }
            Ld += EstimateDirect(scene, renderer, arena, light, p, n, wo,
                rayEpsilon, time, bsdf, rng, lightSample, bsdfSample,
                BxDFType(BSDF_ALL & ~BSDF_SPECULAR));
        }
        L += Ld / nSamples;
    }
    return L;
}





BSDFSample::BSDFSample(const Sample *sample,
                       const BSDFSampleOffsets &offsets, uint32_t n) {
    Assert(n < sample->n2D[offsets.dirOffset]);
    Assert(n < sample->n1D[offsets.componentOffset]);
    uDir[0] = sample->twoD[offsets.dirOffset][2*n];
    uDir[1] = sample->twoD[offsets.dirOffset][2*n+1];
    uComponent = sample->oneD[offsets.componentOffset][n];
    Assert(uDir[0] >= 0.f && uDir[0] < 1.f);
    Assert(uDir[1] >= 0.f && uDir[1] < 1.f);
    Assert(uComponent >= 0.f && uComponent < 1.f);
}

這裡是,引數有 BSDFSampleOffsets 陣列,那麼 這裡就是利用 bsdfSampleOffsets[i] 來直接 構造 bsdfsample, 就是利用 bsdfSampleOffset 中的  componentOffset, dirOffset 來作為索引,確定 sample->oneD 和 sample->twoD 對應資料。

 

總結來說,BSDFSampleOffsets 中的 componentOffset, dirOffset 儲存的是 Sample 中的oneD,twoD 某一組取樣點資料的 索引,之後 BSDFSample 通過利用 componentOffset, dirOffset 來取 Sample 中的oneD,twoD 的取樣點資料。

 

BSDF::Sample_f()


Spectrum BSDF::Sample_f(const Vector &woW, Vector *wiW,
                        const BSDFSample &bsdfSample, float *pdf,
                        BxDFType flags, BxDFType *sampledType) const {
    PBRT_STARTED_BSDF_SAMPLE();
    // Choose which _BxDF_ to sample
    int matchingComps = NumComponents(flags);
    if (matchingComps == 0) {
        *pdf = 0.f;
        if (sampledType) *sampledType = BxDFType(0);
        PBRT_FINISHED_BSDF_SAMPLE();
        return Spectrum(0.f);
    }
    int which = min(Floor2Int(bsdfSample.uComponent * matchingComps),
                    matchingComps-1);
    BxDF *bxdf = NULL;
    int count = which;
    for (int i = 0; i < nBxDFs; ++i)
        if (bxdfs[i]->MatchesFlags(flags) && count-- == 0) {
            bxdf = bxdfs[i];
            break;
        }
    Assert(bxdf);

    // Sample chosen _BxDF_
    Vector wo = WorldToLocal(woW);
    Vector wi;
    *pdf = 0.f;
    Spectrum f = bxdf->Sample_f(wo, &wi, bsdfSample.uDir[0],
                                bsdfSample.uDir[1], pdf);
    if (*pdf == 0.f)
    {
        if (sampledType) *sampledType = BxDFType(0);
        PBRT_FINISHED_BSDF_SAMPLE();
        return 0.f;
    }
    if (sampledType) *sampledType = bxdf->type;
    *wiW = LocalToWorld(wi);

    // Compute overall PDF with all matching _BxDF_s
    if (!(bxdf->type & BSDF_SPECULAR) && matchingComps > 1)
        for (int i = 0; i < nBxDFs; ++i)
            if (bxdfs[i] != bxdf && bxdfs[i]->MatchesFlags(flags))
                *pdf += bxdfs[i]->Pdf(wo, wi);
    if (matchingComps > 1) *pdf /= matchingComps;

    // Compute value of BSDF for sampled direction
    if (!(bxdf->type & BSDF_SPECULAR)) {
        f = 0.;
        if (Dot(*wiW, ng) * Dot(woW, ng) > 0) // ignore BTDFs
            flags = BxDFType(flags & ~BSDF_TRANSMISSION);
        else // ignore BRDFs
            flags = BxDFType(flags & ~BSDF_REFLECTION);
        for (int i = 0; i < nBxDFs; ++i)
            if (bxdfs[i]->MatchesFlags(flags))
                f += bxdfs[i]->f(wo, wi);
    }
        PBRT_FINISHED_BSDF_SAMPLE();
    return f;
}

作用:

(BSDF::Sample_f  跟其他的 BxDF::Sample_f 類似,但是不同的就是,傳進來的woW和計算的wiW都是基於世界座標的,額外的,同時也會返回pdf,給 Monte Carlo Integration 使用,這個pdf 是 所有與flags 相關的 BxDF的pdf的平均,返回值其實是 所有與flags 相關的 BxDF 的 f 的和)

the BSDF::Sample_f() method can now be implemented. The outgoing direction passed to it and the incident direction it returns are in world space, which is indicated by the W at the end of the respective variable names.

 

細節

a.

    int matchingComps = NumComponents(flags);
    if (matchingComps == 0) {
        *pdf = 0.f;
        if (sampledType) *sampledType = BxDFType(0);
        PBRT_FINISHED_BSDF_SAMPLE();
        return Spectrum(0.f);
    }
    int which = min(Floor2Int(bsdfSample.uComponent * matchingComps),
                    matchingComps-1);
    BxDF *bxdf = NULL;
    int count = which;
    for (int i = 0; i < nBxDFs; ++i)
        if (bxdfs[i]->MatchesFlags(flags) && count-- == 0) {
            bxdf = bxdfs[i];
            break;
        }

作用:

(這裡一開始,就用 bsdfSample.uComponent 來隨機選擇  1個 BxDF,)

This method first determines which BxDF’s sampling method to use for this particular
sample. This is complicated by the fact that the caller may pass in flags that the chosen
BxDF must match (e.g., specifying that only diffuse components should be considered).
Thus, only a subset of the sampling densities may actually be used here.

 

b.

    // Sample chosen _BxDF_
    Vector wo = WorldToLocal(woW);
    Vector wi;
    *pdf = 0.f;
    Spectrum f = bxdf->Sample_f(wo, &wi, bsdfSample.uDir[0],
                                bsdfSample.uDir[1], pdf);
    if (*pdf == 0.f)
    {
        if (sampledType) *sampledType = BxDFType(0);
        PBRT_FINISHED_BSDF_SAMPLE();
        return 0.f;
    }
    if (sampledType) *sampledType = bxdf->type;
    *wiW = LocalToWorld(wi);

作用:

(之前選擇了一個BxDF了,那麼這裡就是利用 BxDF::Sample_f 來計算 pdf 和 wi,之後再把wi 變換到 世界空間中,儲存到wiW中)

Once the appropriate BxDF is chosen, its BxDF::Sample_f() method is called. Recall that
these methods expect and return vectors in the BxDF’s local coordinate system, so the
supplied vector must be transformed to the BxDF’s coordinate system and the returned
vector must be transformed back into world coordinates.

 

c.

    // Compute overall PDF with all matching _BxDF_s
    if (!(bxdf->type & BSDF_SPECULAR) && matchingComps > 1)
        for (int i = 0; i < nBxDFs; ++i)
            if (bxdfs[i] != bxdf && bxdfs[i]->MatchesFlags(flags))
                *pdf += bxdfs[i]->Pdf(wo, wi);
    if (matchingComps > 1) *pdf /= matchingComps;

作用:

(上面的BxDF::Sample_f 計算的 pdf 其實就是某一個BxDF的pdf,如果要計算這個BSDF的pdf的話, 程式碼的做法就是平均 其他除BSDF_SPECULAR 的BxDF的pdf)

To compute the actual PDF for sampling the direction ωi, we need the average of all of the
PDFs of the BxDFs that could have been used, given the BxDFType flags passed in.
Because
*pdf already holds the PDF value for the distribution the sample was taken from, we only
need to add in the contributions of the others. It’s important that this step be skipped if
the chosen BxDF is perfectly specular, since the PDF has an implicit delta distribution in
it. It would be incorrect to add the other PDF values to this one, since it is a delta term
represented with value one, rather than as an actual delta distribution.

 

d.

    if (!(bxdf->type & BSDF_SPECULAR)) {
        f = 0.;
        if (Dot(*wiW, ng) * Dot(woW, ng) > 0) // ignore BTDFs
            flags = BxDFType(flags & ~BSDF_TRANSMISSION);
        else // ignore BRDFs
            flags = BxDFType(flags & ~BSDF_REFLECTION);
        for (int i = 0; i < nBxDFs; ++i)
            if (bxdfs[i]->MatchesFlags(flags))
                f += bxdfs[i]->f(wo, wi);
    }

作用:

(看 《PBRT_V2 總結記錄 BSDF》,可以看到 BSDF的 f 相關的內容,這裡非常類似的,所有與flags 相關的 BxDF 的 f 的和)

Given the sampled direction, this method needs to compute the value of the BSDF
for the pair of directions (ωo, ωi) accounting for all of the relevant components in the

BSDF, unless the sampled direction was from a specular component, in which case the
value returned from Sample_f() earlier is used. (If a specular component generated this
direction, its BxDF::f() method will return black, even if we pass back the direction its
sampling routine returned.)

While this method could just call the BSDF::f() method to compute the BSDF’s value,
the value can be more efficiently computed by calling the BxDF::f() methods directly,
taking advantage of the fact that here we already have the directions in both world space
and the reflection coordinate system available.

 

BSDF::Pdf

float BSDF::Pdf(const Vector &woW, const Vector &wiW,
        BxDFType flags) const {
    if (nBxDFs == 0.) return 0.;
    PBRT_STARTED_BSDF_PDF();
    Vector wo = WorldToLocal(woW), wi = WorldToLocal(wiW);
    float pdf = 0.f;
    int matchingComps = 0;
    for (int i = 0; i < nBxDFs; ++i)
        if (bxdfs[i]->MatchesFlags(flags)) {
            ++matchingComps;
            pdf += bxdfs[i]->Pdf(wo, wi);
        }
    float v = matchingComps > 0 ? pdf / matchingComps : 0.f;
    PBRT_FINISHED_BSDF_PDF();
    return v;
}

作用:

(平均所有的 與 flags 關聯的 BxDF的Pdf)

Given these methods to sample individual BxDFs, we can now define a sampling method
for the BSDF class, BSDF::Sample_f(). This method is called by SurfaceIntegrators
when they want to sample according to the BSDF’s distribution; it calls the individual
BxDF::Sample_f() methods to generate samples. The BSDF stores pointers to one or more
individual BxDFs that can be sampled individually, but here we will sample from the
density that is the sum of their individual densities,