PBRT_V2 总结记录 PhotonIntegrator

PhotonIntegrator

void Preprocess(const Scene *scene, const Camera *camera, const Renderer *renderer); 

思路:

1.  光源发出多条Ray,拿一条 Ray 作为例子,这条 Ray  与 场景的物体相交得到交点 P0,那么 就会 产生一个 Photon,这个就是所谓的光子,这个 Photon 的结构体就是:

struct Photon {
    Photon(const Point &pp, const Spectrum &wt, const Vector &w)
        : p(pp), alpha(wt), wi(w) { }
    Photon() { }
    Point p;
    Spectrum alpha;
    Vector wi;
};

Point p 是交点坐标.

alpha :   ,光子的 weight

wi : 保存的就是    -ray.d,也就是 光源发出的Ray的反方向,记录这个光子是从哪一个方向来的。

光子还会 根据碰撞的BSDF  来区分是 直接光的光子,还是间接光的光子,还是焦散光的光子。

产生出来的光子也会各自保存到 对应的 vector<Photon> causticPhotons, directPhotons, indirectPhotons 中。

(If this is the first intersection found after the particle has left the light source, then the
photon represents direct illumination. Otherwise, if it has only reflected from specular
surfaces before arriving at the current intersection point, itmust be a caustic photon. Any
other case—a path that only hit nonspecular surfaces or a path that hit a series of specular
surfaces before scattering from nonspecular surfaces—represents indirect illumination.
The finite-state machine diagram in Figure 15.20 illustrates the possibilities. The nodes of
the graph show which of the photon maps should be updated for a photon in that state,
and the edges describe whether the photon has scattered from a specular or a nonspecular
BSDF component at its previous intersection.)

2. 当 Ray  与 场景的物体相交得到交点 P0 之后,再 与其他的物体碰撞得到交点P1的时候,那么这个时候也会 产生一个Photon,不过这个时候的 Photon的alpha就变成了:

Spectrum anew = alpha * fr * AbsDot(wi, photonBSDF->dgShading.nn) / pdf;

(就是 相当对于上一个 alpha 乘多了 f * cos / pdf)

对于 P3 交点,道理也是一样的。

3. 最后利用  vector<Photon> causticPhotons, directPhotons, indirectPhotons

得到 directMap,causticMap,indirectMap

KdTree<Photon> *directMap = NULL;
    if (directPhotons.size() > 0)
        directMap = new KdTree<Photon>(directPhotons);
    if (causticPhotons.size() > 0)
        causticMap = new KdTree<Photon>(causticPhotons);
    if (indirectPhotons.size() > 0)
        indirectMap = new KdTree<Photon>(indirectPhotons);

4. 在收集 causticPhotons, directPhotons, indirectPhotons 光子的时候,同时也会去收集 RadiancePhoton,随机收集(也就是每一次光源的射线与场景物体进行相交的时候,都会随机的几率生成RadiancePhoton, 但是这个时候还没有计算Lo,等到所有的光子都生成完了,才会进行计算,因为Lo依赖光子, 同时也会收集 交点BSDF的 rho (BSDF_ALL_REFLECTION 和 BSDF_ALL_TRANSMISSION),因为计算Lo的时候要用到)

struct RadiancePhoton {
    RadiancePhoton(const Point &pp, const Normal &nn)
        : p(pp), n(nn), Lo(0.f) { }
    RadiancePhoton() { }
    Point p;
    Normal n;
    Spectrum Lo;
};

Point p 是交点坐标。

Normal n 是交点的法线。

Spectrum Lo 表示的就是,这个交点每一个方向会 反射出多少 radiance。

因为所以光子已经准备就绪了,所以就可以计算 每一个 收集好的 RadiancePhoton 的Lo,所有的 RadiancePhoton都是保存到 radiancePhotons 中,最后生成 radianceMap = new KdTree<RadiancePhoton>(radiancePhotons);

