// WhittedIntegrator [ surface integrator ] Method Definitions Spectrum WhittedIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Intersection &isect, const Sample *sample, RNG &rng, MemoryArena &arena) const { Spectrum L(0.); // Compute emitted and reflected light at ray intersection point // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena); // return BSDF of the material of the hit primitive // Initialize common variables for Whitted integrator const pbrt::Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; // Compute emitted light at the intersection point. The emiited light exists if // the intersection occured at an area light source; otherwise, it is zero. L += isect.Le(wo); // Add contribution of each light source for (uint32_t i = 0; i < scene->lights.size(); ++i) { Vector wi; float pdf; VisibilityTester visibility; Spectrum Li = scene->lights[i]->Sample_L(p, isect.rayEpsilon, LightSample(rng), ray.time, &wi, &pdf, &visibility); // if light is a delta light, then "reflection" direction wi is set to the light direction with pdf =1 if (Li.IsBlack() || pdf == 0.f) continue; Spectrum f = bsdf->f(wo, wi); // // compute the fraction of light to be reflected from wi to wo. // ONLY non-delta BXDF, e.g. Lambertian (diffuse reflection) contributes to f here. if (!f.IsBlack() && visibility.Unoccluded(scene)) // bsdf indicates that some of the incident // light from direction wi is in fact scattered to the direction wo L += f * Li * AbsDot(wi, n) * visibility.Transmittance(scene, renderer, sample, rng, arena) / pdf; } // consider the specular reflection and the specular transmission; // you can get this component only when the light sources are NOT delta distribution. // there's no possible way that the peaks of the two delta distributions (the glass // and the point light) match. it is not possible // to get a highlight of a point light source in a mirror. if (ray.depth + 1 < maxDepth) { // 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; }
int RandomWalk(const Scene &scene, RayDifferential ray, Sampler &sampler, MemoryArena &arena, Spectrum beta, Float pdf, int maxDepth, TransportMode mode, Vertex *path) { if (maxDepth == 0) return 0; int bounces = 0; // Declare variables for forward and reverse probability densities Float pdfFwd = pdf, pdfRev = 0; while (true) { // Attempt to create the next subpath vertex in _path_ MediumInteraction mi; VLOG(2) << "Random walk. Bounces " << bounces << ", beta " << beta << ", pdfFwd " << pdfFwd << ", pdfRev " << pdfRev; // Trace a ray and sample the medium, if any SurfaceInteraction isect; bool foundIntersection = scene.Intersect(ray, &isect); if (ray.medium) beta *= ray.medium->Sample(ray, sampler, arena, &mi); if (beta.IsBlack()) break; Vertex &vertex = path[bounces], &prev = path[bounces - 1]; if (mi.IsValid()) { // Record medium interaction in _path_ and compute forward density vertex = Vertex::CreateMedium(mi, beta, pdfFwd, prev); if (++bounces >= maxDepth) break; // Sample direction and compute reverse density at preceding vertex Vector3f wi; pdfFwd = pdfRev = mi.phase->Sample_p(-ray.d, &wi, sampler.Get2D()); ray = mi.SpawnRay(wi); } else { // Handle surface interaction for path generation if (!foundIntersection) { // Capture escaped rays when tracing from the camera if (mode == TransportMode::Radiance) { vertex = Vertex::CreateLight(EndpointInteraction(ray), beta, pdfFwd); ++bounces; } break; } // Compute scattering functions for _mode_ and skip over medium // boundaries isect.ComputeScatteringFunctions(ray, arena, true, mode); if (!isect.bsdf) { ray = isect.SpawnRay(ray.d); continue; } // Initialize _vertex_ with surface intersection information vertex = Vertex::CreateSurface(isect, beta, pdfFwd, prev); if (++bounces >= maxDepth) break; // Sample BSDF at current vertex and compute reverse probability Vector3f wi, wo = isect.wo; BxDFType type; Spectrum f = isect.bsdf->Sample_f(wo, &wi, sampler.Get2D(), &pdfFwd, BSDF_ALL, &type); VLOG(2) << "Random walk sampled dir " << wi << " f: " << f << ", pdfFwd: " << pdfFwd; if (f.IsBlack() || pdfFwd == 0.f) break; beta *= f * AbsDot(wi, isect.shading.n) / pdfFwd; VLOG(2) << "Random walk beta now " << beta; pdfRev = isect.bsdf->Pdf(wi, wo, BSDF_ALL); if (type & BSDF_SPECULAR) { vertex.delta = true; pdfRev = pdfFwd = 0; } beta *= CorrectShadingNormal(isect, wo, wi, mode); VLOG(2) << "Random walk beta after shading normal correction " << beta; ray = isect.SpawnRay(wi); } // Compute reverse area density at preceding vertex prev.pdfRev = vertex.ConvertDensity(pdfRev, prev); } return bounces; }
int main(int argc, char *argv[]) { Options opt; pbrtInit(opt); // number of monte carlo estimates //const int estimates = 1; const int estimates = 100000000; // radiance of uniform environment map const double environmentRadiance = 1.0; fprintf(stderr, "outgoing radiance from a surface viewed\n" "straight on with uniform lighting\n\n" " uniform incoming radiance = %.3f\n" " monte carlo samples = %d\n\n\n", environmentRadiance, estimates); CreateBSDFFunc BSDFFuncArray[] = { createBlinn0, createBlinn05, createBlinn2, createBlinn30and0, createAniso0_0, createAniso10_10, createAniso30_30, //CO createLambertian, //CO createOrenNayar0, //CO createOrenNayar20, //CO createFresnelBlend0, //CO createFresnelBlend30, //CO createPlastic, //CO createSubstrate, }; const char* BSDFFuncDescripArray[] = { "Blinn (exponent 0)", "Blinn (exponent 0.5)", "Blinn (exponent 2)", "Blinn (exponent 30 and 0)", "Anisotropic (exponent 0, 0)", "Anisotropic (exponent 10, 10)", "Anisotropic (exponent 30, 30)", //CO "Lambertian", //CO "Oren Nayar (sigma 0)", //CO "Oren Nayar (sigma 20)", //CO "FresnelBlend (Blinn exponent 0)", //CO "FresnelBlend (Blinn exponent 30)", //CO "Plastic", //CO "Substrate", }; GenSampleFunc SampleFuncArray[] = { Gen_Sample_f, Gen_CosHemisphere, //CO Gen_UniformHemisphere, }; const char* SampleFuncDescripArray[] = { "BSDF Importance Sampling", "Cos Hemisphere", //CO "Uniform Hemisphere", }; int numModels = sizeof(BSDFFuncArray) / sizeof(BSDFFuncArray[0]); int numModelsDescrip = sizeof(BSDFFuncDescripArray) / sizeof(BSDFFuncDescripArray[0]); int numGenerators = sizeof(SampleFuncArray) / sizeof(SampleFuncArray[0]); int numGeneratorsDescrip = sizeof(SampleFuncDescripArray) / sizeof(SampleFuncDescripArray[0]); if (numModels != numModelsDescrip) { fprintf(stderr, "BSDFFuncArray and BSDFFuncDescripArray out of sync!\n"); exit(1); } if (numGenerators != numGeneratorsDescrip) { fprintf(stderr, "SampleFuncArray and SampleFuncDescripArray out of sync!\n"); exit(1); } // for each bsdf model for (int model = 0; model < numModels; model++) { BSDF* bsdf; // create BSDF which requires creating a Shape, casting a Ray // that hits the shape to get a DifferentialGeometry object, // and passing the DifferentialGeometry object into the BSDF { Transform t = RotateX(-90); bool reverseOrientation = false; ParamSet p; Reference<Shape> disk = new Disk(new Transform(t), new Transform(Inverse(t)), reverseOrientation, 0., 1., 0, 360.); if (!disk) { fprintf(stderr, "Could not load disk plugin\n" " make sure the PBRT_SEARCHPATH environment variable is set\n"); exit(1); } Point origin(0.1, 1, 0); // offset slightly so we don't hit center of disk Vector direction(0, -1, 0); float tHit, rayEps; Ray r(origin, direction, 1e-3, INFINITY); DifferentialGeometry* dg = BSDF_ALLOC(arena, DifferentialGeometry)(); disk->Intersect(r, &tHit, &rayEps, dg); bsdf = BSDF_ALLOC(arena, BSDF)(*dg, dg->nn); (BSDFFuncArray[model])(bsdf); } // facing directly at normal Vector woL = Normalize(Vector(0, 0, 1)); Vector wo = bsdf->LocalToWorld(woL); const Normal &n = bsdf->dgShading.nn; // for each method of generating samples over the hemisphere for (int gen = 0; gen < numGenerators; gen++) { double redSum = 0.0; const int numHistoBins = 10; double histogram[numHistoBins][numHistoBins]; for (int i = 0; i < numHistoBins; i++) { for (int j = 0; j < numHistoBins; j++) { histogram[i][j] = 0; } } int badSamples = 0; int outsideSamples = 0; int warningTarget = 1; for (int sample = 0; sample < estimates; sample++) { Vector wi; float pdf; Spectrum f; // sample hemisphere around bsdf, wo is fixed (SampleFuncArray[gen])(bsdf, wo, & wi, & pdf, & f); double redF = spectrumRedValue(f); // add hemisphere sample to histogram Vector wiL = bsdf->WorldToLocal(wi); float x = Clamp(wiL.x, -1.f, 1.f); float y = Clamp(wiL.y, -1.f, 1.f); float wiPhi = (atan2(y, x) + M_PI) / (2.0 * M_PI); float wiCosTheta = wiL.z; bool validSample = (wiCosTheta > 1e-7); if (wiPhi < -0.0001 || wiPhi > 1.0001 || wiCosTheta > 1.0001) { // wiCosTheta can be less than 0 fprintf(stderr, "bad wi! %.3f %.3f %.3f, (%.3f %.3f)\n", wiL[0], wiL[1], wiL[2], wiPhi, wiCosTheta); } else if (validSample) { int histoPhi = (int) (wiPhi * numHistoBins); int histoCosTheta = (int) (wiCosTheta * numHistoBins); histogram[histoCosTheta][histoPhi] += 1.0 / pdf; } if (!validSample) { outsideSamples++; } else if (pdf == 0.f || isnan(pdf) || redF < 0 || isnan(redF)) { if (badSamples == warningTarget) { fprintf(stderr, "warning %d, bad sample %d! " "pdf: %.3f, redF: %.3f\n", warningTarget, sample, pdf, redF); warningTarget *= 10; } badSamples++; } else { // outgoing radiance estimate = // bsdf * incomingRadiance * cos(wi) / pdf redSum += redF * environmentRadiance * AbsDot(wi, n) / pdf; } } int goodSamples = estimates - badSamples; // print results fprintf(stderr, "*** BRDF: '%s', Samples: '%s'\n\n" "wi histogram showing the relative weight in each bin\n" " all entries should be close to 2pi = %.5f:\n" " (%d bad samples, %d outside samples)\n\n" " cos(theta) bins\n", BSDFFuncDescripArray[model], SampleFuncDescripArray[gen], M_PI * 2.0, badSamples, outsideSamples); double totalSum = 0.0; for (int i = 0; i < numHistoBins; i++) { fprintf(stderr, " phi bin %02d:", i); for (int j = 0; j < numHistoBins; j++) { fprintf(stderr, " %5.2f", histogram[i][j] * numHistoBins * numHistoBins / goodSamples); totalSum += histogram[i][j]; } fprintf(stderr, "\n"); } fprintf(stderr, "\n final average : %.5f (error %.5f)\n\n" " radiance = %.5f\n\n", totalSum / goodSamples, totalSum / goodSamples - M_PI * 2.0, redSum / goodSamples); } } pbrtCleanup(); return 0; }
// WhittedIntegrator Method Definitions Spectrum WhittedIntegrator::Li(const Scene *scene, const RayDifferential &ray, const Sample *sample, float *alpha) const { Intersection isect; Spectrum L(0.); bool hitSomething; // Search for ray-primitive intersection hitSomething = scene->Intersect(ray, &isect); if (!hitSomething) { // 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; } else { // Initialize _alpha_ for ray hit if (alpha) *alpha = 1.; // Compute emitted and reflected light at ray intersection point // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray); // Initialize common variables for Whitted integrator const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Add contribution of each light source Vector wi; for (u_int i = 0; i < scene->lights.size(); ++i) { VisibilityTester visibility; Spectrum Li = scene->lights[i]->Sample_L(p, &wi, &visibility); if (Li.Black()) continue; Spectrum f = bsdf->f(wo, wi); if (!f.Black() && visibility.Unoccluded(scene)) L += f * Li * AbsDot(wi, n) * visibility.Transmittance(scene); } if (rayDepth++ < maxDepth) { // Trace rays for specular reflection and refraction Spectrum f = bsdf->Sample_f(wo, &wi, BxDFType(BSDF_REFLECTION | BSDF_SPECULAR)); if (!f.Black() && AbsDot(wi, n) > 0.f) { // 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() && AbsDot(wi, n) > 0.f) { // 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; } return L; }
void PhotonShootingTask::Run() { // Declare local variables for _PhotonShootingTask_ MemoryArena arena; RNG rng(31 * taskNum); vector<Photon> localDirectPhotons, localIndirectPhotons, localCausticPhotons; vector<RadiancePhoton> localRadiancePhotons; uint32_t totalPaths = 0; bool causticDone = (integrator->nCausticPhotonsWanted == 0); bool indirectDone = (integrator->nIndirectPhotonsWanted == 0); PermutedHalton halton(6, rng); vector<Spectrum> localRpReflectances, localRpTransmittances; while (true) { // Follow photon paths for a block of samples const uint32_t blockSize = 4096; for (uint32_t i = 0; i < blockSize; ++i) { float u[6]; halton.Sample(++totalPaths, u); // Choose light to shoot photon from float lightPdf; int lightNum = lightDistribution->SampleDiscrete(u[0], &lightPdf); const Light *light = scene->lights[lightNum]; // Generate _photonRay_ from light source and initialize _alpha_ RayDifferential photonRay; float pdf; LightSample ls(u[1], u[2], u[3]); Normal Nl; Spectrum Le = light->Sample_L(scene, ls, u[4], u[5], time, &photonRay, &Nl, &pdf); if (pdf == 0.f || Le.IsBlack()) continue; Spectrum alpha = (AbsDot(Nl, photonRay.d) * Le) / (pdf * lightPdf); if (!alpha.IsBlack()) { // Follow photon path through scene and record intersections PBRT_PHOTON_MAP_STARTED_RAY_PATH(&photonRay, &alpha); bool specularPath = true; Intersection photonIsect; int nIntersections = 0; while (scene->Intersect(photonRay, &photonIsect)) { ++nIntersections; // Handle photon/surface intersection alpha *= renderer->Transmittance(scene, photonRay, NULL, rng, arena); BSDF *photonBSDF = photonIsect.GetBSDF(photonRay, arena); BxDFType specularType = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_SPECULAR); bool hasNonSpecular = (photonBSDF->NumComponents() > photonBSDF->NumComponents(specularType)); Vector wo = -photonRay.d; if (hasNonSpecular) { // Deposit photon at surface Photon photon(photonIsect.dg.p, alpha, wo); bool depositedPhoton = false; if (specularPath && nIntersections > 1) { if (!causticDone) { PBRT_PHOTON_MAP_DEPOSITED_CAUSTIC_PHOTON(&photonIsect.dg, &alpha, &wo); depositedPhoton = true; localCausticPhotons.push_back(photon); } } else { // Deposit either direct or indirect photon // stop depositing direct photons once indirectDone is true; don't // want to waste memory storing too many if we're going a long time // trying to get enough caustic photons desposited. if (nIntersections == 1 && !indirectDone && integrator->finalGather) { PBRT_PHOTON_MAP_DEPOSITED_DIRECT_PHOTON(&photonIsect.dg, &alpha, &wo); depositedPhoton = true; localDirectPhotons.push_back(photon); } else if (nIntersections > 1 && !indirectDone) { PBRT_PHOTON_MAP_DEPOSITED_INDIRECT_PHOTON(&photonIsect.dg, &alpha, &wo); depositedPhoton = true; localIndirectPhotons.push_back(photon); } } // Possibly create radiance photon at photon intersection point if (depositedPhoton && integrator->finalGather && rng.RandomFloat() < .125f) { Normal n = photonIsect.dg.nn; n = Faceforward(n, -photonRay.d); localRadiancePhotons.push_back(RadiancePhoton(photonIsect.dg.p, n)); Spectrum rho_r = photonBSDF->rho(rng, BSDF_ALL_REFLECTION); localRpReflectances.push_back(rho_r); Spectrum rho_t = photonBSDF->rho(rng, BSDF_ALL_TRANSMISSION); localRpTransmittances.push_back(rho_t); } } if (nIntersections >= integrator->maxPhotonDepth) break; // Sample new photon ray direction Vector wi; float pdf; BxDFType flags; Spectrum fr = photonBSDF->Sample_f(wo, &wi, BSDFSample(rng), &pdf, BSDF_ALL, &flags); if (fr.IsBlack() || pdf == 0.f) break; Spectrum anew = alpha * fr * AbsDot(wi, photonBSDF->dgShading.nn) / pdf; // Possibly terminate photon path with Russian roulette float continueProb = min(1.f, anew.y() / alpha.y()); if (rng.RandomFloat() > continueProb) break; alpha = anew / continueProb; specularPath &= ((flags & BSDF_SPECULAR) != 0); if (indirectDone && !specularPath) break; photonRay = RayDifferential(photonIsect.dg.p, wi, photonRay, photonIsect.rayEpsilon); } PBRT_PHOTON_MAP_FINISHED_RAY_PATH(&photonRay, &alpha); } arena.FreeAll(); } // Merge local photon data with data in _PhotonIntegrator_ { MutexLock lock(mutex); // Give up if we're not storing enough photons if (abortTasks) return; if (nshot > 500000 && (unsuccessful(integrator->nCausticPhotonsWanted, causticPhotons.size(), blockSize) || unsuccessful(integrator->nIndirectPhotonsWanted, indirectPhotons.size(), blockSize))) { Error("Unable to store enough photons. Giving up.\n"); causticPhotons.erase(causticPhotons.begin(), causticPhotons.end()); indirectPhotons.erase(indirectPhotons.begin(), indirectPhotons.end()); radiancePhotons.erase(radiancePhotons.begin(), radiancePhotons.end()); abortTasks = true; return; } progress.Update(localIndirectPhotons.size() + localCausticPhotons.size()); nshot += blockSize; // Merge indirect photons into shared array if (!indirectDone) { integrator->nIndirectPaths += blockSize; for (uint32_t i = 0; i < localIndirectPhotons.size(); ++i) indirectPhotons.push_back(localIndirectPhotons[i]); localIndirectPhotons.erase(localIndirectPhotons.begin(), localIndirectPhotons.end()); if (indirectPhotons.size() >= integrator->nIndirectPhotonsWanted) indirectDone = true; nDirectPaths += blockSize; for (uint32_t i = 0; i < localDirectPhotons.size(); ++i) directPhotons.push_back(localDirectPhotons[i]); localDirectPhotons.erase(localDirectPhotons.begin(), localDirectPhotons.end()); } // Merge direct, caustic, and radiance photons into shared array if (!causticDone) { integrator->nCausticPaths += blockSize; for (uint32_t i = 0; i < localCausticPhotons.size(); ++i) causticPhotons.push_back(localCausticPhotons[i]); localCausticPhotons.erase(localCausticPhotons.begin(), localCausticPhotons.end()); if (causticPhotons.size() >= integrator->nCausticPhotonsWanted) causticDone = true; } for (uint32_t i = 0; i < localRadiancePhotons.size(); ++i) radiancePhotons.push_back(localRadiancePhotons[i]); localRadiancePhotons.erase(localRadiancePhotons.begin(), localRadiancePhotons.end()); for (uint32_t i = 0; i < localRpReflectances.size(); ++i) rpReflectances.push_back(localRpReflectances[i]); localRpReflectances.erase(localRpReflectances.begin(), localRpReflectances.end()); for (uint32_t i = 0; i < localRpTransmittances.size(); ++i) rpTransmittances.push_back(localRpTransmittances[i]); localRpTransmittances.erase(localRpTransmittances.begin(), localRpTransmittances.end()); } // Exit task if enough photons have been found if (indirectDone && causticDone) break; } }
// 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 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; }
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; }
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 LightSample ls(lightSampPos[2*sampOffset], lightSampPos[2*sampOffset+1], lightSampComp[sampOffset]); LightInfo2 li = light->Sample_L(scene, ls, lightSampDir[2*sampOffset], lightSampDir[2*sampOffset+1], camera->shutterOpen); RayDifferential ray(li.ray); Spectrum alpha(li.L); if (li.pdf == 0.f || alpha.IsBlack()) continue; alpha /= li.pdf * lightPdf; Intersection isect; auto optIsect = scene.Intersect(ray); while (optIsect && !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 = optIsect->GetBSDF(ray, arena); // Create virtual light at ray intersection point Spectrum contrib = alpha * bsdf->rho(wo, rng) / M_PI; virtualLights[s].push_back(VirtualLight(optIsect->dg.p, optIsect->dg.nn, contrib, optIsect->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(optIsect->dg.p, wi, ray, optIsect->rayEpsilon); optIsect = scene.Intersect(ray); } arena.FreeAll(); } } delete lightDistribution; }
bool Curve::recursiveIntersect(const Ray &ray, Float *tHit, SurfaceInteraction *isect, const Point3f cp[4], const Transform &rayToObject, Float u0, Float u1, int depth) const { // Try to cull curve segment versus ray // Compute bounding box of curve segment, _curveBounds_ Bounds3f curveBounds = Union(Bounds3f(cp[0], cp[1]), Bounds3f(cp[2], cp[3])); Float maxWidth = std::max(Lerp(u0, common->width[0], common->width[1]), Lerp(u1, common->width[0], common->width[1])); curveBounds = Expand(curveBounds, 0.5 * maxWidth); // Compute bounding box of ray, _rayBounds_ Float rayLength = ray.d.Length(); Float zMax = rayLength * ray.tMax; Bounds3f rayBounds(Point3f(0, 0, 0), Point3f(0, 0, zMax)); if (Overlaps(curveBounds, rayBounds) == false) return false; if (depth > 0) { // Split curve segment into sub-segments and test for intersection Float uMid = 0.5f * (u0 + u1); Point3f cpSplit[7]; SubdivideBezier(cp, cpSplit); return (recursiveIntersect(ray, tHit, isect, &cpSplit[0], rayToObject, u0, uMid, depth - 1) || recursiveIntersect(ray, tHit, isect, &cpSplit[3], rayToObject, uMid, u1, depth - 1)); } else { // Intersect ray with curve segment // Test ray against segment endpoint boundaries // Test sample point against tangent perpendicular at curve start Float edge = (cp[1].y - cp[0].y) * -cp[0].y + cp[0].x * (cp[0].x - cp[1].x); if (edge < 0) return false; // Test sample point against tangent perpendicular at curve end edge = (cp[2].y - cp[3].y) * -cp[3].y + cp[3].x * (cp[3].x - cp[2].x); if (edge < 0) return false; // Compute line $w$ that gives minimum distance to sample point Vector2f segmentDirection = Point2f(cp[3]) - Point2f(cp[0]); Float denom = segmentDirection.LengthSquared(); if (denom == 0) return false; Float w = Dot(-Vector2f(cp[0]), segmentDirection) / denom; // Compute $u$ coordinate of curve intersection point and _hitWidth_ Float u = Clamp(Lerp(w, u0, u1), u0, u1); Float hitWidth = Lerp(u, common->width[0], common->width[1]); Normal3f nHit; if (common->type == CurveType::Ribbon) { // Scale _hitWidth_ based on ribbon orientation Float sin0 = std::sin((1 - u) * common->normalAngle) * common->invSinNormalAngle; Float sin1 = std::sin(u * common->normalAngle) * common->invSinNormalAngle; nHit = sin0 * common->n[0] + sin1 * common->n[1]; hitWidth *= AbsDot(nHit, ray.d) / rayLength; } // Test intersection point against curve width Vector3f dpcdw; Point3f pc = EvalBezier(cp, Clamp(w, 0, 1), &dpcdw); Float ptCurveDist2 = pc.x * pc.x + pc.y * pc.y; if (ptCurveDist2 > hitWidth * hitWidth * .25) return false; if (pc.z < 0 || pc.z > zMax) return false; // Compute $v$ coordinate of curve intersection point Float ptCurveDist = std::sqrt(ptCurveDist2); Float edgeFunc = dpcdw.x * -pc.y + pc.x * dpcdw.y; Float v = (edgeFunc > 0) ? 0.5f + ptCurveDist / hitWidth : 0.5f - ptCurveDist / hitWidth; // Compute hit _t_ and partial derivatives for curve intersection if (tHit != nullptr) { // FIXME: this tHit isn't quite right for ribbons... *tHit = pc.z / rayLength; // Compute error bounds for curve intersection Vector3f pError(2 * hitWidth, 2 * hitWidth, 2 * hitWidth); // Compute $\dpdu$ and $\dpdv$ for curve intersection Vector3f dpdu, dpdv; EvalBezier(common->cpObj, u, &dpdu); if (common->type == CurveType::Ribbon) dpdv = Normalize(Cross(nHit, dpdu)) * hitWidth; else { // Compute curve $\dpdv$ for flat and cylinder curves Vector3f dpduPlane = (Inverse(rayToObject))(dpdu); Vector3f dpdvPlane = Normalize(Vector3f(-dpduPlane.y, dpduPlane.x, 0)) * hitWidth; if (common->type == CurveType::Cylinder) { // Rotate _dpdvPlane_ to give cylindrical appearance Float theta = Lerp(v, -90., 90.); Transform rot = Rotate(-theta, dpduPlane); dpdvPlane = rot(dpdvPlane); } dpdv = rayToObject(dpdvPlane); } *isect = (*ObjectToWorld)(SurfaceInteraction( ray(pc.z), pError, Point2f(u, v), -ray.d, dpdu, dpdv, Normal3f(0, 0, 0), Normal3f(0, 0, 0), ray.time, this)); } ++nHits; return true; } }
Spectrum ConnectBDPT(const Scene &scene, Vertex *lightVertices, Vertex *cameraVertices, int s, int t, const Distribution1D &lightDistr, const Camera &camera, Sampler &sampler, Point2f *pRaster, Float *misWeightPtr) { Spectrum L(0.f); // Ignore invalid connections related to infinite area lights if (t > 1 && s != 0 && cameraVertices[t - 1].type == VertexType::Light) return Spectrum(0.f); // Perform connection and write contribution to _L_ Vertex sampled; if (s == 0) { // Interpret the camera subpath as a complete path const Vertex &pt = cameraVertices[t - 1]; if (pt.IsLight()) L = pt.Le(scene, cameraVertices[t - 2]) * pt.beta; } else if (t == 1) { // Sample a point on the camera and connect it to the light subpath const Vertex &qs = lightVertices[s - 1]; if (qs.IsConnectible()) { VisibilityTester vis; Vector3f wi; Float pdf; Spectrum Wi = camera.Sample_Wi(qs.GetInteraction(), sampler.Get2D(), &wi, &pdf, pRaster, &vis); if (pdf > 0 && !Wi.IsBlack()) { // Initialize dynamically sampled vertex and _L_ for $t=1$ case sampled = Vertex::CreateCamera(&camera, vis.P1(), Wi / pdf); L = qs.beta * qs.f(sampled) * vis.Tr(scene, sampler) * sampled.beta; if (qs.IsOnSurface()) L *= AbsDot(wi, qs.ns()); } } } else if (s == 1) { // Sample a point on a light and connect it to the camera subpath const Vertex &pt = cameraVertices[t - 1]; if (pt.IsConnectible()) { Float lightPdf; VisibilityTester vis; Vector3f wi; Float pdf; int lightNum = lightDistr.SampleDiscrete(sampler.Get1D(), &lightPdf); const std::shared_ptr<Light> &light = scene.lights[lightNum]; Spectrum lightWeight = light->Sample_Li( pt.GetInteraction(), sampler.Get2D(), &wi, &pdf, &vis); if (pdf > 0 && !lightWeight.IsBlack()) { EndpointInteraction ei(vis.P1(), light.get()); sampled = Vertex::CreateLight(ei, lightWeight / (pdf * lightPdf), 0); sampled.pdfFwd = sampled.PdfLightOrigin(scene, pt, lightDistr); L = pt.beta * pt.f(sampled) * vis.Tr(scene, sampler) * sampled.beta; if (pt.IsOnSurface()) L *= AbsDot(wi, pt.ns()); } } } else { // Handle all other bidirectional connection cases const Vertex &qs = lightVertices[s - 1], &pt = cameraVertices[t - 1]; if (qs.IsConnectible() && pt.IsConnectible()) { L = qs.beta * qs.f(pt) * pt.f(qs) * pt.beta; if (!L.IsBlack()) L *= G(scene, sampler, qs, pt); } } // Compute MIS weight for connection strategy Float misWeight = L.IsBlack() ? 0.f : MISWeight(scene, lightVertices, cameraVertices, sampled, s, t, lightDistr); L *= misWeight; if (misWeightPtr) *misWeightPtr = misWeight; return L; }
bool Shade(Path* path, Geometry *geometry, BVHAccel *bvh, const RayHit& rayHit) { uint tracedShadowRayCount; if (rayHit.index == 0xffffffffu) { return false; } // Something was hit unsigned int currentTriangleIndex = rayHit.index; RGB triInterpCol = geometry->triangles[currentTriangleIndex].InterpolateColor( geometry->vertColors, rayHit.b1, rayHit.b2); Normal shadeN = geometry->triangles[currentTriangleIndex].InterpolateNormal( geometry->vertNormals, rayHit.b1, rayHit.b2); // Calculate next step path->depth++; // Check if I have to stop if (path->depth >= MAX_PATH_DEPTH) { // Too depth, terminate the path return false; } else if (path->depth > 2) { // Russian Rulette, maximize cos const float p = min(1.f, triInterpCol.filter() * AbsDot(shadeN, path->pathRay.d)); if (p > getFloatRNG(&path->seed)) path->throughput /= p; else { // Terminate the path return false; } } //-------------------------------------------------------------------------- // Build the shadow ray //-------------------------------------------------------------------------- // Check if it is a light source float RdotShadeN = Dot(path->pathRay.d, shadeN); if (geometry->IsLight(currentTriangleIndex)) { // Check if we are on the right side of the light source if ((path->depth == 1) && (RdotShadeN < 0.f)) path->radiance += triInterpCol * path->throughput; // Terminate the path return false; } if (RdotShadeN > 0.f) { // Flip shade normal shadeN = -shadeN; } else RdotShadeN = -RdotShadeN; path->throughput *= RdotShadeN * triInterpCol; // Trace shadow rays const Point hitPoint = path->pathRay(rayHit.t); tracedShadowRayCount = 0; const float lightStrategyPdf = static_cast<float> (SHADOWRAY) / static_cast<float> (geometry->nLights); float lightPdf[SHADOWRAY]; RGB lightColor[SHADOWRAY]; Ray shadowRay[SHADOWRAY]; for (unsigned int i = 0; i < SHADOWRAY; ++i) { // Select the light to sample const unsigned int currentLightIndex = geometry->SampleLights(getFloatRNG(&path->seed)); // const TriangleLight &light = scene->lights[currentLightIndex]; // Select a point on the surface lightColor[tracedShadowRayCount] = Sample_L(currentLightIndex, geometry, hitPoint, shadeN, getFloatRNG(&path->seed), getFloatRNG(&path->seed), &lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]); // Scale light pdf for ONE_UNIFORM strategy lightPdf[tracedShadowRayCount] *= lightStrategyPdf; // Using 0.1 instead of 0.0 to cut down fireflies if (lightPdf[tracedShadowRayCount] > 0.1f) tracedShadowRayCount++; } RayHit* rh = new RayHit[tracedShadowRayCount]; for (unsigned int i = 0; i < tracedShadowRayCount; ++i) Intersect(shadowRay[i], rh[i], bvh->bvhTree, geometry->triangles, geometry->vertices); if ((tracedShadowRayCount > 0)) { for (unsigned int i = 0; i < tracedShadowRayCount; ++i) { const RayHit *shadowRayHit = &rh[i]; if (shadowRayHit->index == 0xffffffffu) { // Nothing was hit, light is visible path->radiance += path->throughput * lightColor[i] / lightPdf[i]; } } } //-------------------------------------------------------------------------- // Build the next vertex path ray //-------------------------------------------------------------------------- // Calculate exit direction float r1 = 2.f * M_PI * getFloatRNG(&path->seed); float r2 = getFloatRNG(&path->seed); float r2s = sqrt(r2); const Vector w(shadeN); Vector u; if (fabsf(shadeN.x) > .1f) { const Vector a(0.f, 1.f, 0.f); u = Cross(a, w); } else { const Vector a(1.f, 0.f, 0.f); u = Cross(a, w); } u = Normalize(u); Vector v = Cross(w, u); Vector newDir = u * (cosf(r1) * r2s) + v * (sinf(r1) * r2s) + w * sqrtf(1.f - r2); newDir = Normalize(newDir); path->pathRay.o = hitPoint; path->pathRay.d = newDir; return true; }
Spectrum PhotonIntegrator::Li(const Scene *scene, const RayDifferential &ray, const Sample *sample, float *alpha) const { // Compute reflected radiance with photon map Spectrum L(0.); Intersection isect; if (scene->Intersect(ray, &isect)) { if (alpha) *alpha = 1.; 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); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; // Compute direct lighting for photon map integrator if (directWithPhotons) L += LPhoton(directMap, nDirectPaths, nLookup, bsdf, isect, wo, maxDistSquared); else L += UniformSampleAllLights(scene, p, n, wo, bsdf, sample, lightSampleOffset, bsdfSampleOffset, bsdfComponentOffset); // Compute indirect lighting for photon map integrator L += LPhoton(causticMap, nCausticPaths, nLookup, bsdf, isect, wo, maxDistSquared); if (finalGather) { // Do one-bounce final gather for photon map Spectrum Li(0.); for (int i = 0; i < gatherSamples; ++i) { // Sample random direction for final gather ray Vector wi; float u1 = sample->twoD[gatherSampleOffset][2*i]; float u2 = sample->twoD[gatherSampleOffset][2*i+1]; float u3 = sample->oneD[gatherComponentOffset][i]; float pdf; Spectrum fr = bsdf->Sample_f(wo, &wi, u1, u2, u3, &pdf, BxDFType(BSDF_ALL & (~BSDF_SPECULAR))); if (fr.Black() || pdf == 0.f) continue; RayDifferential bounceRay(p, wi); static StatsCounter gatherRays("Photon Map", // NOBOOK "Final gather rays traced"); // NOBOOK ++gatherRays; // NOBOOK Intersection gatherIsect; if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance at final gather intersection BSDF *gatherBSDF = gatherIsect.GetBSDF(bounceRay); Vector bounceWo = -bounceRay.d; Spectrum Lindir = LPhoton(directMap, nDirectPaths, nLookup, gatherBSDF, gatherIsect, bounceWo, maxDistSquared) + LPhoton(indirectMap, nIndirectPaths, nLookup, gatherBSDF, gatherIsect, bounceWo, maxDistSquared) + LPhoton(causticMap, nCausticPaths, nLookup, gatherBSDF, gatherIsect, bounceWo, maxDistSquared); Lindir *= scene->Transmittance(bounceRay); Li += fr * Lindir * AbsDot(wi, n) / pdf; } } L += Li / float(gatherSamples); } else L += LPhoton(indirectMap, nIndirectPaths, nLookup, bsdf, isect, wo, maxDistSquared); if (specularDepth++ < maxSpecularDepth) { 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); } } --specularDepth; } 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; }
void PhotonIntegrator::Preprocess(const Scene *scene) { if (scene->lights.size() == 0) return; ProgressReporter progress(nCausticPhotons+nDirectPhotons+ // NOBOOK nIndirectPhotons, "Shooting photons"); // NOBOOK vector<Photon> causticPhotons; vector<Photon> directPhotons; vector<Photon> indirectPhotons; causticPhotons.reserve(nCausticPhotons); // NOBOOK directPhotons.reserve(nDirectPhotons); // NOBOOK indirectPhotons.reserve(nIndirectPhotons); // NOBOOK // Initialize photon shooting statistics static StatsCounter nshot("Photon Map", "Number of photons shot from lights"); bool causticDone = (nCausticPhotons == 0); bool directDone = (nDirectPhotons == 0); bool indirectDone = (nIndirectPhotons == 0); while (!causticDone || !directDone || !indirectDone) { ++nshot; // Give up if we're not storing enough photons if (nshot > 500000 && (unsuccessful(nCausticPhotons, causticPhotons.size(), nshot) || unsuccessful(nDirectPhotons, directPhotons.size(), nshot) || unsuccessful(nIndirectPhotons, indirectPhotons.size(), nshot))) { Error("Unable to store enough photons. Giving up.\n"); return; } // Trace a photon path and store contribution // Choose 4D sample values for photon float u[4]; u[0] = (float)RadicalInverse((int)nshot+1, 2); u[1] = (float)RadicalInverse((int)nshot+1, 3); u[2] = (float)RadicalInverse((int)nshot+1, 5); u[3] = (float)RadicalInverse((int)nshot+1, 7); // Choose light to shoot photon from int nLights = int(scene->lights.size()); int lightNum = min(Floor2Int(nLights * (float)RadicalInverse((int)nshot+1, 11)), nLights-1); Light *light = scene->lights[lightNum]; float lightPdf = 1.f / nLights; // Generate _photonRay_ from light source and initialize _alpha_ RayDifferential photonRay; float pdf; Spectrum alpha = light->Sample_L(scene, u[0], u[1], u[2], u[3], &photonRay, &pdf); if (pdf == 0.f || alpha.Black()) continue; alpha /= pdf * lightPdf; if (!alpha.Black()) { // Follow photon path through scene and record intersections bool specularPath = false; Intersection photonIsect; int nIntersections = 0; while (scene->Intersect(photonRay, &photonIsect)) { ++nIntersections; // Handle photon/surface intersection alpha *= scene->Transmittance(photonRay); Vector wo = -photonRay.d; BSDF *photonBSDF = photonIsect.GetBSDF(photonRay); BxDFType specularType = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_SPECULAR); bool hasNonSpecular = (photonBSDF->NumComponents() > photonBSDF->NumComponents(specularType)); if (hasNonSpecular) { // Deposit photon at surface Photon photon(photonIsect.dg.p, alpha, wo); if (nIntersections == 1) { // Process direct lighting photon intersection if (!directDone) { directPhotons.push_back(photon); if (directPhotons.size() == nDirectPhotons) { directDone = true; nDirectPaths = (int)nshot; directMap = new KdTree<Photon, PhotonProcess>(directPhotons); } progress.Update(); // NOBOOK } } else if (specularPath) { // Process caustic photon intersection if (!causticDone) { causticPhotons.push_back(photon); if (causticPhotons.size() == nCausticPhotons) { causticDone = true; nCausticPaths = (int)nshot; causticMap = new KdTree<Photon, PhotonProcess>(causticPhotons); } progress.Update(); } } else { // Process indirect lighting photon intersection if (!indirectDone) { indirectPhotons.push_back(photon); if (indirectPhotons.size() == nIndirectPhotons) { indirectDone = true; nIndirectPaths = (int)nshot; indirectMap = new KdTree<Photon, PhotonProcess>(indirectPhotons); } progress.Update(); } } } // Sample new photon ray direction Vector wi; float pdf; BxDFType flags; // Get random numbers for sampling outgoing photon direction float u1, u2, u3; if (nIntersections == 1) { u1 = (float)RadicalInverse((int)nshot+1, 13); u2 = (float)RadicalInverse((int)nshot+1, 17); u3 = (float)RadicalInverse((int)nshot+1, 19); } else { u1 = RandomFloat(); u2 = RandomFloat(); u3 = RandomFloat(); } Spectrum fr = photonBSDF->Sample_f(wo, &wi, u1, u2, u3, &pdf, BSDF_ALL, &flags); if (fr.Black() || pdf == 0.f) break; specularPath = (nIntersections == 1 || specularPath) && ((flags & BSDF_SPECULAR) != 0); alpha *= fr * AbsDot(wi, photonBSDF->dgShading.nn) / pdf; photonRay = RayDifferential(photonIsect.dg.p, wi); // Possibly terminate photon path if (nIntersections > 3) { float continueProbability = .5f; if (RandomFloat() > continueProbability) break; alpha /= continueProbability; } } } BSDF::FreeAll(); } progress.Done(); // NOBOOK }
// return the radiance of a specific direction // note : there are one factor makes the method biased. // there is a limitation on the number of vertexes in the path Spectrum PathTracing::Li( const Ray& ray , const PixelSample& ps ) const { Spectrum L = 0.0f; Spectrum throughput = 1.0f; int bounces = 0; Ray r = ray; while(true) { Intersection inter; // get the intersection between the ray and the scene // if it's a light , accumulate the radiance and break if( false == scene.GetIntersect( r , &inter ) ) { if( bounces == 0 ) return scene.Le( r ); break; } if( bounces == 0 ) L+=inter.Le(-r.m_Dir); // make sure there is intersected primitive Sort_Assert( inter.primitive != 0 ); // evaluate the light Bsdf* bsdf = inter.primitive->GetMaterial()->GetBsdf(&inter); float light_pdf = 0.0f; LightSample light_sample = (bounces==0)?ps.light_sample[0]:LightSample(true); BsdfSample bsdf_sample = (bounces==0)?ps.bsdf_sample[0]:BsdfSample(true); const Light* light = scene.SampleLight( light_sample.t , &light_pdf ); if( light_pdf > 0.0f ) L += throughput * EvaluateDirect( r , scene , light , inter , light_sample , bsdf_sample , BXDF_TYPE(BXDF_ALL) ) / light_pdf; // sample the next direction using bsdf float path_pdf; Vector wi; BXDF_TYPE bxdf_type; Spectrum f; BsdfSample _bsdf_sample = (bounces==0)?ps.bsdf_sample[1]:BsdfSample(true); f = bsdf->sample_f( -r.m_Dir , wi , _bsdf_sample , &path_pdf , BXDF_ALL , &bxdf_type ); if( f.IsBlack() || path_pdf == 0.0f ) break; // update path weight throughput *= f * AbsDot( wi , inter.normal ) / path_pdf; if( throughput.GetIntensity() == 0.0f ) break; if( bounces > 4 ) { float continueProperbility = min( 0.5f , throughput.GetIntensity() ); if( sort_canonical() > continueProperbility ) break; throughput /= continueProperbility; } r.m_Ori = inter.intersect; r.m_Dir = wi; r.m_fMin = 0.001f; ++bounces; // note : the following code makes the method biased // 'path_per_pixel' could be set very large to reduce the side-effect. if( bounces >= max_recursive_depth ) break; } return L; }
// main program int main(int argc, char *argv[]) { Spectrum blanc(1.0); FresnelOne myFresnel; IsotropicBeckmann myD(1.0); //Blinn myD(0.5); MSHAC myMSHAC(&myD); Microfacetpp myBRDF(blanc, &myFresnel, &myD, &myMSHAC); Spectrum integral(0.0); float theta_o = M_PI / 2.2f; float phi_o = 0.f; Vector w_o(cosf(phi_o) * sinf(theta_o), sinf(phi_o) * sinf(theta_o), cosf(theta_o)); Vector w_i(0.0,0.0,1.0); Vector w_g(0.0, 0.0, 1.0); //float myG = myMSHAC.G(w_o,w_i,Normalize(w_o + w_i)); //float lambda(myD.Lambda(w_o)); float deltaPhi = 0.01, deltaTheta = 0.01; /*RNG myRNG; const int n(300); const int nSamplers(n*n); float samples[nSamplers*2];*/ /*for (int i(0); i < n; i++) { float u1 = (float)i / (float)n; for (int j(0); j < n; j++) { float u2 = (float)j / (float)n; samples[i * n + j * 2] = u1; samples[i * n + j * 2 + 1] = u2; } }*/ /*for (int i(0); i < nSamplers * 2; i++) { samples[i] = myRNG.RandomFloat(); }*/ for (float phi = 0.0; phi <= 2 * M_PI; phi += deltaPhi) { for (float theta = 0.0; theta <= M_PI / 2.0; theta += deltaTheta) { w_i.x = cos(phi) * sin(theta); w_i.y = sin(phi) * sin(theta); w_i.z = cos(theta); integral += myBRDF.f(w_o, w_i) * AbsDot(w_g,w_i) * deltaPhi * deltaTheta * sin(theta); //integral += myD.D(w_i) * AbsDot(w_g, w_i) * deltaPhi * deltaTheta * sin(theta); } } integral = Spectrum(1.0) - integral; //Spectrum retour = myBRDF.rho(w_o,nSamplers, samples); /*Options options; vector<string> filenames; // Process command-line arguments for (int i = 1; i < argc; ++i) { if (!strcmp(argv[i], "--ncores")) options.nCores = atoi(argv[++i]); else if (!strcmp(argv[i], "--outfile")) options.imageFile = argv[++i]; else if (!strcmp(argv[i], "--quick")) options.quickRender = true; else if (!strcmp(argv[i], "--quiet")) options.quiet = true; else if (!strcmp(argv[i], "--verbose")) options.verbose = true; else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) { printf("usage: pbrt [--ncores n] [--outfile filename] [--quick] [--quiet] " "[--verbose] [--help] <filename.pbrt> ...\n"); return 0; } else filenames.push_back(argv[i]); } // Print welcome banner if (!options.quiet) { printf("pbrt version %s of %s at %s [Detected %d core(s)]\n", PBRT_VERSION, __DATE__, __TIME__, NumSystemCores()); printf("Copyright (c)1998-2014 Matt Pharr and Greg Humphreys.\n"); printf("The source code to pbrt (but *not* the book contents) is covered by the BSD License.\n"); printf("See the file LICENSE.txt for the conditions of the license.\n"); fflush(stdout); } pbrtInit(options); // Process scene description PBRT_STARTED_PARSING(); if (filenames.size() == 0) { // Parse scene from standard input ParseFile("-"); } else { // Parse scene from input files for (u_int i = 0; i < filenames.size(); i++) if (!ParseFile(filenames[i])) Error("Couldn't open scene file \"%s\"", filenames[i].c_str()); } pbrtCleanup();*/ return 0; }
void HashGridLookup::ReHash(float currentPhotonRadius2) { #else void HashGridLookup::ReHash(float /*currentPhotonRadius*/) { #endif const unsigned int hitPointsCount = engine->hitPointTotal; const BBox &hpBBox = hitPointsbbox; // Calculate the size of the grid cell #if defined USE_SPPMPA || defined USE_PPMPA float maxPhotonRadius2 = currentPhotonRadius2; #else float maxPhotonRadius2 = 0.f; for (unsigned int i = 0; i < hitPointsCount; ++i) { HitPointStaticInfo *ihp = &workerHitPointsInfo[i]; HitPoint *hp = &workerHitPoints[i]; if (ihp->type == SURFACE) maxPhotonRadius2 = Max(maxPhotonRadius2, hp->accumPhotonRadius2); } #endif const float cellSize = sqrtf(maxPhotonRadius2) * 2.f; //std::cerr << "Hash grid cell size: " << cellSize << std::endl; invCellSize = 1.f / cellSize; // TODO: add a tunable parameter for hashgrid size //hashGridSize = hitPointsCount; if (!hashGrid) { hashGrid = new std::list<uint>*[hashGridSize]; for (unsigned int i = 0; i < hashGridSize; ++i) hashGrid[i] = NULL; } else { for (unsigned int i = 0; i < hashGridSize; ++i) { delete hashGrid[i]; hashGrid[i] = NULL; } } //std::cerr << "Building hit points hash grid:" << std::endl; //std::cerr << " 0k/" << hitPointsCount / 1000 << "k" << std::endl; //unsigned int maxPathCount = 0; // double lastPrintTime = WallClockTime(); unsigned long long entryCount = 0; for (unsigned int i = 0; i < hitPointsCount; ++i) { // if (WallClockTime() - lastPrintTime > 2.0) { // std::cerr << " " << i / 1000 << "k/" << hitPointsCount / 1000 << "k" << std::endl; // lastPrintTime = WallClockTime(); // } //HitPointInfo *hp = engine->GetHitPointInfo(i); HitPointStaticInfo *hp = &workerHitPointsInfo[i]; if (hp->type == SURFACE) { #if defined USE_SPPMPA || defined USE_PPMPA const float photonRadius = sqrtf(currentPhotonRadius2); #else HitPoint *hpp = &workerHitPoints[i]; const float photonRadius = sqrtf(hpp->accumPhotonRadius2); #endif const Vector rad(photonRadius, photonRadius, photonRadius); const Vector bMin = ((hp->position - rad) - hpBBox.pMin) * invCellSize; const Vector bMax = ((hp->position + rad) - hpBBox.pMin) * invCellSize; for (int iz = abs(int(bMin.z)); iz <= abs(int(bMax.z)); iz++) { for (int iy = abs(int(bMin.y)); iy <= abs(int(bMax.y)); iy++) { for (int ix = abs(int(bMin.x)); ix <= abs(int(bMax.x)); ix++) { int hv = Hash(ix, iy, iz); //if (hv == engine->hitPointTotal - 1) if (hashGrid[hv] == NULL) hashGrid[hv] = new std::list<uint>(); // std::list<unsigned int>* hps = hashGrid[hv]; // if (hps) { // std::list<unsigned int>::iterator iter = hps->begin(); // while (iter != hps->end()) { // if (*iter == i) { // printf("found"); // exit(0); // } // } // } hashGrid[hv]->push_front(i); ++entryCount; /*// hashGrid[hv]->size() is very slow to execute if (hashGrid[hv]->size() > maxPathCount) maxPathCount = hashGrid[hv]->size();*/ } } } } } hashGridEntryCount = entryCount; //std::cerr << "Max. hit points in a single hash grid entry: " << maxPathCount << std::endl; // std::cerr << "Total hash grid entry: " << entryCount << std::endl; // std::cerr << "Avg. hit points in a single hash grid entry: " << entryCount // / hashGridSize << std::endl; //printf("Sizeof %d\n", sizeof(HitPoint*)); // HashGrid debug code /*for (unsigned int i = 0; i < hashGridSize; ++i) { if (hashGrid[i]) { if (hashGrid[i]->size() > 10) { std::cerr << "HashGrid[" << i << "].size() = " <<hashGrid[i]->size() << std::endl; } } }*/ } #if defined USE_SPPM || defined USE_PPM void HashGridLookup::AddFlux(PointerFreeScene *ss, const float /*alpha*/, const Point &hitPoint, const Normal &shadeN, const Vector wi, const Spectrum photonFlux, float /*currentPhotonRadius2*/) { #else void HashGridLookup::AddFlux(PointerFreeScene *ss, const float /*alpha*/, const Point &hitPoint, const Normal &shadeN, const Vector wi, const Spectrum photonFlux, float currentPhotonRadius2) { #endif // Look for eye path hit points near the current hit point Vector hh = (hitPoint - hitPointsbbox.pMin) * invCellSize; const int ix = abs(int(hh.x)); const int iy = abs(int(hh.y)); const int iz = abs(int(hh.z)); // std::list<uint> *hps = hashGrid[Hash(ix, iy, iz, hashGridSize)]; // if (hps) { // std::list<uint>::iterator iter = hps->begin(); // while (iter != hps->end()) { // // HitPoint *hp = &hitPoints[*iter++]; uint gridEntry = Hash(ix, iy, iz); std::list<unsigned int>* hps = hashGrid[gridEntry]; if (hps) { std::list<unsigned int>::iterator iter = hps->begin(); while (iter != hps->end()) { HitPointStaticInfo *hp = &workerHitPointsInfo[*iter]; HitPoint *ihp = &workerHitPoints[*iter++]; // Vector v = hp->position - hitPoint; #if defined USE_SPPM || defined USE_PPM //if ((Dot(hp->normal, shadeN) > 0.5f) && (Dot(v, v) <= ihp->accumPhotonRadius2)) { const float dist2 = DistanceSquared(hp->position, hitPoint); if ((dist2 > ihp->accumPhotonRadius2)) continue; const float dot = Dot(hp->normal, wi); if (dot <= 0.0001f) continue; #else const float dist2 = DistanceSquared(hp->position, hitPoint); if ((dist2 > currentPhotonRadius2)) continue; const float dot = Dot(hp->normal, wi); if (dot <= 0.0001f) continue; #endif //const float g = (hp->accumPhotonCount * alpha + alpha) // / (hp->accumPhotonCount * alpha + 1.f); //hp->photonRadius2 *= g; __sync_fetch_and_add(&ihp->accumPhotonCount, 1); Spectrum f; POINTERFREESCENE::Material *hitPointMat = &ss->materials[hp->materialSS]; switch (hitPointMat->type) { case MAT_MATTE: ss->Matte_f(&hitPointMat->param.matte, hp->wo, wi, shadeN, f); break; case MAT_MATTEMIRROR: ss->MatteMirror_f(&hitPointMat->param.matteMirror, hp->wo, wi, shadeN, f); break; case MAT_MATTEMETAL: ss->MatteMetal_f(&hitPointMat->param.matteMetal, hp->wo, wi, shadeN, f); break; case MAT_ALLOY: ss->Alloy_f(&hitPointMat->param.alloy, hp->wo, wi, shadeN, f); break; default: break; } //f = hp->material->f(hp->wo, wi, shadeN); Spectrum flux = photonFlux * AbsDot(shadeN, wi) * hp->throughput * f; //if ((*iter-1) == 200) printf("%f\n", photonFlux.g); #pragma omp critical { ihp->accumReflectedFlux = (ihp->accumReflectedFlux + flux) /** g*/; } // // hp->accumReflectedFlux.r += flux.r; // hp->accumReflectedFlux.r += flux.r; // hp->accumReflectedFlux.r += flux.r; //printf("%f\n", f.r); } } }
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; }
Spectrum PathIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &r, const Intersection &isect, const Sample *sample, RNG &rng, 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 bounces = 0; ; ++bounces) { // Possibly add emitted light at path vertex if (bounces == 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 (bounces < SAMPLE_DEPTH) L += pathThroughput * UniformSampleOneLight(scene, renderer, arena, p, n, wo, isectp->rayEpsilon, ray.time, bsdf, sample, rng, lightNumOffset[bounces], &lightSampleOffsets[bounces], &bsdfSampleOffsets[bounces]); else L += pathThroughput * UniformSampleOneLight(scene, renderer, arena, p, n, wo, isectp->rayEpsilon, ray.time, bsdf, sample, rng); // Sample BSDF to get new path direction // Get _outgoingBSDFSample_ for sampling new path direction BSDFSample outgoingBSDFSample; if (bounces < SAMPLE_DEPTH) outgoingBSDFSample = BSDFSample(sample, pathSampleOffsets[bounces], 0); else outgoingBSDFSample = BSDFSample(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 (bounces > 3) { float continueProbability = min(.5f, pathThroughput.y()); if (rng.RandomFloat() > continueProbability) break; pathThroughput /= continueProbability; } if (bounces == maxDepth) break; // Find next vertex of path if (!scene->Intersect(ray, &localIsect)) { if (specularBounce) for (uint32_t i = 0; i < scene->lights.size(); ++i) L += pathThroughput * scene->lights[i]->Le(ray); break; } if (bounces > 1) pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena); isectp = &localIsect; } return L; }
Spectrum EstimateDirect(const Interaction &it, const Point2f &uScattering, const Light &light, const Point2f &uLight, const Scene &scene, Sampler &sampler, MemoryArena &arena, bool handleMedia, bool specular) { BxDFType bsdfFlags = specular ? BSDF_ALL : BxDFType(BSDF_ALL & ~BSDF_SPECULAR); Spectrum Ld(0.f); // Sample light source with multiple importance sampling Vector3f wi; Float lightPdf = 0, scatteringPdf = 0; VisibilityTester visibility; Spectrum Li = light.Sample_Li(it, uLight, &wi, &lightPdf, &visibility); if (lightPdf > 0 && !Li.IsBlack()) { // Compute BSDF or phase function's value for light sample Spectrum f; if (it.IsSurfaceInteraction()) { // Evaluate BSDF for light sampling strategy const SurfaceInteraction &isect = (const SurfaceInteraction &)it; f = isect.bsdf->f(isect.wo, wi, bsdfFlags) * AbsDot(wi, isect.shading.n); scatteringPdf = isect.bsdf->Pdf(isect.wo, wi, bsdfFlags); } else { // Evaluate phase function for light sampling strategy const MediumInteraction &mi = (const MediumInteraction &)it; Float p = mi.phase->p(mi.wo, wi); f = Spectrum(p); scatteringPdf = p; } if (!f.IsBlack()) { // Compute effect of visibility for light source sample if (handleMedia) Li *= visibility.Tr(scene, sampler); else if (!visibility.Unoccluded(scene)) Li = Spectrum(0.f); // Add light's contribution to reflected radiance if (!Li.IsBlack()) { if (IsDeltaLight(light.flags)) Ld += f * Li / lightPdf; else { Float weight = PowerHeuristic(1, lightPdf, 1, scatteringPdf); Ld += f * Li * weight / lightPdf; } } } } // Sample BSDF with multiple importance sampling if (!IsDeltaLight(light.flags)) { Spectrum f; bool sampledSpecular = false; if (it.IsSurfaceInteraction()) { // Sample scattered direction for surface interactions BxDFType sampledType; const SurfaceInteraction &isect = (const SurfaceInteraction &)it; f = isect.bsdf->Sample_f(isect.wo, &wi, uScattering, &scatteringPdf, bsdfFlags, &sampledType); f *= AbsDot(wi, isect.shading.n); sampledSpecular = sampledType & BSDF_SPECULAR; } else { // Sample scattered direction for medium interactions const MediumInteraction &mi = (const MediumInteraction &)it; Float p = mi.phase->Sample_p(mi.wo, &wi, uScattering); f = Spectrum(p); scatteringPdf = p; } if (!f.IsBlack() && scatteringPdf > 0) { // Account for light contributions along sampled direction _wi_ Float weight = 1; if (!sampledSpecular) { lightPdf = light.Pdf_Li(it, wi); if (lightPdf == 0) return Ld; weight = PowerHeuristic(1, scatteringPdf, 1, lightPdf); } // Find intersection and compute transmittance SurfaceInteraction lightIsect; Ray ray = it.SpawnRay(wi); Spectrum Tr(1.f); bool foundSurfaceInteraction = handleMedia ? scene.IntersectTr(ray, sampler, &lightIsect, &Tr) : scene.Intersect(ray, &lightIsect); // Add light contribution from material sampling Spectrum Li(0.f); if (foundSurfaceInteraction) { if (lightIsect.primitive->GetAreaLight() == &light) Li = lightIsect.Le(-wi); } else Li = light.Le(ray); if (!Li.IsBlack()) Ld += f * Li * Tr * weight / scatteringPdf; } } return Ld; }
void DipoleSubsurfaceIntegrator::Preprocess(const Scene *scene, const Camera *camera, const Renderer *renderer) { if (scene->lights.size() == 0) return; vector<SurfacePoint> pts; // Get _SurfacePoint_s for translucent objects in scene if (filename != "") { // Initialize _SurfacePoint_s from file vector<float> fpts; if (ReadFloatFile(filename.c_str(), &fpts)) { if ((fpts.size() % 8) != 0) Error("Excess values (%d) in points file \"%s\"", int(fpts.size() % 8), filename.c_str()); for (u_int i = 0; i < fpts.size(); i += 8) pts.push_back(SurfacePoint(Point(fpts[i], fpts[i+1], fpts[i+2]), Normal(fpts[i+3], fpts[i+4], fpts[i+5]), fpts[i+6], fpts[i+7])); } } if (pts.size() == 0) { Point pCamera = camera->CameraToWorld(camera->shutterOpen, Point(0, 0, 0)); FindPoissonPointDistribution(pCamera, camera->shutterOpen, minSampleDist, scene, &pts); } // Compute irradiance values at sample points RNG rng; MemoryArena arena; PBRT_SUBSURFACE_STARTED_COMPUTING_IRRADIANCE_VALUES(); ProgressReporter progress(pts.size(), "Computing Irradiances"); for (uint32_t i = 0; i < pts.size(); ++i) { SurfacePoint &sp = pts[i]; Spectrum E(0.f); for (uint32_t j = 0; j < scene->lights.size(); ++j) { // Add irradiance from light at point const Light *light = scene->lights[j]; Spectrum Elight = 0.f; int nSamples = RoundUpPow2(light->nSamples); uint32_t scramble[2] = { rng.RandomUInt(), rng.RandomUInt() }; uint32_t compScramble = rng.RandomUInt(); for (int s = 0; s < nSamples; ++s) { float lpos[2]; Sample02(s, scramble, lpos); float lcomp = VanDerCorput(s, compScramble); LightSample ls(lpos[0], lpos[1], lcomp); Vector wi; float lightPdf; VisibilityTester visibility; Spectrum Li = light->Sample_L(sp.p, sp.rayEpsilon, ls, camera->shutterOpen, &wi, &lightPdf, &visibility); if (Dot(wi, sp.n) <= 0.) continue; if (Li.IsBlack() || lightPdf == 0.f) continue; Li *= visibility.Transmittance(scene, renderer, NULL, rng, arena); if (visibility.Unoccluded(scene)) Elight += Li * AbsDot(wi, sp.n) / lightPdf; } E += Elight / nSamples; } irradiancePoints.push_back(IrradiancePoint(sp, E)); PBRT_SUBSURFACE_COMPUTED_IRRADIANCE_AT_POINT(&sp, &E); arena.FreeAll(); progress.Update(); } progress.Done(); PBRT_SUBSURFACE_FINISHED_COMPUTING_IRRADIANCE_VALUES(); // Create octree of clustered irradiance samples octree = octreeArena.Alloc<SubsurfaceOctreeNode>(); for (uint32_t i = 0; i < irradiancePoints.size(); ++i) octreeBounds = Union(octreeBounds, irradiancePoints[i].p); for (uint32_t i = 0; i < irradiancePoints.size(); ++i) octree->Insert(octreeBounds, &irradiancePoints[i], octreeArena); octree->InitHierarchy(); }
Spectrum IGIIntegrator::Li(const Scene *scene, const RayDifferential &ray, const Sample *sample, float *alpha) const { Spectrum L(0.); Intersection isect; if (scene->Intersect(ray, &isect)) { if (alpha) *alpha = 1.; 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); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; L += UniformSampleAllLights(scene, p, n, wo, bsdf, sample, lightSampleOffset, bsdfSampleOffset, bsdfComponentOffset); // Compute indirect illumination with virtual lights u_int lSet = min(u_int(sample->oneD[vlSetOffset][0] * nLightSets), nLightSets-1); for (u_int i = 0; i < virtualLights[lSet].size(); ++i) { const VirtualLight &vl = virtualLights[lSet][i]; // Add contribution from _VirtualLight_ _vl_ // Ignore light if it's too close to current point float d2 = DistanceSquared(p, vl.p); //if (d2 < .8 * minDist2) continue; float distScale = SmoothStep(.8 * minDist2, 1.2 * minDist2, d2); // Compute virtual light's tentative contribution _Llight_ Vector wi = Normalize(vl.p - p); Spectrum f = distScale * bsdf->f(wo, wi); if (f.Black()) continue; float G = AbsDot(wi, n) * AbsDot(wi, vl.n) / d2; Spectrum Llight = indirectScale * f * G * vl.Le / virtualLights[lSet].size(); Llight *= scene->Transmittance(Ray(p, vl.p - p)); // Possibly skip shadow ray with Russian roulette if (Llight.y() < rrThreshold) { float continueProbability = .1f; if (RandomFloat() > continueProbability) continue; Llight /= continueProbability; } static StatsCounter vlsr("IGI Integrator", "Shadow Rays to Virtual Lights"); //NOBOOK ++vlsr; //NOBOOK if (!scene->IntersectP(Ray(p, vl.p - p, RAY_EPSILON, 1.f - RAY_EPSILON))) L += Llight; } // Trace rays for specular reflection and refraction if (specularDepth++ < maxSpecularDepth) { 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); } } --specularDepth; } 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; }
Spectrum MetropolisRenderer::Lbidir(const Scene *scene, const PathVertex *cameraPath, int cameraPathLength, const PathVertex *lightPath, int lightPathLength, MemoryArena &arena, const vector<LightingSample> &samples, RNG &rng, float time, const Distribution1D *lightDistribution, const RayDifferential &eRay, const Spectrum &eAlpha) const { PBRT_MLT_STARTED_LBIDIR(); Spectrum L = 0.; bool previousSpecular = true, allSpecular = true; // Compute number of specular vertices for each path length int nVerts = cameraPathLength + lightPathLength + 2; int *nSpecularVertices = ALLOCA(int, nVerts); memset(nSpecularVertices, 0, nVerts * sizeof(int)); for (int i = 0; i < cameraPathLength; ++i) for (int j = 0; j < lightPathLength; ++j) if (cameraPath[i].specularBounce || lightPath[j].specularBounce) ++nSpecularVertices[i+j+2]; for (int i = 0; i < cameraPathLength; ++i) { // Initialize basic variables for camera path vertex const PathVertex &vc = cameraPath[i]; const Point &pc = vc.bsdf->dgShading.p; const Normal &nc = vc.bsdf->dgShading.nn; // Compute reflected light at camera path vertex // Add emitted light from vertex if appropriate if (previousSpecular && (directLighting == NULL || !allSpecular)) L += vc.alpha * vc.isect.Le(vc.wPrev); // Compute direct illumination for Metropolis path vertex Spectrum Ld(0.f); if (directLighting == NULL || !allSpecular) { // Choose light and call _EstimateDirect()_ for Metropolis vertex const LightingSample &ls = samples[i]; float lightPdf; uint32_t lightNum = lightDistribution->SampleDiscrete(ls.lightNum, &lightPdf); const Light *light = scene->lights[lightNum]; PBRT_MLT_STARTED_ESTIMATE_DIRECT(); Ld = vc.alpha * EstimateDirect(scene, this, arena, light, pc, nc, vc.wPrev, vc.isect.rayEpsilon, time, vc.bsdf, rng, NULL, ls.lightSample, ls.bsdfSample, BxDFType(BSDF_ALL & ~BSDF_SPECULAR)) / lightPdf; PBRT_MLT_FINISHED_ESTIMATE_DIRECT(); } previousSpecular = vc.specularBounce; allSpecular &= previousSpecular; L += Ld / (i + 1 - nSpecularVertices[i+1]); if (!vc.specularBounce) { // Loop over light path vertices and connect to camera vertex for (int j = 0; j < lightPathLength; ++j) { const PathVertex &vl = lightPath[j]; const Point &pl = vl.bsdf->dgShading.p; const Normal &nl = vl.bsdf->dgShading.nn; if (!vl.specularBounce) { // Compute contribution between camera and light vertices Vector w = Normalize(pl - pc); Spectrum fc = vc.bsdf->f(vc.wPrev, w) * (1 + vc.nSpecularComponents); Spectrum fl = vl.bsdf->f(-w, vl.wPrev) * (1 + vl.nSpecularComponents); if (fc.IsBlack() || fl.IsBlack()) continue; Ray r(pc, pl - pc, 1e-3f, .999f, time); if (!scene->IntersectP(r)) { // Compute weight for bidirectional path, _pathWt_ float pathWt = 1.f / (i + j + 2 - nSpecularVertices[i+j+2]); float G = AbsDot(nc, w) * AbsDot(nl, w) / DistanceSquared(pl, pc); L += (vc.alpha * fc * G * fl * vl.alpha) * pathWt; } } } } } // Add contribution of escaped ray, if any if (!eAlpha.IsBlack() && previousSpecular && (directLighting == NULL || !allSpecular)) for (uint32_t i = 0; i < scene->lights.size(); ++i) L += eAlpha * scene->lights[i]->Le(eRay); PBRT_MLT_FINISHED_LBIDIR(); return L; }
void IGIIntegrator::Preprocess(const Scene *scene) { if (scene->lights.size() == 0) return; // Compute samples for emitted rays from lights float *lightNum = new float[nLightPaths * nLightSets]; float *lightSamp0 = new float[2 * nLightPaths * nLightSets]; float *lightSamp1 = new float[2 * nLightPaths * nLightSets]; LDShuffleScrambled1D(nLightPaths, nLightSets, lightNum); LDShuffleScrambled2D(nLightPaths, nLightSets, lightSamp0); LDShuffleScrambled2D(nLightPaths, nLightSets, lightSamp1); // Precompute information for light sampling densities int nLights = int(scene->lights.size()); float *lightPower = (float *)alloca(nLights * sizeof(float)); float *lightCDF = (float *)alloca((nLights+1) * sizeof(float)); for (int i = 0; i < nLights; ++i) lightPower[i] = scene->lights[i]->Power(scene).y(); float totalPower; ComputeStep1dCDF(lightPower, nLights, &totalPower, lightCDF); for (u_int s = 0; s < nLightSets; ++s) { for (u_int i = 0; i < nLightPaths; ++i) { // Follow path _i_ from light to create virtual lights int sampOffset = s*nLightPaths + i; // Choose light source to trace path from float lightPdf; int lNum = Floor2Int(SampleStep1d(lightPower, lightCDF, totalPower, nLights, lightNum[sampOffset], &lightPdf) * nLights); // fprintf(stderr, "samp %f -> num %d\n", lightNum[sampOffset], lNum); Light *light = scene->lights[lNum]; // Sample ray leaving light source RayDifferential ray; float pdf; Spectrum alpha = light->Sample_L(scene, lightSamp0[2*sampOffset], lightSamp0[2*sampOffset+1], lightSamp1[2*sampOffset], lightSamp1[2*sampOffset+1], &ray, &pdf); if (pdf == 0.f || alpha.Black()) continue; alpha /= pdf * lightPdf; // fprintf(stderr, "initial alpha %f, light # %d\n", alpha.y(), lNum); Intersection isect; int nIntersections = 0; while (scene->Intersect(ray, &isect) && !alpha.Black()) { ++nIntersections; alpha *= scene->Transmittance(ray); Vector wo = -ray.d; BSDF *bsdf = isect.GetBSDF(ray); // Create virtual light at ray intersection point static StatsCounter vls("IGI Integrator", "Virtual Lights Created"); //NOBOOK ++vls; //NOBOOK Spectrum Le = alpha * bsdf->rho(wo) / M_PI; // fprintf(stderr, "\tmade light with le y %f\n", Le.y()); virtualLights[s].push_back(VirtualLight(isect.dg.p, isect.dg.nn, Le)); // Sample new ray direction and update weight Vector wi; float pdf; BxDFType flags; Spectrum fr = bsdf->Sample_f(wo, &wi, RandomFloat(), RandomFloat(), RandomFloat(), &pdf, BSDF_ALL, &flags); if (fr.Black() || pdf == 0.f) break; Spectrum anew = alpha * fr * AbsDot(wi, bsdf->dgShading.nn) / pdf; float r = anew.y() / alpha.y(); // fprintf(stderr, "\tr = %f\n", r); if (RandomFloat() > r) break; alpha = anew / r; // fprintf(stderr, "\tnew alpha %f\n", alpha.y()); ray = RayDifferential(isect.dg.p, wi); } BSDF::FreeAll(); } } delete[] lightNum; // NOBOOK delete[] lightSamp0; // NOBOOK delete[] lightSamp1; // NOBOOK }
Spectrum PhotonIntegrator::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 PbrtPoint &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 caustic lighting for photon map integrator ClosePhoton *lookupBuf = arena.Alloc<ClosePhoton>(nLookup); L += LPhoton(causticMap, nCausticPaths, nLookup, lookupBuf, bsdf, rng, isect, wo, maxDistSquared); // Compute indirect lighting for photon map integrator if (finalGather && indirectMap != NULL) { #if 1 // Do one-bounce final gather for photon map BxDFType nonSpecular = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_DIFFUSE | BSDF_GLOSSY); if (bsdf->NumComponents(nonSpecular) > 0) { // Find indirect photons around point for importance sampling const uint32_t nIndirSamplePhotons = 50; PhotonProcess proc(nIndirSamplePhotons, arena.Alloc<ClosePhoton>(nIndirSamplePhotons)); float searchDist2 = maxDistSquared; while (proc.nFound < nIndirSamplePhotons) { float md2 = searchDist2; proc.nFound = 0; indirectMap->Lookup(p, proc, md2); searchDist2 *= 2.f; } // Copy photon directions to local array Vector *photonDirs = arena.Alloc<Vector>(nIndirSamplePhotons); for (uint32_t i = 0; i < nIndirSamplePhotons; ++i) photonDirs[i] = proc.photons[i].photon->wi; // Use BSDF to do final gathering Spectrum Li = 0.; for (int i = 0; i < gatherSamples; ++i) { // Sample random direction from BSDF for final gather ray Vector wi; float pdf; BSDFSample bsdfSample(sample, bsdfGatherSampleOffsets, i); Spectrum fr = bsdf->Sample_f(wo, &wi, bsdfSample, &pdf, BxDFType(BSDF_ALL & ~BSDF_SPECULAR)); if (fr.IsBlack() || pdf == 0.f) continue; Assert(pdf >= 0.f); // Trace BSDF final gather ray and accumulate radiance RayDifferential bounceRay(p, wi, ray, isect.rayEpsilon); Intersection gatherIsect; if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance _Lindir_ using radiance photons Spectrum Lindir = 0.f; Normal nGather = gatherIsect.dg.nn; nGather = Faceforward(nGather, -bounceRay.d); RadiancePhotonProcess proc(nGather); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon != NULL) Lindir = proc.photon->Lo; Lindir *= renderer->Transmittance(scene, bounceRay, NULL, rng, arena); // Compute MIS weight for BSDF-sampled gather ray // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (uint32_t j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; float wt = PowerHeuristic(gatherSamples, pdf, gatherSamples, photonPdf); Li += fr * Lindir * (AbsDot(wi, n) * wt / pdf); } } L += Li / gatherSamples; // Use nearby photons to do final gathering Li = 0.; for (int i = 0; i < gatherSamples; ++i) { // Sample random direction using photons for final gather ray BSDFSample gatherSample(sample, indirGatherSampleOffsets, i); int photonNum = min((int)nIndirSamplePhotons - 1, Floor2Int(gatherSample.uComponent * nIndirSamplePhotons)); // Sample gather ray direction from _photonNum_ Vector vx, vy; CoordinateSystem(photonDirs[photonNum], &vx, &vy); Vector wi = UniformSampleCone(gatherSample.uDir[0], gatherSample.uDir[1], cosGatherAngle, vx, vy, photonDirs[photonNum]); // Trace photon-sampled final gather ray and accumulate radiance Spectrum fr = bsdf->f(wo, wi); if (fr.IsBlack()) continue; RayDifferential bounceRay(p, wi, ray, isect.rayEpsilon); Intersection gatherIsect; PBRT_PHOTON_MAP_STARTED_GATHER_RAY(&bounceRay); if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance _Lindir_ using radiance photons Spectrum Lindir = 0.f; Normal nGather = gatherIsect.dg.nn; nGather = Faceforward(nGather, -bounceRay.d); RadiancePhotonProcess proc(nGather); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon != NULL) Lindir = proc.photon->Lo; Lindir *= renderer->Transmittance(scene, bounceRay, NULL, rng, arena); // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (uint32_t j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; // Compute MIS weight for photon-sampled gather ray float bsdfPdf = bsdf->Pdf(wo, wi); float wt = PowerHeuristic(gatherSamples, photonPdf, gatherSamples, bsdfPdf); Li += fr * Lindir * AbsDot(wi, n) * wt / photonPdf; } PBRT_PHOTON_MAP_FINISHED_GATHER_RAY(&bounceRay); } L += Li / gatherSamples; } #else // for debugging / examples: use the photon map directly Normal nn = Faceforward(n, -ray.d); RadiancePhotonProcess proc(nn); float md2 = INFINITY; radianceMap->Lookup(p, proc, md2); if (proc.photon) L += proc.photon->Lo; #endif } else L += LPhoton(indirectMap, nIndirectPaths, nLookup, lookupBuf, bsdf, rng, isect, wo, maxDistSquared); 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; }
void ExPhotonIntegrator::Preprocess(const Scene *scene) { if (scene->lights.size() == 0) return; ProgressReporter progress(nCausticPhotons+ // NOBOOK nIndirectPhotons, "Shooting photons"); // NOBOOK vector<Photon> causticPhotons; vector<Photon> indirectPhotons; vector<Photon> directPhotons; vector<RadiancePhoton> radiancePhotons; causticPhotons.reserve(nCausticPhotons); // NOBOOK indirectPhotons.reserve(nIndirectPhotons); // NOBOOK // Initialize photon shooting statistics static StatsCounter nshot("Photon Map", "Number of photons shot from lights"); bool causticDone = (nCausticPhotons == 0); bool indirectDone = (nIndirectPhotons == 0); // Compute light power CDF for photon shooting int nLights = int(scene->lights.size()); float *lightPower = (float *)alloca(nLights * sizeof(float)); float *lightCDF = (float *)alloca((nLights+1) * sizeof(float)); for (int i = 0; i < nLights; ++i) lightPower[i] = scene->lights[i]->Power(scene).y(); float totalPower; ComputeStep1dCDF(lightPower, nLights, &totalPower, lightCDF); // Declare radiance photon reflectance arrays vector<Spectrum> rpReflectances, rpTransmittances; while (!causticDone || !indirectDone) { ++nshot; // Give up if we're not storing enough photons if (nshot > 500000 && (unsuccessful(nCausticPhotons, causticPhotons.size(), nshot) || unsuccessful(nIndirectPhotons, indirectPhotons.size(), nshot))) { Error("Unable to store enough photons. Giving up.\n"); return; } // Trace a photon path and store contribution // Choose 4D sample values for photon float u[4]; u[0] = RadicalInverse((int)nshot+1, 2); u[1] = RadicalInverse((int)nshot+1, 3); u[2] = RadicalInverse((int)nshot+1, 5); u[3] = RadicalInverse((int)nshot+1, 7); // Choose light to shoot photon from float lightPdf; float uln = RadicalInverse((int)nshot+1, 11); int lightNum = Floor2Int(SampleStep1d(lightPower, lightCDF, totalPower, nLights, uln, &lightPdf) * nLights); lightNum = min(lightNum, nLights-1); const Light *light = scene->lights[lightNum]; // Generate _photonRay_ from light source and initialize _alpha_ RayDifferential photonRay; float pdf; Spectrum alpha = light->Sample_L(scene, u[0], u[1], u[2], u[3], &photonRay, &pdf); if (pdf == 0.f || alpha.Black()) continue; alpha /= pdf * lightPdf; if (!alpha.Black()) { // Follow photon path through scene and record intersections bool specularPath = false; Intersection photonIsect; int nIntersections = 0; while (scene->Intersect(photonRay, &photonIsect)) { ++nIntersections; // Handle photon/surface intersection alpha *= scene->Transmittance(photonRay); Vector wo = -photonRay.d; BSDF *photonBSDF = photonIsect.GetBSDF(photonRay); BxDFType specularType = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_SPECULAR); bool hasNonSpecular = (photonBSDF->NumComponents() > photonBSDF->NumComponents(specularType)); if (hasNonSpecular) { // Deposit photon at surface Photon photon(photonIsect.dg.p, alpha, wo); if (nIntersections == 1) { // Deposit direct photon directPhotons.push_back(photon); } else { // Deposit either caustic or indirect photon if (specularPath) { // Process caustic photon intersection if (!causticDone) { causticPhotons.push_back(photon); if (causticPhotons.size() == nCausticPhotons) { causticDone = true; nCausticPaths = (int)nshot; causticMap = new KdTree<Photon, PhotonProcess>(causticPhotons); } progress.Update(); } } else { // Process indirect lighting photon intersection if (!indirectDone) { indirectPhotons.push_back(photon); if (indirectPhotons.size() == nIndirectPhotons) { indirectDone = true; nIndirectPaths = (int)nshot; indirectMap = new KdTree<Photon, PhotonProcess>(indirectPhotons); } progress.Update(); } } } if (finalGather && RandomFloat() < .125f) { // Store data for radiance photon static StatsCounter rp("Photon Map", "Radiance photons created"); // NOBOOK ++rp; // NOBOOK Normal n = photonIsect.dg.nn; if (Dot(n, photonRay.d) > 0.f) n = -n; radiancePhotons.push_back(RadiancePhoton(photonIsect.dg.p, n)); Spectrum rho_r = photonBSDF->rho(BSDF_ALL_REFLECTION); rpReflectances.push_back(rho_r); Spectrum rho_t = photonBSDF->rho(BSDF_ALL_TRANSMISSION); rpTransmittances.push_back(rho_t); } } // Sample new photon ray direction Vector wi; float pdf; BxDFType flags; // Get random numbers for sampling outgoing photon direction float u1, u2, u3; if (nIntersections == 1) { u1 = RadicalInverse((int)nshot+1, 13); u2 = RadicalInverse((int)nshot+1, 17); u3 = RadicalInverse((int)nshot+1, 19); } else { u1 = RandomFloat(); u2 = RandomFloat(); u3 = RandomFloat(); } // Compute new photon weight and possibly terminate with RR Spectrum fr = photonBSDF->Sample_f(wo, &wi, u1, u2, u3, &pdf, BSDF_ALL, &flags); if (fr.Black() || pdf == 0.f) break; Spectrum anew = alpha * fr * AbsDot(wi, photonBSDF->dgShading.nn) / pdf; float continueProb = min(1.f, anew.y() / alpha.y()); if (RandomFloat() > continueProb || nIntersections > 10) break; alpha = anew / continueProb; specularPath = (nIntersections == 1 || specularPath) && ((flags & BSDF_SPECULAR) != 0); photonRay = RayDifferential(photonIsect.dg.p, wi); } } BSDF::FreeAll(); } progress.Done(); // NOBOOK // Precompute radiance at a subset of the photons KdTree<Photon, PhotonProcess> directMap(directPhotons); int nDirectPaths = nshot; if (finalGather) { ProgressReporter p2(radiancePhotons.size(), "Computing photon radiances"); // NOBOOK for (u_int i = 0; i < radiancePhotons.size(); ++i) { // Compute radiance for radiance photon _i_ RadiancePhoton &rp = radiancePhotons[i]; const Spectrum &rho_r = rpReflectances[i]; const Spectrum &rho_t = rpTransmittances[i]; Spectrum E; Point p = rp.p; Normal n = rp.n; if (!rho_r.Black()) { E = estimateE(&directMap, nDirectPaths, p, n) + estimateE(indirectMap, nIndirectPaths, p, n) + estimateE(causticMap, nCausticPaths, p, n); rp.Lo += E * INV_PI * rho_r; } if (!rho_t.Black()) { E = estimateE(&directMap, nDirectPaths, p, -n) + estimateE(indirectMap, nIndirectPaths, p, -n) + estimateE(causticMap, nCausticPaths, p, -n); rp.Lo += E * INV_PI * rho_t; } p2.Update(); // NOBOOK } radianceMap = new KdTree<RadiancePhoton, RadiancePhotonProcess>(radiancePhotons); p2.Done(); // NOBOOK } }
Spectrum ConnectBDPT( const Scene &scene, Vertex *lightVertices, Vertex *cameraVertices, int s, int t, const Distribution1D &lightDistr, const std::unordered_map<const Light *, size_t> &lightToIndex, const Camera &camera, Sampler &sampler, Point2f *pRaster, Float *misWeightPtr) { Spectrum L(0.f); // Ignore invalid connections related to infinite area lights if (t > 1 && s != 0 && cameraVertices[t - 1].type == VertexType::Light) return Spectrum(0.f); // Perform connection and write contribution to _L_ Vertex sampled; if (s == 0) { // Interpret the camera subpath as a complete path const Vertex &pt = cameraVertices[t - 1]; if (pt.IsLight()) L = pt.Le(scene, cameraVertices[t - 2]) * pt.beta; DCHECK(!L.HasNaNs()); } else if (t == 1) { // Sample a point on the camera and connect it to the light subpath const Vertex &qs = lightVertices[s - 1]; if (qs.IsConnectible()) { VisibilityTester vis; Vector3f wi; Float pdf; Spectrum Wi = camera.Sample_Wi(qs.GetInteraction(), sampler.Get2D(), &wi, &pdf, pRaster, &vis); if (pdf > 0 && !Wi.IsBlack()) { // Initialize dynamically sampled vertex and _L_ for $t=1$ case sampled = Vertex::CreateCamera(&camera, vis.P1(), Wi / pdf); L = qs.beta * qs.f(sampled, TransportMode::Importance) * sampled.beta; if (qs.IsOnSurface()) L *= AbsDot(wi, qs.ns()); DCHECK(!L.HasNaNs()); // Only check visibility after we know that the path would // make a non-zero contribution. if (!L.IsBlack()) L *= vis.Tr(scene, sampler); } } } else if (s == 1) { // Sample a point on a light and connect it to the camera subpath const Vertex &pt = cameraVertices[t - 1]; if (pt.IsConnectible()) { Float lightPdf; VisibilityTester vis; Vector3f wi; Float pdf; int lightNum = lightDistr.SampleDiscrete(sampler.Get1D(), &lightPdf); const std::shared_ptr<Light> &light = scene.lights[lightNum]; Spectrum lightWeight = light->Sample_Li( pt.GetInteraction(), sampler.Get2D(), &wi, &pdf, &vis); if (pdf > 0 && !lightWeight.IsBlack()) { EndpointInteraction ei(vis.P1(), light.get()); sampled = Vertex::CreateLight(ei, lightWeight / (pdf * lightPdf), 0); sampled.pdfFwd = sampled.PdfLightOrigin(scene, pt, lightDistr, lightToIndex); L = pt.beta * pt.f(sampled, TransportMode::Radiance) * sampled.beta; if (pt.IsOnSurface()) L *= AbsDot(wi, pt.ns()); // Only check visibility if the path would carry radiance. if (!L.IsBlack()) L *= vis.Tr(scene, sampler); } } } else { // Handle all other bidirectional connection cases const Vertex &qs = lightVertices[s - 1], &pt = cameraVertices[t - 1]; if (qs.IsConnectible() && pt.IsConnectible()) { L = qs.beta * qs.f(pt, TransportMode::Importance) * pt.f(qs, TransportMode::Radiance) * pt.beta; VLOG(2) << "General connect s: " << s << ", t: " << t << " qs: " << qs << ", pt: " << pt << ", qs.f(pt): " << qs.f(pt, TransportMode::Importance) << ", pt.f(qs): " << pt.f(qs, TransportMode::Radiance) << ", G: " << G(scene, sampler, qs, pt) << ", dist^2: " << DistanceSquared(qs.p(), pt.p()); if (!L.IsBlack()) L *= G(scene, sampler, qs, pt); } } ++totalPaths; if (L.IsBlack()) ++zeroRadiancePaths; ReportValue(pathLength, s + t - 2); // Compute MIS weight for connection strategy Float misWeight = L.IsBlack() ? 0.f : MISWeight(scene, lightVertices, cameraVertices, sampled, s, t, lightDistr, lightToIndex); VLOG(2) << "MIS weight for (s,t) = (" << s << ", " << t << ") connection: " << misWeight; DCHECK(!std::isnan(misWeight)); L *= misWeight; if (misWeightPtr) *misWeightPtr = misWeight; return L; }
Spectrum ExPhotonIntegrator::Li(const Scene *scene, const RayDifferential &ray, const Sample *sample, float *alpha) const { // Compute reflected radiance with photon map Spectrum L(0.); Intersection isect; if (scene->Intersect(ray, &isect)) { if (alpha) *alpha = 1.; 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); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; L += UniformSampleAllLights(scene, p, n, wo, bsdf, sample, lightSampleOffset, bsdfSampleOffset, bsdfComponentOffset); // Compute indirect lighting for photon map integrator L += LPhoton(causticMap, nCausticPaths, nLookup, bsdf, isect, wo, maxDistSquared); if (finalGather) { #if 1 // Do one-bounce final gather for photon map BxDFType nonSpecular = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_DIFFUSE | BSDF_GLOSSY); if (bsdf->NumComponents(nonSpecular) > 0) { // Find indirect photons around point for importance sampling u_int nIndirSamplePhotons = 50; PhotonProcess proc(nIndirSamplePhotons, p); proc.photons = (ClosePhoton *)alloca(nIndirSamplePhotons * sizeof(ClosePhoton)); float searchDist2 = maxDistSquared; while (proc.foundPhotons < nIndirSamplePhotons) { float md2 = searchDist2; proc.foundPhotons = 0; indirectMap->Lookup(p, proc, md2); searchDist2 *= 2.f; } // Copy photon directions to local array Vector *photonDirs = (Vector *)alloca(nIndirSamplePhotons * sizeof(Vector)); for (u_int i = 0; i < nIndirSamplePhotons; ++i) photonDirs[i] = proc.photons[i].photon->wi; // Use BSDF to do final gathering Spectrum Li = 0.; static StatsCounter gatherRays("Photon Map", // NOBOOK "Final gather rays traced"); // NOBOOK for (int i = 0; i < gatherSamples; ++i) { // Sample random direction from BSDF for final gather ray Vector wi; float u1 = sample->twoD[gatherSampleOffset[0]][2*i]; float u2 = sample->twoD[gatherSampleOffset[0]][2*i+1]; float u3 = sample->oneD[gatherComponentOffset[0]][i]; float pdf; Spectrum fr = bsdf->Sample_f(wo, &wi, u1, u2, u3, &pdf, BxDFType(BSDF_ALL & (~BSDF_SPECULAR))); if (fr.Black() || pdf == 0.f) continue; // Trace BSDF final gather ray and accumulate radiance RayDifferential bounceRay(p, wi); ++gatherRays; // NOBOOK Intersection gatherIsect; if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance using precomputed irradiance Spectrum Lindir = 0.f; Normal n = gatherIsect.dg.nn; if (Dot(n, bounceRay.d) > 0) n = -n; RadiancePhotonProcess proc(gatherIsect.dg.p, n); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon) Lindir = proc.photon->Lo; Lindir *= scene->Transmittance(bounceRay); // Compute MIS weight for BSDF-sampled gather ray // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (u_int j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; float wt = PowerHeuristic(gatherSamples, pdf, gatherSamples, photonPdf); Li += fr * Lindir * AbsDot(wi, n) * wt / pdf; } } L += Li / gatherSamples; // Use nearby photons to do final gathering Li = 0.; for (int i = 0; i < gatherSamples; ++i) { // Sample random direction using photons for final gather ray float u1 = sample->oneD[gatherComponentOffset[1]][i]; float u2 = sample->twoD[gatherSampleOffset[1]][2*i]; float u3 = sample->twoD[gatherSampleOffset[1]][2*i+1]; int photonNum = min((int)nIndirSamplePhotons - 1, Floor2Int(u1 * nIndirSamplePhotons)); // Sample gather ray direction from _photonNum_ Vector vx, vy; CoordinateSystem(photonDirs[photonNum], &vx, &vy); Vector wi = UniformSampleCone(u2, u3, cosGatherAngle, vx, vy, photonDirs[photonNum]); // Trace photon-sampled final gather ray and accumulate radiance Spectrum fr = bsdf->f(wo, wi); if (fr.Black()) continue; // Compute PDF for photon-sampling of direction _wi_ float photonPdf = 0.f; float conePdf = UniformConePdf(cosGatherAngle); for (u_int j = 0; j < nIndirSamplePhotons; ++j) if (Dot(photonDirs[j], wi) > .999f * cosGatherAngle) photonPdf += conePdf; photonPdf /= nIndirSamplePhotons; RayDifferential bounceRay(p, wi); ++gatherRays; // NOBOOK Intersection gatherIsect; if (scene->Intersect(bounceRay, &gatherIsect)) { // Compute exitant radiance using precomputed irradiance Spectrum Lindir = 0.f; Normal n = gatherIsect.dg.nn; if (Dot(n, bounceRay.d) > 0) n = -n; RadiancePhotonProcess proc(gatherIsect.dg.p, n); float md2 = INFINITY; radianceMap->Lookup(gatherIsect.dg.p, proc, md2); if (proc.photon) Lindir = proc.photon->Lo; Lindir *= scene->Transmittance(bounceRay); // Compute MIS weight for photon-sampled gather ray float bsdfPdf = bsdf->Pdf(wo, wi); float wt = PowerHeuristic(gatherSamples, photonPdf, gatherSamples, bsdfPdf); Li += fr * Lindir * AbsDot(wi, n) * wt / photonPdf; } } L += Li / gatherSamples; } #else // look at radiance map directly.. Normal nn = n; if (Dot(nn, ray.d) > 0.) nn = -n; RadiancePhotonProcess proc(p, nn); float md2 = INFINITY; radianceMap->Lookup(p, proc, md2); if (proc.photon) L += proc.photon->Lo; #endif } else { L += LPhoton(indirectMap, nIndirectPaths, nLookup, bsdf, isect, wo, maxDistSquared); } if (specularDepth++ < maxSpecularDepth) { 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); } } --specularDepth; } 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; }
float BidirIntegrator::G(const BidirVertex &v0, const BidirVertex &v1) { Vector w = Normalize(v1.p - v0.p); return AbsDot(v0.ng, w) * AbsDot(v1.ng, -w) / DistanceSquared(v0.p, v1.p); }
void HashGridLookup::AddFlux(HitPointRadianceFlux *workerHitPoints, PointerFreeScene *ss, const float alpha, const Point &hitPoint, const Normal &shadeN, const Vector wi, const Spectrum photonFlux, float currentPhotonRadius2) { // Look for eye path hit points near the current hit point Vector hh = (hitPoint - hitPointsbbox.pMin) * invCellSize; const int ix = abs(int(hh.x)); const int iy = abs(int(hh.y)); const int iz = abs(int(hh.z)); // std::list<uint> *hps = hashGrid[Hash(ix, iy, iz, hashGridSize)]; // if (hps) { // std::list<uint>::iterator iter = hps->begin(); // while (iter != hps->end()) { // // HitPoint *hp = &hitPoints[*iter++]; uint gridEntry = Hash(ix, iy, iz); std::list<unsigned int>* hps = hashGrid[gridEntry]; if (hps) { std::list<unsigned int>::iterator iter = hps->begin(); while (iter != hps->end()) { HitPointPositionInfo *hp = engine->GetHitPointInfo(*iter); HitPointRadianceFlux *ihp = &workerHitPoints[*iter++]; //Vector v = hp->position - hitPoint; #if defined USE_SPPM || defined USE_PPM //if ((Dot(hp->normal, shadeN) > 0.5f) && (Dot(v, v) <= ihp->accumPhotonRadius2)) { const float dist2 = DistanceSquared(hp->position, hitPoint); if ((dist2 > ihp->accumPhotonRadius2)) continue; const float dot = Dot(hp->normal, wi); if (dot <= 0.0001f) continue; #else const float dist2 = DistanceSquared(hp->position, hitPoint); if ((dist2 > currentPhotonRadius2)) continue; const float dot = Dot(hp->normal, wi); if (dot <= 0.0001f) continue; #endif //const float g = (hp->accumPhotonCount * alpha + alpha) // / (hp->accumPhotonCount * alpha + 1.f); //hp->photonRadius2 *= g; __sync_fetch_and_add(&ihp->accumPhotonCount, 1); Spectrum f; POINTERFREESCENE::Material *hitPointMat = &ss->materials[hp->materialSS]; switch (hitPointMat->type) { case MAT_MATTE: ss->Matte_f(&hitPointMat->param.matte, hp->wo, wi, shadeN, f); break; case MAT_MATTEMIRROR: ss->MatteMirror_f(&hitPointMat->param.matteMirror, hp->wo, wi, shadeN, f); break; case MAT_MATTEMETAL: ss->MatteMetal_f(&hitPointMat->param.matteMetal, hp->wo, wi, shadeN, f); break; case MAT_ALLOY: ss->Alloy_f(&hitPointMat->param.alloy, hp->wo, wi, shadeN, f); break; default: break; } //f = hp->material->f(hp->wo, wi, shadeN); Spectrum flux = photonFlux * AbsDot(shadeN, wi) * hp->throughput * f; //if ((*iter-1) == 200) printf("%f\n", photonFlux.g); #pragma omp critical { ihp->accumReflectedFlux = (ihp->accumReflectedFlux + flux) /** g*/; } // // hp->accumReflectedFlux.r += flux.r; // hp->accumReflectedFlux.r += flux.r; // hp->accumReflectedFlux.r += flux.r; //printf("%f\n", f.r); } } }