void PathCPURenderThread::DirectHitInfiniteLight( const bool lastSpecular, const Spectrum &pathThrouput, const Vector &eyeDir, const float lastPdfW, Spectrum *radiance) { PathCPURenderEngine *engine = (PathCPURenderEngine *)renderEngine; Scene *scene = engine->renderConfig->scene; // Infinite light float directPdfW; if (scene->envLight) { const Spectrum envRadiance = scene->envLight->GetRadiance(scene, -eyeDir, &directPdfW); if (!envRadiance.Black()) { if(!lastSpecular) { // MIS between BSDF sampling and direct light sampling *radiance += pathThrouput * PowerHeuristic(lastPdfW, directPdfW) * envRadiance; } else *radiance += pathThrouput * envRadiance; } } // Sun light if (scene->sunLight) { const Spectrum sunRadiance = scene->sunLight->GetRadiance(scene, -eyeDir, &directPdfW); if (!sunRadiance.Black()) { if(!lastSpecular) { // MIS between BSDF sampling and direct light sampling *radiance += pathThrouput * PowerHeuristic(lastPdfW, directPdfW) * sunRadiance; } else *radiance += pathThrouput * sunRadiance; } } }
Spectrum EstimateDirect(const Scene *scene, const Renderer *renderer, MemoryArena &arena, const Light *light, const Point &p, const Normal &n, const Vector &wo, float rayEpsilon, float time, const BSDF *bsdf, RNG &rng, const LightSample &lightSample, const BSDFSample &bsdfSample, BxDFType flags) { Spectrum Ld(0.); // Sample light source with multiple importance sampling Vector wi; float lightPdf, bsdfPdf; VisibilityTester visibility; Spectrum Li = light->Sample_L(p, rayEpsilon, lightSample, time, &wi, &lightPdf, &visibility); if (lightPdf > 0. && !Li.IsBlack()) { Spectrum f = bsdf->f(wo, wi, flags); if (!f.IsBlack() && visibility.Unoccluded(scene)) { // Add light's contribution to reflected radiance Li *= visibility.Transmittance(scene, renderer, NULL, rng, arena); if (light->IsDeltaLight()) Ld += f * Li * (AbsDot(wi, n) / lightPdf); else { bsdfPdf = bsdf->Pdf(wo, wi, flags); float weight = PowerHeuristic(1, lightPdf, 1, bsdfPdf); Ld += f * Li * (AbsDot(wi, n) * weight / lightPdf); } } } // Sample BSDF with multiple importance sampling if (!light->IsDeltaLight()) { BxDFType sampledType; Spectrum f = bsdf->Sample_f(wo, &wi, bsdfSample, &bsdfPdf, flags, &sampledType); if (!f.IsBlack() && bsdfPdf > 0.) { float weight = 1.f; if (!(sampledType & BSDF_SPECULAR)) { lightPdf = light->Pdf(p, wi); if (lightPdf == 0.) return Ld; weight = PowerHeuristic(1, bsdfPdf, 1, lightPdf); } // Add light contribution from BSDF sampling Intersection lightIsect; Spectrum Li(0.f); RayDifferential ray(p, wi, rayEpsilon, INFINITY, time); if (scene->Intersect(ray, &lightIsect)) { if (lightIsect.primitive->GetAreaLight() == light) Li = lightIsect.Le(-wi); } else //Li = light->Le(ray,); Li = Spectrum(0.); if (!Li.IsBlack()) { Li *= renderer->Transmittance(scene, ray, NULL, rng, arena); Ld += f * Li * AbsDot(wi, n) * weight / bsdfPdf; } } } return Ld; }
void PathCPURenderThread::DirectHitFiniteLight( const bool lastSpecular, const Spectrum &pathThrouput, const float distance, const BSDF &bsdf, const float lastPdfW, Spectrum *radiance) { PathCPURenderEngine *engine = (PathCPURenderEngine *)renderEngine; Scene *scene = engine->renderConfig->scene; float directPdfA; const Spectrum emittedRadiance = bsdf.GetEmittedRadiance(scene, &directPdfA); if (!emittedRadiance.Black()) { float weight; if (!lastSpecular) { const float lightPickProb = scene->PickLightPdf(); const float directPdfW = PdfAtoW(directPdfA, distance, AbsDot(bsdf.fixedDir, bsdf.shadeN)); // MIS between BSDF sampling and direct light sampling weight = PowerHeuristic(lastPdfW, directPdfW * lightPickProb); } else weight = 1.f; *radiance += pathThrouput * weight * emittedRadiance; } }
float ComputeHeuristic(float fpdf, float gpdf) { #if 0 return BalanceHeuristic(fpdf, gpdf); #else return PowerHeuristic(fpdf, gpdf); #endif }
void PathCPURenderThread::DirectLightSampling( const float u0, const float u1, const float u2, const float u3, const float u4, const Spectrum &pathThrouput, const BSDF &bsdf, const int depth, Spectrum *radiance) { PathCPURenderEngine *engine = (PathCPURenderEngine *)renderEngine; Scene *scene = engine->renderConfig->scene; if (!bsdf.IsDelta()) { // Pick a light source to sample float lightPickPdf; const LightSource *light = scene->SampleAllLights(u0, &lightPickPdf); Vector lightRayDir; float distance, directPdfW; Spectrum lightRadiance = light->Illuminate(scene, bsdf.hitPoint, u1, u2, u3, &lightRayDir, &distance, &directPdfW); if (!lightRadiance.Black()) { BSDFEvent event; float bsdfPdfW; Spectrum bsdfEval = bsdf.Evaluate(lightRayDir, &event, &bsdfPdfW); if (!bsdfEval.Black()) { const float epsilon = Max(MachineEpsilon::E(bsdf.hitPoint), MachineEpsilon::E(distance)); Ray shadowRay(bsdf.hitPoint, lightRayDir, epsilon, distance - epsilon); RayHit shadowRayHit; BSDF shadowBsdf; Spectrum connectionThroughput; // Check if the light source is visible if (!scene->Intersect(device, false, u4, &shadowRay, &shadowRayHit, &shadowBsdf, &connectionThroughput)) { const float cosThetaToLight = AbsDot(lightRayDir, bsdf.shadeN); const float directLightSamplingPdfW = directPdfW * lightPickPdf; const float factor = cosThetaToLight / directLightSamplingPdfW; if (depth >= engine->rrDepth) { // Russian Roulette bsdfPdfW *= Max(bsdfEval.Filter(), engine->rrImportanceCap); } // MIS between direct light sampling and BSDF sampling const float weight = PowerHeuristic(directLightSamplingPdfW, bsdfPdfW); *radiance += (weight * factor) * pathThrouput * connectionThroughput * lightRadiance * bsdfEval; } } } } }
// MIS: sampling BRDF glm::vec3 BidirectionalIntegrator::MIS_SampleBRDF(Intersection &intersection, Ray &r, Geometry* &light) { if(Number_BRDF == 0) return glm::vec3(0); // Direct light estimator: sample BRDF glm::vec3 sum_brdf_sample(0.0f); for(int i = 0; i < Number_BRDF; i++) { glm::vec3 wo_local = intersection.ToLocalNormalCoordinate(-r.direction); glm::vec3 wj_local; float pdf_brdf; glm::vec3 F = intersection.object_hit->material->SampleAndEvaluateScatteredEnergy(intersection,wo_local,wj_local,pdf_brdf); glm::vec3 wj_world = intersection.ToWorldNormalCoordinate(wj_local); glm::vec3 wo_world = - r.direction; Intersection isxOnLight = intersection_engine->GetIntersection(Ray(intersection.point+float(1e-3)*intersection.normal, wj_world)); if(isxOnLight.t > 0 && isxOnLight.object_hit == light && pdf_brdf > 0) { float temp,pdf_light = light->RayPDF(intersection, Ray(intersection.point, wj_world)); float W = PowerHeuristic(pdf_brdf,float(Number_BRDF),pdf_light,float(Number_Light)); glm::vec3 Ld = light->material->EvaluateScatteredEnergy(isxOnLight,wo_world,-wj_world,temp); if(pdf_light > 0 ) { if(isinf(pdf_brdf)) // delta specular surface { sum_brdf_sample = sum_brdf_sample + F * Ld * float(fabs(glm::dot(wj_world, intersection.normal))) / pdf_light; } else { sum_brdf_sample = sum_brdf_sample + W * F * Ld * float(fabs(glm::dot(wj_world,intersection.normal))) / pdf_brdf; } } } } return sum_brdf_sample / float(Number_BRDF); }
// MIS: sampling light source glm::vec3 BidirectionalIntegrator::MIS_SampleLight(Intersection &intersection, Ray &r, Geometry* &light) { if(Number_Light == 0) return glm::vec3(0); // Direct light estimator: sample Light source glm::vec3 sum_light_sample(0); for(int i = 0; i < Number_Light; i++) { float u = uniform_distribution(generator); float v = uniform_distribution(generator); Intersection lightSample = light->SampleOnGeometrySurface(u, v, intersection.point + 1e-3f * intersection.normal); //Intersection lightSample = light->RandomSampleOnSurface(u,v); glm::vec3 wj = glm::normalize(lightSample.point - intersection.point); glm::vec3 wo = - r.direction; glm::vec3 P = intersection.point; glm::vec3 N = intersection.normal; float pdf_light = light->RayPDF(intersection, Ray(P + float(1e-3)*N, wj)); Intersection lightIntersection = intersection_engine->GetIntersection(Ray(P + float(1e-3)*N, wj)); float temp, pdf_brdf; glm::vec3 wo_local = intersection.ToLocalNormalCoordinate(wo); glm::vec3 wj_local = intersection.ToLocalNormalCoordinate(wj); glm::vec3 F = intersection.object_hit->material->EvaluateScatteredEnergy(intersection, wo_local, wj_local, pdf_brdf); // reach light directly && pdf(wj) > 0 if(lightIntersection.t > 0 && lightIntersection.object_hit == light && pdf_light > 0 && pdf_brdf > 0) { glm::vec3 Ld = light->material->EvaluateScatteredEnergy(lightSample, wo, -wj, temp); float W = PowerHeuristic(pdf_light, float(Number_Light), pdf_brdf, float(Number_BRDF)); // cause shadow in center sum_light_sample = sum_light_sample + W * F * Ld * float(fabs(glm::dot(wj, N))) / pdf_light; } } return sum_light_sample / float(Number_Light); }
Color RayTracer::EstimateDirect(const Scene *scene, const Ray& ray, const LocalGeo& geom, const Light *light, const Sample *lightsample, const Sample *bsdfsample){ Vec3 wo = -ray.dir; // dir to camera Ray lightray; float light_pdf, bsdf_pdf; Color color, lightcolor, surfacecolor; // sample light sources with multiple importance sampling light->SampleDirect(geom.point, lightsample, &lightray, &lightcolor, &light_pdf); if (light_pdf > 0.f && lightcolor != Color::BLACK){ surfacecolor = geom.bsdf->Eval(lightray.dir, wo, geom, BSDFType(BSDF_ALL & ~BSDF_SPECULAR)); if (surfacecolor != Color::BLACK && !scene->Occlude(lightray)){ if (light->IsDelta()) color = surfacecolor * lightcolor * (Math::AbsDot(lightray.dir, geom.normal)); else{ bsdf_pdf = geom.bsdf->Pdf(wo, lightray.dir, geom); color = surfacecolor * lightcolor * (Math::AbsDot(lightray.dir, geom.normal) / light_pdf * PowerHeuristic(1, light_pdf, 1, bsdf_pdf)); } } } if (light->IsDelta()) return color; // sample bsdf with multiple importance sampling surfacecolor = geom.bsdf->SampleDirect(wo, geom, *bsdfsample, &(lightray.dir), &bsdf_pdf, BSDFType(BSDF_ALL & ~BSDF_SPECULAR)); if (bsdf_pdf > 0.f && surfacecolor != Color::BLACK){ lightcolor = Color::BLACK; LocalGeo geom_light; if (scene->Intersect(lightray, geom_light)){ if (light == geom_light.prim->GetAreaLight()){ scene->PostIntersect(lightray, geom_light); light_pdf = geom_light.prim->Pdf(geom_light.triId, geom.point, lightray.dir); geom_light.Emit(-lightray.dir, &lightcolor); } } //else // light->Emit(lightray, &lightcolor); if (lightcolor != Color::BLACK){ color += surfacecolor * lightcolor * (Math::AbsDot(lightray.dir, geom.normal) / bsdf_pdf * PowerHeuristic(1, bsdf_pdf, 1, light_pdf)); } } return color; }
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 pbrt::Point BSDF *bsdf = isect.GetBSDF(ray, arena); const pbrt::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 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; }
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; }
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; }
float3 DiTracer::GetDi(World const& world, Light const& light, Sampler const& lightsampler, Sampler const& bsdfsampler, float3 const& wo, ShapeBundle::Hit& hit) const { float3 radiance; // TODO: fix that later with correct heuristic assert(lightsampler.num_samples() == bsdfsampler.num_samples()); // Sample light source first to apply MIS later { // Direction from the shading point to the light float3 lightdir; // PDF for BSDF sample float bsdfpdf = 0.f; // PDF for light sample float lightpdf = 0.f; // Sample numsamples times int numsamples = lightsampler.num_samples(); // Allocate samples std::vector<float2> lightsamples(numsamples); std::vector<float2> bsdfsamples(numsamples); // Generate samples for (int i = 0; i < numsamples; ++i) { lightsamples[i] = lightsampler.Sample2D(); bsdfsamples[i] = bsdfsampler.Sample2D(); } // Cache singularity flag to avoid virtual call in the loop below bool singularlight = light.Singular(); // Fetch the material Material const& mat = *world.materials_[hit.m]; // Start sampling for (int i=0; i<numsamples; ++i) { lightpdf = 0.f; bsdfpdf = 0.f; // This is needed to support normal mapping. // Original intersection needs to be kept around since bsdf might alter the normal ShapeBundle::Hit hitlocal = hit; // Sample light source float3 le = light.GetSample(hitlocal, lightsamples[i], lightdir, lightpdf); // Continue if intensity > 0 and there is non-zero probability of sampling the point if (lightpdf > MINPDF && le.sqnorm() > 0.f) { // Normalize direction to light float3 wi = normalize(lightdir); // Calculate distance for shadow testing float dist = sqrtf(lightdir.sqnorm()); // Spawn shadow ray ray shadowray; // From an intersection point shadowray.o = hitlocal.p; // Into evaluated direction shadowray.d = wi; // TODO: move ray epsilon into some global options object shadowray.t = float2(0.01f, dist - 0.01f); // Check for an occlusion float shadow = world.Intersect(shadowray) ? 0.f : 1.f; // If we are not in shadow if (shadow > 0.f) { // Evaluate BSDF float3 bsdf = mat.Evaluate(hitlocal, wi, wo); // We can't apply MIS for singular lights, so use simple estimator if (singularlight) { // Estimate with Monte-Carlo L(wo) = int{ Ld(wi, wo) * fabs(dot(n, wi)) * dwi } radiance += le * bsdf * fabs(dot(hitlocal.n, wi)) * (1.f / lightpdf); assert(!has_nans(radiance)); } else { // Apply MIS bsdfpdf = mat.GetPdf(hitlocal, wi, wo); // Evaluate weight float weight = PowerHeuristic(1, lightpdf, 1, bsdfpdf); // Estimate with Monte-Carlo L(wo) = int{ Ld(wi, wo) * fabs(dot(n, wi)) * dwi } radiance += le * bsdf * fabs(dot(hitlocal.n, wi)) * weight * (1.f / lightpdf); assert(!has_nans(radiance)); } } } // Sample BSDF if the light is not singular if (!singularlight) { int bsdftype = 0; float3 wi; // Sample material float3 bsdf = mat.Sample(hitlocal, bsdfsamples[i], wo, wi, bsdfpdf, bsdftype); //assert(!has_nans(bsdf)); //assert(!has_nans(bsdfpdf < 1000000.f)); // Normalize wi wi = normalize(wi); // If something would be reflected if (bsdf.sqnorm() > 0.f && bsdfpdf > MINPDF) { float weight = 1.f; // Apply MIS if BSDF is not specular if (! (bsdftype & Bsdf::SPECULAR)) { // Evaluate light PDF lightpdf = light.GetPdf(hitlocal, wi); // If light PDF is zero skip to next sample if (lightpdf < MINPDF) { continue; } // Apply heuristic weight = PowerHeuristic(1, bsdfpdf, 1, lightpdf); } // Spawn shadow ray ray shadowray; // From an intersection point shadowray.o = hitlocal.p; // Into evaluated direction shadowray.d = wi; // TODO: move ray epsilon into some global options object shadowray.t = float2(0.01f, 10000000.f); // Cast the ray into the scene ShapeBundle::Hit shadowhit; float3 le(0.f, 0.f, 0.f); // If the ray intersects the scene check if we have intersected this light // TODO: move that to area light class if (world.Intersect(shadowray, shadowhit)) { // Only sample if this is our light if ((Light const*)shadowhit.bundle->GetAreaLight() == &light) { Material const& lightmat = *world.materials_[shadowhit.m]; // Get material emission properties ShapeBundle::Sample sampledata(shadowhit); float3 d = sampledata.p - hitlocal.p; // If the object facing the light compute emission if (dot(sampledata.n, -wi) > 0.f) { // Emissive power with squared fallof float d2inv = 1.f / d.sqnorm(); // Return emission characteristic of the material le = lightmat.GetLe(sampledata, -wi) * d2inv; } } } else { // This is to give a chance for IBL to contribute le = light.GetLe(shadowray); } if (le.sqnorm() > 0.f) { // Estimate with Monte-Carlo L(wo) = int{ Ld(wi, wo) * fabs(dot(n, wi)) * dwi } radiance += le * bsdf * fabs(dot(hitlocal.n, wi)) * weight * (1.f / bsdfpdf); //assert(!has_nans(radiance)); } } } } return (1.f / numsamples) * radiance; } }
Spectrum EstimateDirect(const Scene *scene, const Light *light, const Point &p, const Normal &n, const Vector &wo, BSDF *bsdf, const Sample *sample, int lightSamp, int bsdfSamp, int bsdfComponent, u_int sampleNum) { Spectrum Ld(0.); // Find light and BSDF sample values for direct lighting estimate float ls1, ls2, bs1, bs2, bcs; if (lightSamp != -1 && bsdfSamp != -1 && sampleNum < sample->n2D[lightSamp] && sampleNum < sample->n2D[bsdfSamp]) { ls1 = sample->twoD[lightSamp][2*sampleNum]; ls2 = sample->twoD[lightSamp][2*sampleNum+1]; bs1 = sample->twoD[bsdfSamp][2*sampleNum]; bs2 = sample->twoD[bsdfSamp][2*sampleNum+1]; bcs = sample->oneD[bsdfComponent][sampleNum]; } else { ls1 = RandomFloat(); ls2 = RandomFloat(); bs1 = RandomFloat(); bs2 = RandomFloat(); bcs = RandomFloat(); } // Sample light source with multiple importance sampling Vector wi; float lightPdf, bsdfPdf; VisibilityTester visibility; Spectrum Li = light->Sample_L(p, n, ls1, ls2, &wi, &lightPdf, &visibility); if (lightPdf > 0. && !Li.Black()) { Spectrum f = bsdf->f(wo, wi); if (!f.Black() && visibility.Unoccluded(scene)) { // Add light's contribution to reflected radiance Li *= visibility.Transmittance(scene); if (light->IsDeltaLight()) Ld += f * Li * AbsDot(wi, n) / lightPdf; else { bsdfPdf = bsdf->Pdf(wo, wi); float weight = PowerHeuristic(1, lightPdf, 1, bsdfPdf); Ld += f * Li * AbsDot(wi, n) * weight / lightPdf; } } } // Sample BSDF with multiple importance sampling if (!light->IsDeltaLight()) { BxDFType flags = BxDFType(BSDF_ALL & ~BSDF_SPECULAR); Spectrum f = bsdf->Sample_f(wo, &wi, bs1, bs2, bcs, &bsdfPdf, flags); if (!f.Black() && bsdfPdf > 0.) { lightPdf = light->Pdf(p, n, wi); if (lightPdf > 0.) { // Add light contribution from BSDF sampling float weight = PowerHeuristic(1, bsdfPdf, 1, lightPdf); Intersection lightIsect; Spectrum Li(0.f); RayDifferential ray(p, wi); if (scene->Intersect(ray, &lightIsect)) { if (lightIsect.primitive->GetAreaLight() == light) Li = lightIsect.Le(-wi); } else Li = light->Le(ray); if (!Li.Black()) { Li *= scene->Transmittance(ray); Ld += f * Li * AbsDot(wi, n) * weight / bsdfPdf; } } } } return Ld; }