怎么计算 RadiancePhoton的Lo呢,最主要涉及到的计算Lo的公式:

    for (uint32_t i = rpStart; i < rpEnd; ++i) {
        // Compute radiance for radiance photon _i_
        RadiancePhoton &rp = radiancePhotons[i];
        const Spectrum &rho_r = rpReflectances[i], &rho_t = rpTransmittances[i];
        if (!rho_r.IsBlack()) {
            // Accumulate outgoing radiance due to reflected irradiance
            Spectrum E = EPhoton(directMap, nDirectPaths, nLookup, lookupBuf,
                                 maxDistSquared, rp.p, rp.n) +
                         EPhoton(indirectMap, nIndirectPaths, nLookup, lookupBuf,
                                 maxDistSquared, rp.p, rp.n) +
                         EPhoton(causticMap, nCausticPaths, nLookup, lookupBuf,
                                 maxDistSquared, rp.p, rp.n);
            rp.Lo += INV_PI * rho_r * E;
        }
        if (!rho_t.IsBlack()) {
            // Accumulate outgoing radiance due to transmitted irradiance
            Spectrum E = EPhoton(directMap, nDirectPaths, nLookup, lookupBuf,
                                 maxDistSquared, rp.p, -rp.n) +
                         EPhoton(indirectMap, nIndirectPaths, nLookup, lookupBuf,
                                 maxDistSquared, rp.p, -rp.n) +
                         EPhoton(causticMap, nCausticPaths, nLookup, lookupBuf,
                                 maxDistSquared, rp.p, -rp.n);
            rp.Lo += INV_PI * rho_t * E;
        }
    }

这里涉及到的就是EPhoton 是怎么计算的,之后会介绍,主要理解,在EPhoton中,利用Photon进行计算E值。

The EPhoton() utility function estimates the incident irradiance at the given point with
the given surface normal using the photons in the photon map

总结:

在Preprocess 中,收集3种Photons(光源发出射线,射线与场景物体的相交会生成一个 Photon), 保存到  causticPhotons, directPhotons, indirectPhotons,再利用 这些 Photons 去创建  radianceMap ,radianceMap 保存的就是 RadiancePhoton 集合,RadiancePhoton 保存的就是交点BSDF 对于每一个方向反射的Radiance, 之后的光照会用到这个预先计算好的 Photons 和 RadiancePhoton。

Spectrum PhotonIntegrator::Li(const Scene *scene, const Renderer *renderer,
        const RayDifferential &ray, const Intersection &isect,
        const Sample *sample, RNG &rng, MemoryArena &arena)

思路: 在 Li 中主要是 处理3种光照,1种是 直接光照,1 种是 焦散光照(caustic ),1种是 间接光照

1. 对应这三种光照,最简单的处理就是:

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

// 处理直接光照
L += UniformSampleAllLights(scene, renderer, arena, p, n,
        wo, isect.rayEpsilon, ray.time, bsdf, sample, rng,
        lightSampleOffsets, bsdfSampleOffsets);

...
// Compute caustic lighting for photon map integrator
// 处理焦散效果
ClosePhoton *lookupBuf = arena.Alloc<ClosePhoton>(nLookup);
L += LPhoton(causticMap, nCausticPaths, nLookup, lookupBuf, bsdf,
             rng, isect, wo, maxDistSquared);


//处理间接光照
L += LPhoton(indirectMap, nIndirectPaths, nLookup, lookupBuf,
                     bsdf, rng, isect, wo, maxDistSquared);

其中 LPhoton 函数:(提供 BSDF和 photon map,计算 wo方向的 reflected radiance )

The LPhoton() function, which will be described in Section 15.6.5, computes reflected
radiance in the direction wo for the given BSDF and photon map.

2. 对于间接光照的计算,除了使用 LPhoton之外,还提供了一种 Final Gather 的算法思路

总体思路: 获得 一个交点的 所有入射的间接光,才可以计算出 它对某一个方向的间接反射光,涉及到的公式就是:

如果用Monte Carlo integration 的方式去估算这个 积分 :

