Spectrum DirectLightingIntegrator::Li(const RayDifferential &ray, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { ProfilePhase p(Prof::SamplerIntegratorLi); Spectrum L(0.f); // Find closest ray intersection or return background radiance SurfaceInteraction isect; if (!scene.Intersect(ray, &isect)) { for (const auto &light : scene.lights) L += light->Le(ray); return L; } // Compute scattering functions for surface interaction isect.ComputeScatteringFunctions(ray, arena); if (!isect.bsdf) return Li(isect.SpawnRay(ray.d), scene, sampler, arena, depth); Vector3f wo = isect.wo; // Compute emitted light if ray hit an area light source L += isect.Le(wo); if (scene.lights.size() > 0) { // Compute direct lighting for _DirectLightingIntegrator_ integrator if (strategy == LightStrategy::UniformSampleAll) L += UniformSampleAllLights(isect, scene, arena, sampler, nLightSamples); else L += UniformSampleOneLight(isect, scene, arena, sampler); } if (depth + 1 < maxDepth) { Vector3f wi; // Trace rays for specular reflection and refraction L += SpecularReflect(ray, isect, scene, sampler, arena, depth); L += SpecularTransmit(ray, isect, scene, sampler, arena, depth); } return L; }
COREDLL Spectrum WeightedSampleOneLight(const Scene *scene, const Point &p, const Normal &n, const Vector &wo, BSDF *bsdf, const Sample *sample, int lightSampleOffset, int lightNumOffset, int bsdfSampleOffset, int bsdfComponentOffset, float *&avgY, float *&avgYsample, float *&cdf, float &overallAvgY) { int nLights = int(scene->lights.size()); // Initialize _avgY_ array if necessary if (!avgY) { avgY = new float[nLights]; avgYsample = new float[nLights]; cdf = new float[nLights+1]; for (int i = 0; i < nLights; ++i) avgY[i] = avgYsample[i] = 0.; } Spectrum L(0.); if (overallAvgY == 0.) { // Sample one light uniformly and initialize luminance arrays L = UniformSampleOneLight(scene, p, n, wo, bsdf, sample, lightSampleOffset, lightNumOffset, bsdfSampleOffset, bsdfComponentOffset); float luminance = L.y(); overallAvgY = luminance; for (int i = 0; i < nLights; ++i) avgY[i] = luminance; } else { // Choose _light_ according to average reflected luminance float c, lightSampleWeight; for (int i = 0; i < nLights; ++i) avgYsample[i] = max(avgY[i], .1f * overallAvgY); ComputeStep1dCDF(avgYsample, nLights, &c, cdf); float t = SampleStep1d(avgYsample, cdf, c, nLights, sample->oneD[lightNumOffset][0], &lightSampleWeight); int lightNum = min(Float2Int(nLights * t), nLights-1); Light *light = scene->lights[lightNum]; L = EstimateDirect(scene, light, p, n, wo, bsdf, sample, lightSampleOffset, bsdfSampleOffset, bsdfComponentOffset, 0); // Update _avgY_ array with reflected radiance due to light float luminance = L.y(); avgY[lightNum] = ExponentialAverage(avgY[lightNum], luminance, .99f); overallAvgY = ExponentialAverage(overallAvgY, luminance, .999f); L /= lightSampleWeight; } return L; }
Spectrum BidirIntegrator::Li(const Scene *scene, const RayDifferential &ray, const Sample *sample, float *alpha) const { Spectrum L(0.); // Generate eye and light sub-paths BidirVertex eyePath[MAX_VERTS], lightPath[MAX_VERTS]; int nEye = generatePath(scene, ray, sample, eyeBSDFOffset, eyeBSDFCompOffset, eyePath, MAX_VERTS); if (nEye == 0) { *alpha = 0.; return L; } *alpha = 1; // Choose light for bidirectional path int lightNum = Floor2Int(sample->oneD[lightNumOffset][0] * scene->lights.size()); lightNum = min(lightNum, (int)scene->lights.size() - 1); Light *light = scene->lights[lightNum]; float lightWeight = float(scene->lights.size()); // Sample ray from light source to start light path Ray lightRay; float lightPdf; float u[4]; u[0] = sample->twoD[lightPosOffset][0]; u[1] = sample->twoD[lightPosOffset][1]; u[2] = sample->twoD[lightDirOffset][0]; u[3] = sample->twoD[lightDirOffset][1]; Spectrum Le = light->Sample_L(scene, u[0], u[1], u[2], u[3], &lightRay, &lightPdf); if (lightPdf == 0.) return 0.f; Le = lightWeight / lightPdf; int nLight = generatePath(scene, lightRay, sample, lightBSDFOffset, lightBSDFCompOffset, lightPath, MAX_VERTS); // Connect bidirectional path prefixes and evaluate throughput Spectrum directWt(1.0); for (int i = 1; i <= nEye; ++i) { // Handle direct lighting for bidirectional integrator directWt /= eyePath[i-1].rrWeight; L += directWt * UniformSampleOneLight(scene, eyePath[i-1].p, eyePath[i-1].ng, eyePath[i-1].wi, eyePath[i-1].bsdf, sample, directLightOffset[i-1], directLightNumOffset[i-1], directBSDFOffset[i-1], directBSDFCompOffset[i-1]) / weightPath(eyePath, i, lightPath, 0); directWt *= eyePath[i-1].bsdf->f(eyePath[i-1].wi, eyePath[i-1].wo) * AbsDot(eyePath[i-1].wo, eyePath[i-1].ng) / eyePath[i-1].bsdfWeight; for (int j = 1; j <= nLight; ++j) L += Le * evalPath(scene, eyePath, i, lightPath, j) / weightPath(eyePath, i, lightPath, j); } return L; }
Spectrum IrradianceCacheIntegrator::pathL(Ray &r, const Scene *scene, const Renderer *renderer, const Sample *sample, MemoryArena &arena) const { Spectrum L(0.f); Spectrum pathThroughput = 1.; RayDifferential ray(r); bool specularBounce = false; for (int pathLength = 0; ; ++pathLength) { // Find next vertex of path Intersection isect; if (!scene->Intersect(ray, &isect)) break; if (pathLength == 0) r.maxt = ray.maxt; else if (pathLength == 1) pathThroughput *= renderer->Transmittance(scene, ray, sample, arena, NULL); else pathThroughput *= renderer->Transmittance(scene, ray, NULL, arena, sample->rng); // Possibly add emitted light at path vertex if (specularBounce) L += pathThroughput * isect.Le(-ray.d); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena); // Sample illumination from lights to find path contribution const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; L += pathThroughput * UniformSampleOneLight(scene, renderer, arena, p, n, wo, isect.rayEpsilon, bsdf, sample); if (pathLength+1 == maxIndirectDepth) break; // Sample BSDF to get new path direction // Get random numbers for sampling new direction, \mono{bs1}, \mono{bs2}, and \mono{bcs} Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, BSDFSample(*sample->rng), &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, isect.rayEpsilon); // Possibly terminate the path if (pathLength > 2) { float rrProb = min(1.f, pathThroughput.y()); if (sample->rng->RandomFloat() > rrProb) break; pathThroughput /= rrProb; } } return L; }
Spectrum DirectLightingIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Intersection &isect, const Sample *sample, RNG &rng, MemoryArena &arena) const { Spectrum L(0.f); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena); Vector wo = -ray.d; const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Compute direct lighting for _DirectLightingIntegrator_ integrator if (scene->lights.size() > 0) { // Apply direct lighting strategy switch (strategy) { case SAMPLE_ALL_UNIFORM: L += UniformSampleAllLights(scene, renderer, arena, p, n, wo, isect.rayEpsilon, ray.time, bsdf, sample, rng, lightSampleOffsets, bsdfSampleOffsets); break; case SAMPLE_ONE_UNIFORM: L += UniformSampleOneLight(scene, renderer, arena, p, n, wo, isect.rayEpsilon, ray.time, bsdf, sample, rng, lightNumOffset, lightSampleOffsets, bsdfSampleOffsets); break; } } if (ray.depth + 1 < maxDepth) { Vector wi; // Trace rays for specular reflection and refraction Spectrum reflection(0.f); Spectrum refraction(0.f); reflection = SpecularReflect(ray, bsdf, rng, isect, renderer, scene, sample,arena); refraction = SpecularTransmit(ray, bsdf, rng, isect, renderer, scene, sample, arena); L += reflection; L += refraction; } //printf("Size %i \n", NaiadFoam::cur->FoamPlane().size()); return L*bsdf->dgShading.mult; }
// VolPathIntegrator Method Definitions Spectrum VolPathIntegrator::Li(const RayDifferential &r, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { ProfilePhase p(Prof::SamplerIntegratorLi); Spectrum L(0.f), alpha(1.f); RayDifferential ray(r); bool specularBounce = false; for (int bounces = 0;; ++bounces) { // Store intersection into _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // Sample the participating medium, if present MediumInteraction mi; if (ray.medium) alpha *= ray.medium->Sample(ray, sampler, arena, &mi); if (alpha.IsBlack()) break; // Handle an interaction with a medium or a surface if (mi.IsValid()) { // Handle medium scattering case Vector3f wo = -ray.d, wi; L += alpha * UniformSampleOneLight(mi, scene, sampler, arena, true); Point2f phaseSample = sampler.Get2D(); mi.phase->Sample_p(wo, &wi, phaseSample); ray = mi.SpawnRay(wi); } else { // Handle surface scattering case // Possibly add emitted light and terminate if (bounces == 0 || specularBounce) { // Add emitted light at path vertex or from the environment if (foundIntersection) L += alpha * isect.Le(-ray.d); else for (const auto &light : scene.lights) L += alpha * light->Le(ray); } if (!foundIntersection || bounces >= maxDepth) break; // Compute scattering functions and skip over medium boundaries isect.ComputeScatteringFunctions(ray, arena, true); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); bounces--; continue; } // Sample illumination from lights to find attenuated path // contribution L += alpha * UniformSampleOneLight(isect, scene, sampler, arena, true); // Sample BSDF to get new path direction Vector3f wo = -ray.d, wi; Float pdf; BxDFType flags; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; alpha *= f * AbsDot(wi, isect.shading.n) / pdf; Assert(std::isinf(alpha.y()) == false); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); // Account for attenuated subsurface scattering, if applicable if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) { // Importance sample the BSSRDF SurfaceInteraction pi; Spectrum S = isect.bssrdf->Sample_S( scene, sampler.Get1D(), sampler.Get2D(), arena, &pi, &pdf); #ifndef NDEBUG Assert(std::isinf(alpha.y()) == false); #endif if (S.IsBlack() || pdf == 0) break; alpha *= S / pdf; // Account for the attenuated direct subsurface scattering // component L += alpha * UniformSampleOneLight(pi, scene, sampler, arena, true); // Account for the indirect subsurface scattering component Spectrum f = pi.bsdf->Sample_f(pi.wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; alpha *= f * AbsDot(wi, pi.shading.n) / pdf; #ifndef NDEBUG Assert(std::isinf(alpha.y()) == false); #endif specularBounce = (flags & BSDF_SPECULAR) != 0; ray = pi.SpawnRay(wi); } } // Possibly terminate the path if (bounces > 3) { Float continueProbability = std::min((Float).5, alpha.y()); if (sampler.Get1D() > continueProbability) break; alpha /= continueProbability; Assert(std::isinf(alpha.y()) == false); } } return L; }
// PathIntegrator Method Definitions Spectrum PathIntegrator::Li(const RayDifferential &r, const Scene &scene, Sampler &sampler, MemoryArena &arena) const { Spectrum L(0.f); // Declare common path integration variables RayDifferential ray(r); Spectrum pathThroughput = Spectrum(1.f); bool specularBounce = false; for (int bounces = 0;; ++bounces) { // Store intersection into _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // Possibly add emitted light and terminate if (bounces == 0 || specularBounce) { // Add emitted light at path vertex or from the environment if (foundIntersection) L += pathThroughput * isect.Le(-ray.d); else for (const auto &light : scene.lights) L += pathThroughput * light->Le(ray); } if (!foundIntersection || bounces >= maxDepth) break; // Compute scattering functions and skip over medium boundaries isect.ComputeScatteringFunctions(ray, arena, true); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); bounces--; continue; } // Sample illumination from lights to find path contribution L += pathThroughput * UniformSampleOneLight(isect, scene, sampler, arena); // Sample BSDF to get new path direction Vector3f wo = -ray.d, wi; Float pdf; BxDFType flags; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; pathThroughput *= f * AbsDot(wi, isect.shading.n) / pdf; #ifndef NDEBUG Assert(std::isinf(pathThroughput.y()) == false); #endif specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); // Account for subsurface scattering, if applicable if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) { // Importance sample the BSSRDF BSSRDFSample bssrdfSample; bssrdfSample.uDiscrete = sampler.Get1D(); bssrdfSample.pos = sampler.Get2D(); SurfaceInteraction isect_out = isect; pathThroughput *= isect.bssrdf->Sample_f( isect_out, scene, ray.time, bssrdfSample, arena, &isect, &pdf); #ifndef NDEBUG Assert(std::isinf(pathThroughput.y()) == false); #endif if (pathThroughput.IsBlack()) break; // Account for the direct subsurface scattering component isect.wo = Vector3f(isect.shading.n); // Sample illumination from lights to find path contribution L += pathThroughput * UniformSampleOneLight(isect, scene, sampler, arena); // Account for the indirect subsurface scattering component Spectrum f = isect.bsdf->Sample_f(isect.wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; pathThroughput *= f * AbsDot(wi, isect.shading.n) / pdf; #ifndef NDEBUG Assert(std::isinf(pathThroughput.y()) == false); #endif specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); } // Possibly terminate the path if (bounces > 3) { Float continueProbability = std::min((Float).5, pathThroughput.y()); if (sampler.Get1D() > continueProbability) break; pathThroughput /= continueProbability; Assert(std::isinf(pathThroughput.y()) == false); } } return L; }
Spectrum IrradianceCache::IndirectLo(const Point &p, const Normal &n, const Vector &wo, BSDF *bsdf, BxDFType flags, const Sample *sample, const Scene *scene) const { if (bsdf->NumComponents(flags) == 0) return Spectrum(0.); Spectrum E; if (!InterpolateIrradiance(scene, p, n, &E)) { // Compute irradiance at current point u_int scramble[2] = { RandomUInt(), RandomUInt() }; float sumInvDists = 0.; for (int i = 0; i < nSamples; ++i) { // Trace ray to sample radiance for irradiance estimate // Update irradiance statistics for rays traced static StatsCounter nIrradiancePaths("Irradiance Cache", "Paths followed for irradiance estimates"); ++nIrradiancePaths; float u[2]; Sample02(i, scramble, u); Vector w = CosineSampleHemisphere(u[0], u[1]); RayDifferential r(p, bsdf->LocalToWorld(w)); if (Dot(r.d, n) < 0) r.d = -r.d; Spectrum L(0.); // Do path tracing to compute radiance along ray for estimate { // Declare common path integration variables Spectrum pathThroughput = 1.; RayDifferential ray(r); bool specularBounce = false; for (int pathLength = 0; ; ++pathLength) { // Find next vertex of path Intersection isect; if (!scene->Intersect(ray, &isect)) break; if (pathLength == 0) r.maxt = ray.maxt; pathThroughput *= scene->Transmittance(ray); // Possibly add emitted light at path vertex if (specularBounce) L += pathThroughput * isect.Le(-ray.d); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray); // Sample illumination from lights to find path contribution const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; L += pathThroughput * UniformSampleOneLight(scene, p, n, wo, bsdf, sample); if (pathLength+1 == maxIndirectDepth) break; // Sample BSDF to get new path direction // Get random numbers for sampling new direction, \mono{bs1}, \mono{bs2}, and \mono{bcs} float bs1 = RandomFloat(), bs2 = RandomFloat(), bcs = RandomFloat(); Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, bs1, bs2, bcs, &pdf, BSDF_ALL, &flags); if (f.Black() || pdf == 0.) break; specularBounce = (flags & BSDF_SPECULAR) != 0; pathThroughput *= f * AbsDot(wi, n) / pdf; ray = RayDifferential(p, wi); // Possibly terminate the path if (pathLength > 3) { float continueProbability = .5f; if (RandomFloat() > continueProbability) break; pathThroughput /= continueProbability; } } } E += L; float dist = r.maxt * r.d.Length(); sumInvDists += 1.f / dist; } E *= M_PI / float(nSamples); // Add computed irradiance value to cache // Update statistics for new irradiance sample static StatsCounter nSamplesComputed("Irradiance Cache", "Irradiance estimates computed"); ++nSamplesComputed; // Compute bounding box of irradiance sample's contribution region static float minMaxDist = .001f * powf(scene->WorldBound().Volume(), 1.f/3.f); static float maxMaxDist = .125f * powf(scene->WorldBound().Volume(), 1.f/3.f); float maxDist = nSamples / sumInvDists; if (minMaxDist > 0.f) maxDist = Clamp(maxDist, minMaxDist, maxMaxDist); maxDist *= maxError; BBox sampleExtent(p); sampleExtent.Expand(maxDist); octree->Add(IrradianceSample(E, p, n, maxDist), sampleExtent); } return .5f * bsdf->rho(wo, flags) * E; }
Spectrum PathIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &r, const Intersection &isect, const Sample *sample, 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 pathLength = 0; ; ++pathLength) { // Possibly add emitted light at path vertex if (pathLength == 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 (pathLength < SAMPLE_DEPTH) L += pathThroughput * UniformSampleOneLight(scene, renderer, arena, p, n, wo, isectp->RayEpsilon, bsdf, sample, lightNumOffset[pathLength], &lightSampleOffsets[pathLength], &bsdfSampleOffsets[pathLength]); else L += pathThroughput * UniformSampleOneLight(scene, renderer, arena, p, n, wo, isectp->RayEpsilon, bsdf, sample); // Sample BSDF to get new path direction // Get _outgoingBSDFSample_ for sampling new path direction BSDFSample outgoingBSDFSample; if (pathLength < SAMPLE_DEPTH) outgoingBSDFSample = BSDFSample(sample, pathSampleOffsets[pathLength], 0); else outgoingBSDFSample = BSDFSample(*sample->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 (pathLength > 3) { float continueProbability = min(.5f, pathThroughput.y()); if (sample->rng->RandomFloat() > continueProbability) break; pathThroughput /= continueProbability; } if (pathLength == maxDepth) break; // Find next vertex of path if (!scene->Intersect(ray, &localIsect)) { if (specularBounce) { for (u_int i = 0; i < scene->lights.size(); ++i) L += pathThroughput * scene->lights[i]->Le(ray); } break; } if (pathLength > 1) pathThroughput *= renderer->Transmittance(scene, ray, NULL, arena, sample->rng); isectp = &localIsect; } return L; }
Spectrum VolPathIntegrator::Li(const RayDifferential &r, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { ProfilePhase p(Prof::SamplerIntegratorLi); Spectrum L(0.f), beta(1.f); RayDifferential ray(r); bool specularBounce = false; int bounces; // Added after book publication: etaScale tracks the accumulated effect // of radiance scaling due to rays passing through refractive // boundaries (see the derivation on p. 527 of the third edition). We // track this value in order to remove it from beta when we apply // Russian roulette; this is worthwhile, since it lets us sometimes // avoid terminating refracted rays that are about to be refracted back // out of a medium and thus have their beta value increased. Float etaScale = 1; for (bounces = 0;; ++bounces) { // Intersect _ray_ with scene and store intersection in _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // Sample the participating medium, if present MediumInteraction mi; if (ray.medium) beta *= ray.medium->Sample(ray, sampler, arena, &mi); if (beta.IsBlack()) break; // Handle an interaction with a medium or a surface if (mi.IsValid()) { // Terminate path if ray escaped or _maxDepth_ was reached if (bounces >= maxDepth) break; ++volumeInteractions; // Handle scattering at point in medium for volumetric path tracer const Distribution1D *lightDistrib = lightDistribution->Lookup(mi.p); L += beta * UniformSampleOneLight(mi, scene, arena, sampler, true, lightDistrib); Vector3f wo = -ray.d, wi; mi.phase->Sample_p(wo, &wi, sampler.Get2D()); ray = mi.SpawnRay(wi); } else { ++surfaceInteractions; // Handle scattering at point on surface for volumetric path tracer // Possibly add emitted light at intersection if (bounces == 0 || specularBounce) { // Add emitted light at path vertex or from the environment if (foundIntersection) L += beta * isect.Le(-ray.d); else for (const auto &light : scene.infiniteLights) L += beta * light->Le(ray); } // Terminate path if ray escaped or _maxDepth_ was reached if (!foundIntersection || bounces >= maxDepth) break; // Compute scattering functions and skip over medium boundaries isect.ComputeScatteringFunctions(ray, arena, true); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); bounces--; continue; } // Sample illumination from lights to find attenuated path // contribution const Distribution1D *lightDistrib = lightDistribution->Lookup(isect.p); L += beta * UniformSampleOneLight(isect, scene, arena, sampler, true, lightDistrib); // Sample BSDF to get new path direction Vector3f wo = -ray.d, wi; Float pdf; BxDFType flags; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; beta *= f * AbsDot(wi, isect.shading.n) / pdf; DCHECK(std::isinf(beta.y()) == false); specularBounce = (flags & BSDF_SPECULAR) != 0; if ((flags & BSDF_SPECULAR) && (flags & BSDF_TRANSMISSION)) { Float eta = isect.bsdf->eta; // Update the term that tracks radiance scaling for refraction // depending on whether the ray is entering or leaving the // medium. etaScale *= (Dot(wo, isect.n) > 0) ? (eta * eta) : 1 / (eta * eta); } ray = isect.SpawnRay(ray, wi, flags, isect.bsdf->eta); // Account for attenuated subsurface scattering, if applicable if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) { // Importance sample the BSSRDF SurfaceInteraction pi; Spectrum S = isect.bssrdf->Sample_S( scene, sampler.Get1D(), sampler.Get2D(), arena, &pi, &pdf); DCHECK(std::isinf(beta.y()) == false); if (S.IsBlack() || pdf == 0) break; beta *= S / pdf; // Account for the attenuated direct subsurface scattering // component L += beta * UniformSampleOneLight(pi, scene, arena, sampler, true, lightDistribution->Lookup(pi.p)); // Account for the indirect subsurface scattering component Spectrum f = pi.bsdf->Sample_f(pi.wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0) break; beta *= f * AbsDot(wi, pi.shading.n) / pdf; DCHECK(std::isinf(beta.y()) == false); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = pi.SpawnRay(wi); } } // Possibly terminate the path with Russian roulette // Factor out radiance scaling due to refraction in rrBeta. Spectrum rrBeta = beta * etaScale; if (rrBeta.MaxComponentValue() < rrThreshold && bounces > 3) { Float q = std::max((Float).05, 1 - rrBeta.MaxComponentValue()); if (sampler.Get1D() < q) break; beta /= 1 - q; DCHECK(std::isinf(beta.y()) == false); } } ReportValue(pathLength, bounces); return L; }
Spectrum DirectLighting::Li(const Scene *scene, const RayDifferential &ray, const Sample *sample, float *alpha) const { Intersection isect; Spectrum L(0.); if (scene->Intersect(ray, &isect)) { if (alpha) *alpha = 1.; // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray); Vector wo = -ray.d; const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Compute direct lighting for _DirectLighting_ integrator if (scene->lights.size() > 0) { // Apply direct lighting strategy switch (strategy) { case SAMPLE_ALL_UNIFORM: L += UniformSampleAllLights(scene, p, n, wo, bsdf, sample, lightSampleOffset, bsdfSampleOffset, bsdfComponentOffset); break; case SAMPLE_ONE_UNIFORM: L += UniformSampleOneLight(scene, p, n, wo, bsdf, sample, lightSampleOffset[0], lightNumOffset, bsdfSampleOffset[0], bsdfComponentOffset[0]); break; case SAMPLE_ONE_WEIGHTED: L += WeightedSampleOneLight(scene, p, n, wo, bsdf, sample, lightSampleOffset[0], lightNumOffset, bsdfSampleOffset[0], bsdfComponentOffset[0], avgY, avgYsample, cdf, overallAvgY); break; } } if (rayDepth++ < maxDepth) { Vector wi; // Trace rays for specular reflection and refraction Spectrum f = bsdf->Sample_f(wo, &wi, BxDFType(BSDF_REFLECTION | BSDF_SPECULAR)); if (!f.Black()) { // Compute ray differential _rd_ for specular reflection RayDifferential rd(p, wi); rd.hasDifferentials = true; rd.rx.o = p + isect.dg.dpdx; rd.ry.o = p + isect.dg.dpdy; // Compute differential reflected directions Normal dndx = bsdf->dgShading.dndu * bsdf->dgShading.dudx + bsdf->dgShading.dndv * bsdf->dgShading.dvdx; Normal dndy = bsdf->dgShading.dndu * bsdf->dgShading.dudy + bsdf->dgShading.dndv * bsdf->dgShading.dvdy; Vector dwodx = -ray.rx.d - wo, dwody = -ray.ry.d - wo; float dDNdx = Dot(dwodx, n) + Dot(wo, dndx); float dDNdy = Dot(dwody, n) + Dot(wo, dndy); rd.rx.d = wi - dwodx + 2 * Vector(Dot(wo, n) * dndx + dDNdx * n); rd.ry.d = wi - dwody + 2 * Vector(Dot(wo, n) * dndy + dDNdy * n); L += scene->Li(rd, sample) * f * AbsDot(wi, n); } f = bsdf->Sample_f(wo, &wi, BxDFType(BSDF_TRANSMISSION | BSDF_SPECULAR)); if (!f.Black()) { // Compute ray differential _rd_ for specular transmission RayDifferential rd(p, wi); rd.hasDifferentials = true; rd.rx.o = p + isect.dg.dpdx; rd.ry.o = p + isect.dg.dpdy; float eta = bsdf->eta; Vector w = -wo; if (Dot(wo, n) < 0) eta = 1.f / eta; Normal dndx = bsdf->dgShading.dndu * bsdf->dgShading.dudx + bsdf->dgShading.dndv * bsdf->dgShading.dvdx; Normal dndy = bsdf->dgShading.dndu * bsdf->dgShading.dudy + bsdf->dgShading.dndv * bsdf->dgShading.dvdy; Vector dwodx = -ray.rx.d - wo, dwody = -ray.ry.d - wo; float dDNdx = Dot(dwodx, n) + Dot(wo, dndx); float dDNdy = Dot(dwody, n) + Dot(wo, dndy); float mu = eta * Dot(w, n) - Dot(wi, n); float dmudx = (eta - (eta*eta*Dot(w,n))/Dot(wi, n)) * dDNdx; float dmudy = (eta - (eta*eta*Dot(w,n))/Dot(wi, n)) * dDNdy; rd.rx.d = wi + eta * dwodx - Vector(mu * dndx + dmudx * n); rd.ry.d = wi + eta * dwody - Vector(mu * dndy + dmudy * n); L += scene->Li(rd, sample) * f * AbsDot(wi, n); } } --rayDepth; } else { // Handle ray with no intersection if (alpha) *alpha = 0.; for (u_int i = 0; i < scene->lights.size(); ++i) L += scene->lights[i]->Le(ray); if (alpha && !L.Black()) *alpha = 1.; return L; } return L; }
// PathIntegrator Method Definitions Spectrum PathIntegrator::Li(const RayDifferential &r, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { ProfilePhase p(Prof::SamplerIntegratorLi); Spectrum L(0.f), beta(1.f); RayDifferential ray(r); bool specularBounce = false; for (int bounces = 0;; ++bounces) { // Find next path vertex and accumulate contribution // Intersect _ray_ with scene and store intersection in _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // Possibly add emitted light at intersection if (bounces == 0 || specularBounce) { // 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); } // Terminate path if ray escaped or _maxDepth_ was reached if (!foundIntersection || bounces >= maxDepth) break; // Compute scattering functions and skip over medium boundaries isect.ComputeScatteringFunctions(ray, arena, true); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); bounces--; continue; } // Sample illumination from lights to find path contribution L += beta * UniformSampleOneLight(isect, scene, arena, sampler); // Sample BSDF to get new path direction Vector3f wo = -ray.d, wi; Float pdf; BxDFType flags; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.f) break; beta *= f * AbsDot(wi, isect.shading.n) / pdf; Assert(std::isinf(beta.y()) == false); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); // Account for subsurface scattering, if applicable if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) { // Importance sample the BSSRDF SurfaceInteraction pi; Spectrum S = isect.bssrdf->Sample_S( scene, sampler.Get1D(), sampler.Get2D(), arena, &pi, &pdf); #ifndef NDEBUG Assert(std::isinf(beta.y()) == false); #endif if (S.IsBlack() || pdf == 0) break; beta *= S / pdf; // Account for the direct subsurface scattering component L += beta * UniformSampleOneLight(pi, scene, arena, sampler); // Account for the indirect subsurface scattering component Spectrum f = pi.bsdf->Sample_f(pi.wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0) break; beta *= f * AbsDot(wi, pi.shading.n) / pdf; #ifndef NDEBUG Assert(std::isinf(beta.y()) == false); #endif specularBounce = (flags & BSDF_SPECULAR) != 0; ray = pi.SpawnRay(wi); } // Possibly terminate the path with Russian roulette if (bounces > 3) { Float continueProbability = std::min((Float).95, beta.y()); if (sampler.Get1D() > continueProbability) break; beta /= continueProbability; Assert(std::isinf(beta.y()) == false); } } return L; }
Spectrum PathIntegrator::Li(const RayDifferential &r, const Scene &scene, Sampler &sampler, MemoryArena &arena, int depth) const { ProfilePhase p(Prof::SamplerIntegratorLi); Spectrum L(0.f), beta(1.f); RayDifferential ray(r); bool specularBounce = false; int bounces; for (bounces = 0;; ++bounces) { // Find next path vertex and accumulate contribution VLOG(2) << "Path tracer bounce " << bounces << ", current L = " << L << ", beta = " << beta; // Intersect _ray_ with scene and store intersection in _isect_ SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); // Possibly add emitted light at intersection if (bounces == 0 || specularBounce) { // Add emitted light at path vertex or from the environment if (foundIntersection) { L += beta * isect.Le(-ray.d); VLOG(2) << "Added Le -> L = " << L; } else { for (const auto &light : scene.infiniteLights) L += beta * light->Le(ray); VLOG(2) << "Added infinite area lights -> L = " << L; } } // Terminate path if ray escaped or _maxDepth_ was reached if (!foundIntersection || bounces >= maxDepth) break; // Compute scattering functions and skip over medium boundaries isect.ComputeScatteringFunctions(ray, arena, true); if (!isect.bsdf) { VLOG(2) << "Skipping intersection due to null bsdf"; ray = isect.SpawnRay(ray.d); bounces--; continue; } const Distribution1D *distrib = lightDistribution->Lookup(isect.p); // Sample illumination from lights to find path contribution. // (But skip this for perfectly specular BSDFs.) if (isect.bsdf->NumComponents(BxDFType(BSDF_ALL & ~BSDF_SPECULAR)) > 0) { ++totalPaths; Spectrum Ld = beta * UniformSampleOneLight(isect, scene, arena, sampler, false, distrib); VLOG(2) << "Sampled direct lighting Ld = " << Ld; if (Ld.IsBlack()) ++zeroRadiancePaths; CHECK_GE(Ld.y(), 0.f); L += Ld; } // Sample BSDF to get new path direction Vector3f wo = -ray.d, wi; Float pdf; BxDFType flags; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); VLOG(2) << "Sampled BSDF, f = " << f << ", pdf = " << pdf; if (f.IsBlack() || pdf == 0.f) break; beta *= f * AbsDot(wi, isect.shading.n) / pdf; VLOG(2) << "Updated beta = " << beta; CHECK_GE(beta.y(), 0.f); DCHECK(!std::isinf(beta.y())); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = isect.SpawnRay(wi); // Account for subsurface scattering, if applicable if (isect.bssrdf && (flags & BSDF_TRANSMISSION)) { // Importance sample the BSSRDF SurfaceInteraction pi; Spectrum S = isect.bssrdf->Sample_S( scene, sampler.Get1D(), sampler.Get2D(), arena, &pi, &pdf); DCHECK(!std::isinf(beta.y())); if (S.IsBlack() || pdf == 0) break; beta *= S / pdf; // Account for the direct subsurface scattering component L += beta * UniformSampleOneLight(pi, scene, arena, sampler, false, lightDistribution->Lookup(pi.p)); // Account for the indirect subsurface scattering component Spectrum f = pi.bsdf->Sample_f(pi.wo, &wi, sampler.Get2D(), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0) break; beta *= f * AbsDot(wi, pi.shading.n) / pdf; DCHECK(!std::isinf(beta.y())); specularBounce = (flags & BSDF_SPECULAR) != 0; ray = pi.SpawnRay(wi); } // Possibly terminate the path with Russian roulette if (beta.y() < rrThreshold && bounces > 3) { Float q = std::max((Float).05, 1 - beta.MaxComponentValue()); VLOG(2) << "RR termination probability q = " << q; if (sampler.Get1D() < q) break; beta /= 1 - q; VLOG(2) << "After RR survival, beta = " << beta; DCHECK(!std::isinf(beta.y())); } } ReportValue(pathLength, bounces); return L; }