PBRT_V2 总结记录 <98> PathIntegrator

本文详细介绍了PBRT_V2中PathIntegrator类的作用和工作原理,包括其如何利用采样点进行路径追踪,以及如何在路径的不同阶段应用随机数和多次重要性采样。通过逐步解析每次循环中光线与场景交互的过程,阐述了从光源到像素的辐射亮度计算方法,涉及到光的反射、透射和Russian roulette路径终止策略。

 

PathIntegrator


// PathIntegrator Declarations
class PathIntegrator : public SurfaceIntegrator {
public:
    // PathIntegrator Public Methods
    Spectrum Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &ray, const Intersection &isect,
        const Sample *sample, RNG &rng, MemoryArena &arena) const;
    void RequestSamples(Sampler *sampler, Sample *sample, const Scene *scene);
    PathIntegrator(int md) { maxDepth = md; }
private:
    // PathIntegrator Private Data
    int maxDepth;
#define SAMPLE_DEPTH 3
    LightSampleOffsets lightSampleOffsets[SAMPLE_DEPTH];
    int lightNumOffset[SAMPLE_DEPTH];
    BSDFSampleOffsets bsdfSampleOffsets[SAMPLE_DEPTH];
    BSDFSampleOffsets pathSampleOffsets[SAMPLE_DEPTH];
};

类的作用:

(路径追踪) 

 

1. 

void PathIntegrator::RequestSamples(Sampler *sampler, Sample *sample,
                                    const Scene *scene) {
    for (int i = 0; i < SAMPLE_DEPTH; ++i) {
        lightSampleOffsets[i] = LightSampleOffsets(1, sample);
        lightNumOffset[i] = sample->Add1D(1);
        bsdfSampleOffsets[i] = BSDFSampleOffsets(1, sample);
        pathSampleOffsets[i] = BSDFSampleOffsets(1, sample);
    }
}

作用:

(这里申请  SAMPLE_DEPTH 个采样数据,之后 超过 SAMPLE_DEPTH 就直接使用 随机数)

The integrator uses samples from the Sampler for sampling at the first SAMPLE_DEPTH
vertices of the path. After the first few bounces, the advantages of well-distributed sample
points are greatly reduced, and it switches to using uniform random numbers.
The
integrator needs light and BSDF samples formultiple importance sampling for the direct
lighting calculation at each vertex of the path as well as a second set of BSDF samples for
sampling directions when generating the outgoing direction for finding the next vertex
of the path.

 

2. 

Spectrum PathIntegrator::Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &r, const Intersection &isect,
        const Sample *sample, RNG &rng, MemoryArena &arena) const {
    // Declare common path integration variables
    Spectrum pathThroughput = 1., L = 0.;
    RayDifferential ray(r);
    bool specularBounce = false;
    Intersection localIsect;
    const Intersection *isectp = &isect;
    for (int bounces = 0; ; ++bounces) {
        // Possibly add emitted light at path vertex
        if (bounces == 0 || specularBounce)
            L += pathThroughput * isectp->Le(-ray.d);

        // Sample illumination from lights to find path contribution
        BSDF *bsdf = isectp->GetBSDF(ray, arena);
        const Point &p = bsdf->dgShading.p;
        const Normal &n = bsdf->dgShading.nn;
        Vector wo = -ray.d;
        if (bounces < SAMPLE_DEPTH)
            L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng,
                     lightNumOffset[bounces], &lightSampleOffsets[bounces],
                     &bsdfSampleOffsets[bounces]);
        else
            L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng);

        // Sample BSDF to get new path direction

        // Get _outgoingBSDFSample_ for sampling new path direction
        BSDFSample outgoingBSDFSample;
        if (bounces < SAMPLE_DEPTH)
            outgoingBSDFSample = BSDFSample(sample, pathSampleOffsets[bounces],
                                            0);
        else
            outgoingBSDFSample = BSDFSample(rng);
        Vector wi;
        float pdf;
        BxDFType flags;
        Spectrum f = bsdf->Sample_f(wo, &wi, outgoingBSDFSample, &pdf,
                                    BSDF_ALL, &flags);
        if (f.IsBlack() || pdf == 0.)
            break;
        specularBounce = (flags & BSDF_SPECULAR) != 0;
        pathThroughput *= f * AbsDot(wi, n) / pdf;
        ray = RayDifferential(p, wi, ray, isectp->rayEpsilon);

        // Possibly terminate the path
        if (bounces > 3) {
            float continueProbability = min(.5f, pathThroughput.y());
            if (rng.RandomFloat() > continueProbability)
                break;
            pathThroughput /= continueProbability;
        }
        if (bounces == maxDepth)
            break;

        // Find next vertex of path
        if (!scene->Intersect(ray, &localIsect)) {
            if (specularBounce)
                for (uint32_t i = 0; i < scene->lights.size(); ++i)
                   L += pathThroughput * scene->lights[i]->Le(ray);
            break;
        }
        pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena);
        isectp = &localIsect;
    }
    return L;
}