Final Gather 使用两种采样方式,1种是对BSDF进行采样,得到采样方向,1种是 基于 交点附近的 Photon ,在 Photon 的入射光照范围进行采样,得到采样方向,把这个采样方向作为 这个交点的 间接光入射方向。

结合这两种方法,使用MIS: 

a. 

对BSDF进行采样

利用 bsdf->Sample_f, 获得 wi, 这个wi 就是这个交点 的采样ray 的方向,得到 

RayDifferential bounceRay(p, wi, ray, isect.rayEpsilon);

利用

scene->Intersect(bounceRay, &gatherIsect)

来检查这条 采样Ray是否碰撞到 场景物体了,获得一个 新的交点,这个新的交点可以用来 查找 出一个和它最近的RadiancePhoton,获得这个RadiancePhoton.Lo,作为这条采样Ray 采样回来的 入射 Radiance,那就是 上面的公式的Ld,入射光。

b.

对 photon 的入射范围进行采样

先 获得这个交点 附近 最近 的 Photon 集合,

在这个 Photon 集合里面随机选择 一个Photon,

Photon 根据自己的坐标系,随机采样一个入射方向: 如 Figure 15.25

Vector vx, vy;
CoordinateSystem(photonDirs[photonNum], &vx, &vy);
Vector wi = UniformSampleCone(gatherSample.uDir[0], gatherSample.uDir[1], cosGatherAngle, vx,vy,photonDirs[photonNum]);

那么,得到了Photon的采样入射方向,那么就可以像上面BSDF一样的思路:

RayDifferential bounceRay(p, wi, ray, isect.rayEpsilon);

scene->Intersect(bounceRay, &gatherIsect)

来检查这条 采样Ray是否碰撞到 场景物体了,获得一个 新的交点,这个新的交点可以用来 查找 出一个和它最近的RadiancePhoton,获得这个RadiancePhoton.Lo,作为这条采样Ray 采样回来的 入射 Radiance

总结:其实就是 利用BSDF和Photon 来采样方向,形成不同的Ray,Ray 就可以 碰撞场景,获得不同的 RadiancePhoton.Lo, RadiancePhoton.Lo 就是相当于这个交点的入射光。

When final gathering is being used to estimate reflected radiance to indirect illumination,
we’d like to compute the value of the term

As usual, in order to apply Monte Carlo integration for estimating
this value, we need to sample incident directions ωi from a distribution that we hope
matches the shape of the integrand. The implementation here uses two sampling distributions:
one based on the BSDF and one based on an approximation to the incident
radiance function Li, i that is constructed from the indirect illumination photons around
the point p.

Given a sampled direction, the incident radiance Li, i(p, ωi) is found by tracing a ray to
find the closest intersection and computing the outgoing radiance there using the single
closest RadiancePhoton. Figure 15.23 illustrates the basic concept of final gathering,

Figure 15.23: The final gathering step with photon mapping traces a set of rays to sample incident
radiance at p in order to compute exitant radiance there. However, rather than continuing recursively
and tracing more rays at the gather intersection points, the radiance photons are immediately used
to compute the outgoing radiance there.

The photons around a point in the scene are not just useful for estimating the incident
radiance distribution at the point in order to compute the outgoing reflected radiance:
in their incident directions, they also carry useful information about the directional
distribution of illumination at the point. This information can be put to use to define
a sampling distribution for the final gathering rays that tries to match the distribution of
indirect illumination. Figure 15.25 shows a plot of the incident directions of 50 photons
around a point on the floor of the example scene; note that most of them have incident
directions along a relatively similar set of directions.

Figure 15.25: Incident directions of 50 photons around a point on the floor of the example scene.
Many of the photons are arriving from a cluster of nearby directions. These directions can be used to
define a distribution for importance sampling that approximates the distribution of indirect illumination
at the point.

3. 补充LPhoton() EPhoton()  (  在PBRT中:P827 )

