Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its, bsdfIts; RayDifferential ray(r); LuminaireSamplingRecord lRec; Spectrum Li(0.0f); Point2 sample; /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ if (!rRec.rayIntersect(ray)) { /* If no intersection could be found, possibly return radiance from a background luminaire */ if (rRec.type & RadianceQueryRecord::EEmittedRadiance) return scene->LeBackground(ray); else return Spectrum(0.0f); } /* Possibly include emitted radiance if requested */ if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)) Li += its.Le(-ray.d); /* Include radiance from a subsurface integrator if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); const BSDF *bsdf = its.getBSDF(ray); if (EXPECT_NOT_TAKEN(!bsdf)) { /* The direct illumination integrator doesn't support surfaces without a BSDF (e.g. medium transitions) -- give up. */ return Li; } /* Leave here if direct illumination was not requested */ if (!(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) return Li; /* ==================================================================== */ /* Luminaire sampling */ /* ==================================================================== */ bool adaptiveQuery = (rRec.extra & RadianceQueryRecord::EAdaptiveQuery); Point2 *sampleArray; size_t numLuminaireSamples = m_luminaireSamples, numBSDFSamples = m_bsdfSamples; Float fracLum = m_fracLum, fracBSDF = m_fracBSDF, weightLum = m_weightLum, weightBSDF = m_weightBSDF; if (rRec.depth > 1 || adaptiveQuery) { /* This integrator is used recursively by another integrator. Be less accurate as this sample will not directly be observed. */ numBSDFSamples = numLuminaireSamples = 1; fracLum = fracBSDF = .5f; weightLum = weightBSDF = 1.0f; } if (numLuminaireSamples > 1) { sampleArray = rRec.sampler->next2DArray(numLuminaireSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } for (size_t i=0; i<numLuminaireSamples; ++i) { /* Estimate the direct illumination if this is requested */ if (scene->sampleLuminaire(its.p, ray.time, lRec, sampleArray[i])) { /* Allocate a record for querying the BSDF */ BSDFQueryRecord bRec(its, its.toLocal(-lRec.d)); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->eval(bRec); if (!bsdfVal.isZero()) { /* Calculate prob. of having sampled that direction using BSDF sampling */ Float bsdfPdf = (lRec.luminaire->isIntersectable() || lRec.luminaire->isBackgroundLuminaire()) ? bsdf->pdf(bRec) : 0; /* Weight using the power heuristic */ const Float weight = miWeight(lRec.pdf * fracLum, bsdfPdf * fracBSDF) * weightLum; Li += lRec.value * bsdfVal * weight; } } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ if (numBSDFSamples > 1) { sampleArray = rRec.sampler->next2DArray(numBSDFSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } for (size_t i=0; i<numBSDFSamples; ++i) { /* Sample BSDF * cos(theta) */ BSDFQueryRecord bRec(its, rRec.sampler, ERadiance); Float bsdfPdf; Spectrum bsdfVal = bsdf->sample(bRec, bsdfPdf, sampleArray[i]); if (bsdfVal.isZero()) continue; /* Trace a ray in this direction */ Ray bsdfRay(its.p, its.toWorld(bRec.wo), ray.time); if (scene->rayIntersect(bsdfRay, bsdfIts)) { /* Intersected something - check if it was a luminaire */ if (bsdfIts.isLuminaire()) { lRec = LuminaireSamplingRecord(bsdfIts, -bsdfRay.d); lRec.value = bsdfIts.Le(-bsdfRay.d); } else { continue; } } else { /* No intersection found. Possibly, there is a background luminaire such as an environment map? */ if (scene->hasBackgroundLuminaire()) { lRec.luminaire = scene->getBackgroundLuminaire(); lRec.d = -bsdfRay.d; lRec.value = lRec.luminaire->Le(bsdfRay); } else { continue; } } const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ? scene->pdfLuminaire(its.p, lRec) : 0; const Float weight = miWeight(bsdfPdf * fracBSDF, lumPdf * fracLum) * weightBSDF; Li += lRec.value * bsdfVal * weight; } return Li; }
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its; RayDifferential ray(r); Spectrum Li(0.0f); /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ rRec.rayIntersect(ray); ray.mint = Epsilon; Spectrum pathThroughput(1.0f); while (rRec.depth <= m_maxDepth || m_maxDepth < 0) { if (!its.isValid()) { /* If no intersection could be found, potentially return radiance from a background luminaire if it exists */ if (rRec.type & RadianceQueryRecord::EEmittedRadiance) Li += pathThroughput * scene->LeBackground(ray); break; } const BSDF *bsdf = its.getBSDF(ray); if (EXPECT_NOT_TAKEN(bsdf == NULL)) { /* The MI path tracer doesn't support surfaces without a BSDF (e.g. medium transitions) -- give up. */ break; } /* Possibly include emitted radiance if requested */ if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)) Li += pathThroughput * its.Le(-ray.d); /* Include radiance from a subsurface integrator if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += pathThroughput * its.LoSub(scene, rRec.sampler, -ray.d, rRec.depth); if (m_maxDepth > 0 && rRec.depth >= m_maxDepth) break; /* ==================================================================== */ /* Luminaire sampling */ /* ==================================================================== */ /* Prevent light leaks due to the use of shading normals */ Float wiDotGeoN = -dot(its.geoFrame.n, ray.d), wiDotShN = Frame::cosTheta(its.wi); if (wiDotGeoN * wiDotShN < 0 && m_strictNormals) break; /* Estimate the direct illumination if this is requested */ LuminaireSamplingRecord lRec; if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance && scene->sampleLuminaire(its.p, ray.time, lRec, rRec.nextSample2D())) { /* Allocate a record for querying the BSDF */ const Vector wo = -lRec.d; const BSDFQueryRecord bRec(its, its.toLocal(wo)); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->fCos(bRec); Float woDotGeoN = dot(its.geoFrame.n, wo); /* Prevent light leaks due to the use of shading normals */ if (!bsdfVal.isZero() && (!m_strictNormals || woDotGeoN * Frame::cosTheta(bRec.wo) > 0)) { /* Calculate prob. of having sampled that direction using BSDF sampling */ Float bsdfPdf = (lRec.luminaire->isIntersectable() || lRec.luminaire->isBackgroundLuminaire()) ? bsdf->pdf(bRec) : 0; /* Weight using the power heuristic */ const Float weight = miWeight(lRec.pdf, bsdfPdf); Li += pathThroughput * lRec.value * bsdfVal * weight; } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ /* Sample BSDF * cos(theta) */ BSDFQueryRecord bRec(its); Float bsdfPdf; Spectrum bsdfVal = bsdf->sampleCos(bRec, bsdfPdf, rRec.nextSample2D()); if (bsdfVal.isZero()) break; bsdfVal /= bsdfPdf; /* Prevent light leaks due to the use of shading normals */ const Vector wo = its.toWorld(bRec.wo); Float woDotGeoN = dot(its.geoFrame.n, wo); if (woDotGeoN * Frame::cosTheta(bRec.wo) <= 0 && m_strictNormals) break; /* Trace a ray in this direction */ ray = Ray(its.p, wo, ray.time); bool hitLuminaire = false; if (scene->rayIntersect(ray, its)) { /* Intersected something - check if it was a luminaire */ if (its.isLuminaire()) { lRec = LuminaireSamplingRecord(its, -ray.d); lRec.value = its.Le(-ray.d); hitLuminaire = true; } } else { /* No intersection found. Possibly, there is a background luminaire such as an environment map? */ if (scene->hasBackgroundLuminaire()) { lRec.luminaire = scene->getBackgroundLuminaire(); lRec.value = lRec.luminaire->Le(ray); lRec.d = -ray.d; hitLuminaire = true; } else { rRec.depth++; break; } } /* If a luminaire was hit, estimate the local illumination and weight using the power heuristic */ if (hitLuminaire && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance)) { /* Prob. of having generated this sample using luminaire sampling */ const Float lumPdf = (!(bRec.sampledType & BSDF::EDelta)) ? scene->pdfLuminaire(ray.o, lRec) : 0; const Float weight = miWeight(bsdfPdf, lumPdf); Li += pathThroughput * lRec.value * bsdfVal * weight; } /* ==================================================================== */ /* Indirect illumination */ /* ==================================================================== */ /* Set the recursive query type */ /* Stop if no surface was hit by the BSDF sample or if indirect illumination was not requested */ if (!its.isValid() || !(rRec.type & RadianceQueryRecord::EIndirectSurfaceRadiance)) break; rRec.type = RadianceQueryRecord::ERadianceNoEmission; /* Russian roulette - Possibly stop the recursion. Don't do this when dealing with a transmission component, since solid angle compression factors cause problems with the heuristic below */ if (rRec.depth >= m_rrDepth && !(bRec.sampledType & BSDF::ETransmission)) { /* Assuming that BSDF importance sampling is perfect, 'bsdfVal.max()' should equal the maximum albedo over all spectral samples */ Float approxAlbedo = std::min((Float) 0.9f, bsdfVal.max()); if (rRec.nextSample1D() > approxAlbedo) break; else pathThroughput /= approxAlbedo; } pathThroughput *= bsdfVal; rRec.depth++; } /* Store statistics */ avgPathLength.incrementBase(); avgPathLength += rRec.depth; return Li; }