<<Intersect ray with scene and store intersection in isect>>=
SurfaceInteraction isect; bool foundIntersection = scene.
Intersect(ray, &isect);
If the ray hits an object that is emissive, the emission is usually ignored, since the loop iteration at the previous path vertex performed a direct illumination estimate that already accounted for its effect. The same is true when a ray escapes into an emissive environment. However, there are two exceptions: the first is at the initial intersection point of camera rays, since this is the only opportunity to include emission from directly visible objects. The second is when the sampled direction from the last path vertex was from a specular BSDF component: in this case, the previous iteration’s direct illumination estimate could not evaluate the associated integrand containing a Dirac delta function, and we must account for it here.
<<Possibly add emitted light at intersection>>=
if (bounces == 0 || specularBounce) { <<
Add emitted light at path vertex or from the environment>> }
When no intersection is found, the ray has escaped the scene and thus the path sampling iteration terminates. Similarly, the iteration terminates when bounces exceeds the prescribed maximum value.
<<Terminate path if ray escaped or maxDepth was reached>>=
if (!foundIntersection || bounces >=
maxDepth) break;
When emitted light should be included, the path throughput weight must be multiplied with the radiance emitted by the current path vertex (if an intersection was found) or radiance emitted by infinite area light sources, if present.
<<Add emitted light at path vertex or from the environment>>=
if (foundIntersection) L += beta * isect.
Le(-ray.d); else for (const auto &light : scene.
lights) L += beta * light->
Le(ray);
Before estimating the direct illumination at the current vertex, it is necessary to compute the scattering functions at the vertex. A special case arises when
SurfaceInteraction::bsdf is equal to nullptr, which indicates that the current surface has no effect on light. pbrt uses such surfaces to represent transitions between participating media, whose boundaries are themselves optically inactive (i.e., they have the same index of refraction on both sides). Since the basic PathIntegrator ignores media, it simply skips over such surfaces without counting them as scattering events in the bounces counter.