PBRT_V2 总结记录 <99> IGIIntegrator

本文介绍了PBRT_V2中的IGIIntegrator,这是一种减少采样噪声的集成器。它通过从光源出发生成虚拟光源来模拟间接光照,以实现更快的图像渲染。在Preprocess阶段,它根据光源功率创建分布,并在后续计算中随机选择光源发射光线,形成虚拟光源。通过对每个交点的BRDF和PDF的计算,构建虚拟光源的辐射贡献,模拟间接照明效果。
摘要由CSDN通过智能技术生成

IGIIntegrator 类


// IGIIntegrator Declarations
class IGIIntegrator : public SurfaceIntegrator {
public:
    // IGIIntegrator Public Methods
    ~IGIIntegrator();
    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);
    void Preprocess(const Scene *, const Camera *, const Renderer *);
    IGIIntegrator(uint32_t nl, uint32_t ns, float rrt, int maxd, float gl, int ng) {
        nLightPaths = RoundUpPow2(nl);
        nLightSets = RoundUpPow2(ns);
        rrThreshold = rrt;
        maxSpecularDepth = maxd;
        virtualLights.resize(nLightSets);
        gLimit = gl;
        nGatherSamples = ng;
        lightSampleOffsets = NULL;
        bsdfSampleOffsets = NULL;
    }
private:
    // IGIIntegrator Private Data

    // Declare sample parameters for light source sampling
    LightSampleOffsets *lightSampleOffsets;
    BSDFSampleOffsets *bsdfSampleOffsets;
    uint32_t nLightPaths, nLightSets;
    float gLimit;
    int nGatherSamples;
    float rrThreshold;
    int maxSpecularDepth;
    int vlSetOffset;
    BSDFSampleOffsets gatherSampleOffset;
    vector<vector<VirtualLight> > virtualLights;
};

类的作用:

(由于 path-tracing integrator 的每一个pixel 通过大量的 samples ,才可以保证 image 是没有 噪点的,大量的samples涉及大量的计算,所以,就引入一些其他的 integrator,实现 少samples 也可以没有噪点,那么 instant global illumination 就是其中的这样一个integrator, instant global illumination 的思路就是:从光源 射出 ray,光源的ray 与场景进行碰撞,碰撞点 生成一个 vritual light source,那么在场景中会生成很多这样的 vritual light source,这些 vritual light source 会 模拟 间接光源,integrator 在 计算一个点的 radiance的时候,也会考虑这样 virtual light source)

The path-tracing integrator computes unbiased estimates of the scene radiance, though
at a cost of requiring from tens to thousands of samples in each pixel in order to compute
an image that isn’t noisy. This computational cost can be undesirable;

“instant global illumination” (IGI) integrator. The basic idea is
to follow a small number of light-carrying paths from the light sources and to construct
a number of point light sources at the points where these paths intersect surfaces in the
scene. These point sources (also sometimes called virtual light sources) are deposited in
a way such that their illumination approximates the indirect radiance distribution in the
scene. After these lights have been created, when the integrator later needs to compute
exitant radiance at a point, it loops over these point light sources and traces shadow rays
to see if they are visible, accumulating the indirect illumination that they represent if so.

 

Figure 15.11: Connecting Paths with the IGIIntegrator. With the “instant global illumination”
algorithm, a number of light-carrying paths are constructed from the light sources; the virtual lights
created by such a path are indicated with filled circles here. When a point is being shaded (open
circle), shadow rays (dashed lines) are traced between the point and the vertices of the light path to
compute indirect illumination. Shadow rays to the light source itself are handled separately, using the
direct lighting techniques from Section 15.1.

 

1.  void IGIIntegrator::Preprocess(const Scene *scene, const Camera *camera, const Renderer *renderer) 中执行