作用:

(上面的Li函数,主要的思路就是,第1,每一次循环,都会计算 下一个 vertex,和计算 整体的 光照值,最后还会通过BSDF计算一个最新的 ray 方向wi,利用 Russian roulette来终止循环(概率来确定是否终止循环))

Each time through the for loop of the integrator, the next vertex of the path is found
by intersecting the current ray with the scene geometry and computing the contribution
of the path to the overall radiance value with the direct lighting code. A new direction
is then chosen by sampling from the BSDF’s distribution at the last vertex of the path.
After a few vertices have been sampled, Russian roulette is used to randomly terminate
the path.

 

 

思路:

如图:

现在计算 p0 的 L,执行下面的代码,注意这里的相当于在 p1 点上进行计算。

 

1. 当 第一次循环,bounces = 0 ,pathThroughput = 1 的时候。

 

a. 经过:

    if (bounces == 0 || specularBounce)
            L += pathThroughput * isectp->Le(-ray.d);

得到:

L = Le(p1)

 

 b. 经过:

         L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng,
                     lightNumOffset[bounces], &lightSampleOffsets[bounces],
                     &bsdfSampleOffsets[bounces]);

UniformSampleOneLight 的作用: 计算 p1点 的半球面上的所有的入射角 对 方向 p1->p0 的 radiance

得到的p1点反射到方向p1->p0的radiance 为 Lr (p1->p0) 得到:

L = Le(p1) + Lr(p1->p0)

 

c. 经过 

        Vector wi;
        float pdf;
        BxDFType flags;
        Spectrum f = bsdf->Sample_f(wo, &wi, outgoingBSDFSample, &pdf,
                                    BSDF_ALL, &flags);
        if (f.IsBlack() || pdf == 0.)
            break;
        specularBounce = (flags & BSDF_SPECULAR) != 0;
        pathThroughput *= f * AbsDot(wi, n) / pdf;

pathThroughput  = f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)  (这里其实就是获得在p1处的BSDF和p1点的法线与向量p1->p2的夹角,为了之后的计算做准备,

 

d. 经过

        // Find next vertex of path
        if (!scene->Intersect(ray, &localIsect)) {
            if (specularBounce)
                for (uint32_t i = 0; i < scene->lights.size(); ++i)
                   L += pathThroughput * scene->lights[i]->Le(ray);
            break;
        }
        pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena);
        isectp = &localIsect;

得到 p2 交点,isectp = &localIsect 之后,下一次循环的 计算 就直接在点p2 上计算了。

 

 

2.  当第二次循环的时候,bounces = 1 ,pathThroughput  = f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)  的时候。

a.经过

         L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng,
                     lightNumOffset[bounces], &lightSampleOffsets[bounces],
                     &bsdfSampleOffsets[bounces]);