LPhoton(), computes the reflected radiance at a point with a given BSDF due to incident illumination. the exitant radiance at the point p in direction ω, is given by

这里的 a 就是上面的photon.alpha。

inline float kernel(const Photon *photon, const Point &p,
                    float maxDist2) {
    float s = (1.f - DistanceSquared(photon->p, p) / maxDist2);
    return 3.f * INV_PI * s * s;
}
        

Spectrum LPhoton(KdTree<Photon> *map, int nPaths, int nLookup,
      ClosePhoton *lookupBuf, BSDF *bsdf, RNG &rng,
      const Intersection &isect, const Vector &wo, float maxDist2) {
    Spectrum L(0.);
    BxDFType nonSpecular = BxDFType(BSDF_REFLECTION |
        BSDF_TRANSMISSION | BSDF_DIFFUSE | BSDF_GLOSSY);
    if (map && bsdf->NumComponents(nonSpecular) > 0) {
        PBRT_PHOTON_MAP_STARTED_LOOKUP(const_cast<DifferentialGeometry *>(&isect.dg));
        // Do photon map lookup at intersection point
        PhotonProcess proc(nLookup, lookupBuf);
        map->Lookup(isect.dg.p, proc, maxDist2);

        // Estimate reflected radiance due to incident photons
        ClosePhoton *photons = proc.photons;
        int nFound = proc.nFound;
        Normal Nf = Faceforward(bsdf->dgShading.nn, wo);
        if (bsdf->NumComponents(BxDFType(BSDF_REFLECTION |
                BSDF_TRANSMISSION | BSDF_GLOSSY)) > 0) {
            // Compute exitant radiance from photons for glossy surface
            for (int i = 0; i < nFound; ++i) {
                const Photon *p = photons[i].photon;
                float k = kernel(p, isect.dg.p, maxDist2);
                L += (k / (nPaths * maxDist2)) * bsdf->f(wo, p->wi) *
                     p->alpha;
            }
        }
        else {
            // Compute exitant radiance from photons for diffuse surface
            Spectrum Lr(0.), Lt(0.);
            for (int i = 0; i < nFound; ++i) {
                if (Dot(Nf, photons[i].photon->wi) > 0.f) {
                    float k = kernel(photons[i].photon, isect.dg.p, maxDist2);
                    Lr += (k / (nPaths * maxDist2)) * photons[i].photon->alpha;
                }
                else {
                    float k = kernel(photons[i].photon, isect.dg.p, maxDist2);
                    Lt += (k / (nPaths * maxDist2)) * photons[i].photon->alpha;
                }
            }
            L += Lr * bsdf->rho(wo, rng, BSDF_ALL_REFLECTION) * INV_PI +
                 Lt * bsdf->rho(wo, rng, BSDF_ALL_TRANSMISSION) * INV_PI;
        }
        PBRT_PHOTON_MAP_FINISHED_LOOKUP(const_cast<DifferentialGeometry *>(&isect.dg),
            proc.nFound, proc.nLookup, &L);
    }
    return L;
}

The second, EPhoton(), computes the incident irradiance at a point.

Spectrum EPhoton(KdTree<Photon> *map, int count, int nLookup,
        ClosePhoton *lookupBuf, float maxDist2, const Point &p,
        const Normal &n) {
    if (!map) return 0.f;
    // Lookup nearby photons at irradiance computation point
    PhotonProcess proc(nLookup, lookupBuf);
    float md2 = maxDist2;
    map->Lookup(p, proc, md2);
    Assert(md2 > 0.f);

    // Accumulate irradiance value from nearby photons
    if (proc.nFound == 0) return Spectrum(0.f);
    ClosePhoton *photons = proc.photons;
    Spectrum E(0.);
    for (uint32_t i = 0; i < proc.nFound; ++i)
        if (Dot(n, photons[i].photon->wi) > 0.)
            E += photons[i].photon->alpha;
    return E / (count * md2 * M_PI);
}

猜你喜欢

转载自blog.csdn.net/aa20274270/article/details/85544277