void IGIIntegrator::Preprocess(const Scene *scene, const Camera *camera,
                               const Renderer *renderer) {
    if (scene->lights.size() == 0) return;
    MemoryArena arena;
    RNG rng;
    // Compute samples for emitted rays from lights
    vector<float> lightNum(nLightPaths * nLightSets);
    vector<float> lightSampPos(2 * nLightPaths * nLightSets, 0.f);
    vector<float> lightSampComp(nLightPaths * nLightSets, 0.f);
    vector<float> lightSampDir(2 * nLightPaths * nLightSets, 0.f);
    LDShuffleScrambled1D(nLightPaths, nLightSets, &lightNum[0], rng);
    LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampPos[0], rng);
    LDShuffleScrambled1D(nLightPaths, nLightSets, &lightSampComp[0], rng);
    LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampDir[0], rng);

    // Precompute information for light sampling densities
    Distribution1D *lightDistribution = ComputeLightSamplingCDF(scene);
    for (uint32_t s = 0; s < nLightSets; ++s) {
        for (uint32_t i = 0; i < nLightPaths; ++i) {
            // Follow path _i_ from light to create virtual lights
            int sampOffset = s*nLightPaths + i;

            // Choose light source to trace virtual light path from
            float lightPdf;
            int ln = lightDistribution->SampleDiscrete(lightNum[sampOffset],
                                                       &lightPdf);
            Light *light = scene->lights[ln];

            // Sample ray leaving light source for virtual light path
            RayDifferential ray;
            float pdf;
            LightSample ls(lightSampPos[2*sampOffset], lightSampPos[2*sampOffset+1],
                           lightSampComp[sampOffset]);
            Normal Nl;
            Spectrum alpha = light->Sample_L(scene, ls, lightSampDir[2*sampOffset],
                                             lightSampDir[2*sampOffset+1],
                                             camera->shutterOpen, &ray, &Nl, &pdf);
            if (pdf == 0.f || alpha.IsBlack()) continue;
            alpha *= AbsDot(Nl, ray.d) / (pdf * lightPdf);
            Intersection isect;
            while (scene->Intersect(ray, &isect) && !alpha.IsBlack()) {
                // Create virtual light and sample new ray for path
                alpha *= renderer->Transmittance(scene, RayDifferential(ray), NULL,
                                                 rng, arena);
                Vector wo = -ray.d;
                BSDF *bsdf = isect.GetBSDF(ray, arena);

                // Create virtual light at ray intersection point
                Spectrum contrib = alpha * bsdf->rho(wo, rng) / M_PI;
                virtualLights[s].push_back(VirtualLight(isect.dg.p, isect.dg.nn, contrib,
                                                        isect.rayEpsilon));

                // Sample new ray direction and update weight for virtual light path
                Vector wi;
                float pdf;
                BSDFSample bsdfSample(rng);
                Spectrum fr = bsdf->Sample_f(wo, &wi, bsdfSample, &pdf);
                if (fr.IsBlack() || pdf == 0.f)
                    break;
                Spectrum contribScale = fr * AbsDot(wi, bsdf->dgShading.nn) / pdf;

                // Possibly terminate virtual light path with Russian roulette
                float rrProb = min(1.f, contribScale.y());
                if (rng.RandomFloat() > rrProb)
                    break;
                alpha *= contribScale / rrProb;
                ray = RayDifferential(isect.dg.p, wi, ray, isect.rayEpsilon);
            }
            arena.FreeAll();
        }
    }
    delete lightDistribution;
}

作用:

构造  virtual light source,

细节:

a.

在《PBRT_V2 总结记录 <96> Integrator 和 SurfaceIntegrator》中可以 了解到 Integrator::Preprocess  ,integrator 提供的 Preprocess 是可选 实现的,这个函数在 Scene 完全 初始化之后调用,主要就是提供一个机会给 Integrator 来做一些 依赖场景的相关初始化操作。

 

b.

// Compute samples for emitted rays from lights
    vector<float> lightNum(nLightPaths * nLightSets);
    vector<float> lightSampPos(2 * nLightPaths * nLightSets, 0.f);
    vector<float> lightSampComp(nLightPaths * nLightSets, 0.f);
    vector<float> lightSampDir(2 * nLightPaths * nLightSets, 0.f);
    LDShuffleScrambled1D(nLightPaths, nLightSets, &lightNum[0], rng);
    LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampPos[0], rng);
    LDShuffleScrambled1D(nLightPaths, nLightSets, &lightSampComp[0], rng);
    LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampDir[0], rng);

作用:

(上面的 lightNum,lightSampPos,lightSampComp,lightSampDir,这些变量主要就是保存好一些 分布均匀的采样点[0,1],主要用于下面去初始化 ray的。)

To ensure a good sampling of indirect light, it’s helpful to use well-distributed samples
to choose the initial points on the light sources and their directions.
The LDShuffle
Scrambled*D() functions work well to compute samples for generating the initial rays
from the lights. Using them in this manner ensures not only that the samples used for
each particular set of virtual lights are well distributed but also that in the aggregate over
all of the paths the samples are globally well distributed as well.

 