这里的UniformSampleOneLight 的作用: 计算 p2点 的半球面上的所有的入射角 对 方向 p2->p1 的 radiance

得到的p2点反射到方向p2->p1的radiance 为 Lr (p2->p1) ,这个时候 pathThroughput  = f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)得到:

L = Le(p1) + Lr(p1->p0) + f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1) * Lr (p2->p1)

上面看起来有点复杂,可以直接看最后一项: f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1) * Lr (p2->p1)

其实这里联系到 公式:

那么可以知道 R2 = f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1) * Lr (p2->p1) 其实表示的就是,p2点上的Lr(p2->p1) 射到 p1 点 中,p1 反射到 p0的radiance 是 R 这么多。简单理解就是,R2表示的就是 p2 点贡献到 p0 点上的radiance.(p2 点贡献了 Lr (p2->p1) 到 p1 点上,p1 点 就贡献了 R2 到 p0 点上 )

 

b.

        Vector wi;
        float pdf;
        BxDFType flags;
        Spectrum f = bsdf->Sample_f(wo, &wi, outgoingBSDFSample, &pdf,
                                    BSDF_ALL, &flags);
        if (f.IsBlack() || pdf == 0.)
            break;
        specularBounce = (flags & BSDF_SPECULAR) != 0;
        pathThroughput *= f * AbsDot(wi, n) / pdf;

pathThroughput  = f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)      *  f(p1, p2, p3) * cos(n2, p2->p3) / pdf(p2)

 

c.

        // Find next vertex of path
        if (!scene->Intersect(ray, &localIsect)) {
            if (specularBounce)
                for (uint32_t i = 0; i < scene->lights.size(); ++i)
                   L += pathThroughput * scene->lights[i]->Le(ray);
            break;
        }
        pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena);
        isectp = &localIsect;

得到 p3 交点,isectp = &localIsect 之后,下一次循环的 计算 就直接在点p3 上计算了。

 

2.  当第三次循环的时候,bounces = 2 ,

pathThroughput  = f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)      *   f(p1, p2, p3) * cos(n2, p2->p3) / pdf(p2)  的时候。

a.经过

         L += pathThroughput *
                 UniformSampleOneLight(scene, renderer, arena, p, n, wo,
                     isectp->rayEpsilon, ray.time, bsdf, sample, rng,
                     lightNumOffset[bounces], &lightSampleOffsets[bounces],
                     &bsdfSampleOffsets[bounces]);

这里的UniformSampleOneLight 的作用: 计算 p3点 的半球面上的所有的入射角 对 方向 p3->p2 的 radiance

得到的p3点反射到方向p3->p2的radiance 为 Lr (p3->p2) ,这个时候 

pathThroughput  = f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)      *   f(p1, p2, p3) * cos(n2, p2->p3) / pdf(p2) 

这里时候的L :

L = Le(p1) + Lr(p1->p0) + f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1) * Lr (p2->p1) + 

f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)      *   f(p1, p2, p3) * cos(n2, p2->p3) / pdf(p2)  * Lr (p3->p2)

直接看最后一项: f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)      *   f(p1, p2, p3) * cos(n2, p2->p3) / pdf(p2)  * Lr (p3->p2)

R3 = f(p0, p1, p2) * cos(n1, p1->p2) / pdf(p1)      *   f(p1, p2, p3) * cos(n2, p2->p3) / pdf(p2)  * Lr (p3->p2)

联系公式:

得到知道 R3 表示的就是:

p3 在方向 p3-> p2 上,贡献了 Lr (p3->p2) 给 p2,那么  p2 根据p3的贡献,在方向 p2->p1 贡献了   f(p1, p2, p3) * cos(n2, p2->p3) / pdf(p2)  * Lr (p3->p2) 给p1,p1 根据 p2 的贡献 ,在方向 p1 -> p0 贡献了 R3 给p0, 所以 R3 的意思就是, p3 贡献了多少radiance 给 p0.

 

之后的 循环都是以此类推。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值