c.

    // Precompute information for light sampling densities
    Distribution1D *lightDistribution = ComputeLightSamplingCDF(scene);

作用:

(这个 ComputeLightSamplingCDF  主要作用就是根据光源的Power来创建一个 Distribution1D,这个Distribution1D主要就是用于进行随机采样哪一个光源进行发射射线)

The efficiency of the algorithm is also improved if the probability of starting a path from a
light source is related to the amount of power it emits in comparison to the power emitted
by the other lights. (The intensity of the corresponding virtual lightsmust be reduced appropriately
to counterbalance the fact that more virtual lights are created for bright light
sources.) Increasing the probability of generating paths from bright lights naturally leads
to more virtual lights being created for those lights. The ComputeLightSamplingCDF()
routine computes a discrete probability density for sampling each light according to its
power, returning a Distribution1D to represent the distribution.

 

c.

            float lightPdf;
            int ln = lightDistribution->SampleDiscrete(lightNum[sampOffset],
                                                       &lightPdf);
            Light *light = scene->lights[ln];

            // Sample ray leaving light source for virtual light path
            RayDifferential ray;
            float pdf;
            LightSample ls(lightSampPos[2*sampOffset], lightSampPos[2*sampOffset+1],
                           lightSampComp[sampOffset]);
            Normal Nl;
            Spectrum alpha = light->Sample_L(scene, ls, lightSampDir[2*sampOffset],
                                             lightSampDir[2*sampOffset+1],
                                             camera->shutterOpen, &ray, &Nl, &pdf);
            if (pdf == 0.f || alpha.IsBlack()) continue;
            alpha *= AbsDot(Nl, ray.d) / (pdf * lightPdf);

作用:

这段代码是在 循环体中执行的,

第一,先利用lightNum来随机采样一个光源出来,得到light,

第二,执行light->Sample_L,表示在光源上发出ray,这条ray 的radiance 保存到 alpha中,

第三 ,执行 alpha *= AbsDot(Nl, ray.d) / (pdf * lightPdf); 用alpha保存临时结果。

d.

            while (scene->Intersect(ray, &isect) && !alpha.IsBlack()) {
                // Create virtual light and sample new ray for path
                alpha *= renderer->Transmittance(scene, RayDifferential(ray), NULL,
                                                 rng, arena);
                Vector wo = -ray.d;
                BSDF *bsdf = isect.GetBSDF(ray, arena);

                // Create virtual light at ray intersection point
                Spectrum contrib = alpha * bsdf->rho(wo, rng) / M_PI;
                virtualLights[s].push_back(VirtualLight(isect.dg.p, isect.dg.nn, contrib,
                                                        isect.rayEpsilon));

                // Sample new ray direction and update weight for virtual light path
                Vector wi;
                float pdf;
                BSDFSample bsdfSample(rng);
                Spectrum fr = bsdf->Sample_f(wo, &wi, bsdfSample, &pdf);
                if (fr.IsBlack() || pdf == 0.f)
                    break;
                Spectrum contribScale = fr * AbsDot(wi, bsdf->dgShading.nn) / pdf;

                // Possibly terminate virtual light path with Russian roulette
                float rrProb = min(1.f, contribScale.y());
                if (rng.RandomFloat() > rrProb)
                    break;
                alpha *= contribScale / rrProb;
                ray = RayDifferential(isect.dg.p, wi, ray, isect.rayEpsilon);
            }

作用:

在ray 碰撞了场景的物体的时候,得到交点,那么执行  Spectrum contrib = alpha * bsdf->rho(wo, rng) / M_PI; 来计算这个 virtual light source 的 Le,

在上面可以知道 alpha = Le(光源的Radiance) *  AbsDot(Nl, ray.d) / (pdf * lightPdf);  

 

那么第一个 virtual light source 的计算:

那么 contrib = AbsDot(Nl, ray.d) / (pdf * lightPdf) * bsdf->rho(wo, rng) / M_PI

参考公式 :

其实  bsdf->rho(wo, rng) / M_PI 就相当于 f(BRDF),但是这个BRDF 是 a constant Lambertian BSDF based on the hemispherical–directional reflectance of the surface。

所以 个人理解就是,第一个 virtual light source 对每一个方向的 radiance 都是

contrib = Le * AbsDot(Nl, ray.d) / (pdf * lightPdf) * bsdf->rho(wo, rng) / M_PI  (这里知识计算 virtual Light 对每一个方向发射的 radiance) 

(?? 个人思考,这里的夹角,是不是应该是交点的法线与 光源射线的夹角,而且,pdf,是不是应该是 Lambertian的pdf,这才是计算这个交点某一个方向上的radiance??)

 

那么 计算第二个 virtual Light source,利用 交点的BRDF,

执行  Spectrum contribScale = fr * AbsDot(wi, bsdf->dgShading.nn) / pdf;  得到的是 第一个交点的 BRDF和对应的PDF。

执行 alpha *= contribScale / rrProb; 得到 

alpha = Le * AbsDot(Nl, ray.d) / (pdf * lightPdf) * fr * AbsDot(wi, bsdf->dgShading.nn) / pdf;   (注意这里没有 bsdf->rho(wo, rng) / M_PI)  在得到下一个交点的时候,alpha 才会去乘上  bsdf->rho(wo, rng) / M_PI。

那么,在计算第二个 virtual light source 的radiance的时候,就是:

alpha = Le * AbsDot(Nl, ray.d) / (pdf * lightPdf) * bsdf->rho(wo, rng) / M_PI) * fr * AbsDot(wi, bsdf->dgShading.nn) / pdf

表示:第一个交点的 在 方向 wo 上 射到 第二个交点的 radiance 是 Li =  Le * AbsDot(Nl, ray.d) / (pdf * lightPdf) * bsdf->rho(wo, rng) / M_PI), 那么第二个交点每一个方向的 radiance 的 就是 上面的

alpha = Le * AbsDot(Nl, ray.d) / (pdf * lightPdf) * bsdf->rho(wo, rng) / M_PI) * fr * AbsDot(wi, bsdf->dgShading.nn) / pdf 这么多,对于第三个交点,其实也是以此类推。

 

 

2. 


Spectrum IGIIntegrator::Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &ray, const Intersection &isect,
        const Sample *sample, RNG &rng, MemoryArena &arena) const {
    Spectrum L(0.);
    Vector wo = -ray.d;
    // Compute emitted light if ray hit an area light source
    L += isect.Le(wo);

    // Evaluate BSDF at hit point
    BSDF *bsdf = isect.GetBSDF(ray, arena);
    const Point &p = bsdf->dgShading.p;
    const Normal &n = bsdf->dgShading.nn;
    L += UniformSampleAllLights(scene, renderer, arena, p, n,
                    wo, isect.rayEpsilon, ray.time, bsdf, sample, rng,
                    lightSampleOffsets, bsdfSampleOffsets);
    // Compute indirect illumination with virtual lights
    uint32_t lSet = min(uint32_t(sample->oneD[vlSetOffset][0] * nLightSets),
                        nLightSets-1);
    for (uint32_t i = 0; i < virtualLights[lSet].size(); ++i) {
        const VirtualLight &vl = virtualLights[lSet][i];
        // Compute virtual light's tentative contribution _Llight_
        float d2 = DistanceSquared(p, vl.p);
        Vector wi = Normalize(vl.p - p);
        float G = AbsDot(wi, n) * AbsDot(wi, vl.n) / d2;
        G = min(G, gLimit);
        Spectrum f = bsdf->f(wo, wi);
        if (G == 0.f || f.IsBlack()) continue;
        Spectrum Llight = f * G * vl.pathContrib / nLightPaths;
        RayDifferential connectRay(p, wi, ray, isect.rayEpsilon,
                                   sqrtf(d2) * (1.f - vl.rayEpsilon));
        Llight *= renderer->Transmittance(scene, connectRay, NULL, rng, arena);

        // Possibly skip virtual light shadow ray with Russian roulette
        if (Llight.y() < rrThreshold) {
            float continueProbability = .1f;
            if (rng.RandomFloat() > continueProbability)
                continue;
            Llight /= continueProbability;
        }

        // Add contribution from _VirtualLight_ _vl_
        if (!scene->IntersectP(connectRay))
            L += Llight;
    }
    if (ray.depth < maxSpecularDepth) {
        // Do bias compensation for bounding geometry term
        int nSamples = (ray.depth == 0) ? nGatherSamples : 1;
        for (int i = 0; i < nSamples; ++i) {
            Vector wi;
            float pdf;
            BSDFSample bsdfSample = (ray.depth == 0) ?
                BSDFSample(sample, gatherSampleOffset, i) : BSDFSample(rng);
            Spectrum f = bsdf->Sample_f(wo, &wi, bsdfSample,
                                        &pdf, BxDFType(BSDF_ALL & ~BSDF_SPECULAR));
            if (!f.IsBlack() && pdf > 0.f) {
                // Trace ray for bias compensation gather sample
                float maxDist = sqrtf(AbsDot(wi, n) / gLimit);
                RayDifferential gatherRay(p, wi, ray, isect.rayEpsilon, maxDist);
                Intersection gatherIsect;
                Spectrum Li = renderer->Li(scene, gatherRay, sample, rng, arena,
                                           &gatherIsect);
                if (Li.IsBlack()) continue;

                // Add bias compensation ray contribution to radiance sum
                float Ggather = AbsDot(wi, n) * AbsDot(-wi, gatherIsect.dg.nn) /
                    DistanceSquared(p, gatherIsect.dg.p);
                if (Ggather - gLimit > 0.f && !isinf(Ggather)) {
                    float gs = (Ggather - gLimit) / Ggather;
                    L += f * Li * (AbsDot(wi, n) * gs / (nSamples * pdf));
                }
            }
        }
    }
    if (ray.depth + 1 < maxSpecularDepth) {
        Vector wi;
        // Trace rays for specular reflection and refraction
        L += SpecularReflect(ray, bsdf, rng, isect, renderer, scene, sample,
                             arena);
        L += SpecularTransmit(ray, bsdf, rng, isect, renderer, scene, sample,
                              arena);
    }
    return L;
}

作用:

这里 交一个 点 的 radiance 除了计算 直接光照之外,还会利用 virtual light source 来计算 间接光源。

 

细节

a.

    Spectrum L(0.);
    Vector wo = -ray.d;
    // Compute emitted light if ray hit an area light source
    L += isect.Le(wo);

    // Evaluate BSDF at hit point
    BSDF *bsdf = isect.GetBSDF(ray, arena);
    const Point &p = bsdf->dgShading.p;
    const Normal &n = bsdf->dgShading.nn;
    L += UniformSampleAllLights(scene, renderer, arena, p, n,
                    wo, isect.rayEpsilon, ray.time, bsdf, sample, rng,
                    lightSampleOffsets, bsdfSampleOffsets);

作用:

这里就是计算 直接 光照。

 

b.

    // Compute indirect illumination with virtual lights
    uint32_t lSet = min(uint32_t(sample->oneD[vlSetOffset][0] * nLightSets),
                        nLightSets-1);
    for (uint32_t i = 0; i < virtualLights[lSet].size(); ++i) {
        const VirtualLight &vl = virtualLights[lSet][i];
        // Compute virtual light's tentative contribution _Llight_
        float d2 = DistanceSquared(p, vl.p);
        Vector wi = Normalize(vl.p - p);
        float G = AbsDot(wi, n) * AbsDot(wi, vl.n) / d2;
        G = min(G, gLimit);
        Spectrum f = bsdf->f(wo, wi);
        if (G == 0.f || f.IsBlack()) continue;
        Spectrum Llight = f * G * vl.pathContrib / nLightPaths;
        RayDifferential connectRay(p, wi, ray, isect.rayEpsilon,
                                   sqrtf(d2) * (1.f - vl.rayEpsilon));
        Llight *= renderer->Transmittance(scene, connectRay, NULL, rng, arena);

        // Possibly skip virtual light shadow ray with Russian roulette
        if (Llight.y() < rrThreshold) {
            float continueProbability = .1f;
            if (rng.RandomFloat() > continueProbability)
                continue;
            Llight /= continueProbability;
        }

        // Add contribution from _VirtualLight_ _vl_
        if (!scene->IntersectP(connectRay))
            L += Llight;
    }

作用:

这里主要的作用就是随机选择一个virtual light source 计算间接光照,最主要的代码就是:

 Spectrum Llight = f * G * vl.pathContrib / nLightPaths;

vl.pathContrib : 就是在上面的 Preprocess 的 生成 virtual light source 的时候计算出来的,可以理解为 间接光源的 radiance.

 

上面的其实就是公式 : 

==

 

总结:

个人想法:上面的IGI 其实就是 说明一个 思路,可以利用 Virtual Light Source 来进行 间接光的 模拟,那么计算 Virtual Light Source 的Radiance的思路是可以修改,计算出  Virtual Light Source 的 Radiance ,怎么样去计算 场景中 点的 间接光也是可以根据具体的效果进行修改的,所以,主要是掌握思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值