Spectrum MixMaterial::Sample(const HitPoint &hitPoint, const Vector &localFixedDir, Vector *localSampledDir, const float u0, const float u1, const float passThroughEvent, float *pdfW, float *absCosSampledDir, BSDFEvent *event, const BSDFEvent requestedEvent) const { const Frame frame(hitPoint.GetFrame()); HitPoint hitPointA(hitPoint); matA->Bump(&hitPointA); const Frame frameA(hitPointA.GetFrame()); const Vector fixedDirA = frameA.ToLocal(frame.ToWorld(localFixedDir)); HitPoint hitPointB(hitPoint); matB->Bump(&hitPointB); const Frame frameB(hitPointB.GetFrame()); const Vector fixedDirB = frameB.ToLocal(frame.ToWorld(localFixedDir)); const float weight2 = Clamp(mixFactor->GetFloatValue(hitPoint), 0.f, 1.f); const float weight1 = 1.f - weight2; const bool sampleMatA = (passThroughEvent < weight1); const float weightFirst = sampleMatA ? weight1 : weight2; const float weightSecond = sampleMatA ? weight2 : weight1; const float passThroughEventFirst = sampleMatA ? (passThroughEvent / weight1) : (passThroughEvent - weight1) / weight2; // Sample the first material, evaluate the second const Material *matFirst = sampleMatA ? matA : matB; const Material *matSecond = sampleMatA ? matB : matA; HitPoint &hitPoint1 = sampleMatA ? hitPointA : hitPointB; HitPoint &hitPoint2 = sampleMatA ? hitPointB : hitPointA; const Frame &frame1 = sampleMatA ? frameA : frameB; const Frame &frame2 = sampleMatA ? frameB : frameA; const Vector &fixedDir1 = sampleMatA ? fixedDirA : fixedDirB; const Vector &fixedDir2 = sampleMatA ? fixedDirB : fixedDirA; // Sample the first material Spectrum result = matFirst->Sample(hitPoint1, fixedDir1, localSampledDir, u0, u1, passThroughEventFirst, pdfW, absCosSampledDir, event, requestedEvent); if (result.Black()) return Spectrum(); *localSampledDir = frame1.ToWorld(*localSampledDir); const Vector sampledDir2 = frame2.ToLocal(*localSampledDir); *localSampledDir = frame.ToLocal(*localSampledDir); *pdfW *= weightFirst; result *= *pdfW; // Evaluate the second material const Vector &localLightDir = (hitPoint2.fromLight) ? fixedDir2 : sampledDir2; const Vector &localEyeDir = (hitPoint2.fromLight) ? sampledDir2 : fixedDir2; BSDFEvent eventSecond; float pdfWSecond; Spectrum evalSecond = matSecond->Evaluate(hitPoint2, localLightDir, localEyeDir, &eventSecond, &pdfWSecond); if (!evalSecond.Black()) { result += weightSecond * evalSecond; *pdfW += weightSecond * pdfWSecond; } return result / *pdfW; }
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; } } }
void PathHybridState::DirectHitInfiniteLight(const Scene *scene, const Vector &eyeDir) { // 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 sampleResults[0].radiance += throuput * PowerHeuristic(lastPdfW, directPdfW) * envRadiance; } else sampleResults[0].radiance += throuput * 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 sampleResults[0].radiance += throuput * PowerHeuristic(lastPdfW, directPdfW) * sunRadiance; } else sampleResults[0].radiance += throuput * sunRadiance; } } }
Spectrum MixMaterial::Evaluate(const HitPoint &hitPoint, const Vector &localLightDir, const Vector &localEyeDir, BSDFEvent *event, float *directPdfW, float *reversePdfW) const { const Frame frame(hitPoint.GetFrame()); Spectrum result; const float weight2 = Clamp(mixFactor->GetFloatValue(hitPoint), 0.f, 1.f); const float weight1 = 1.f - weight2; if (directPdfW) *directPdfW = 0.f; if (reversePdfW) *reversePdfW = 0.f; BSDFEvent eventMatA = NONE; if (weight1 > 0.f) { HitPoint hitPointA(hitPoint); matA->Bump(&hitPointA); const Frame frameA(hitPointA.GetFrame()); const Vector lightDirA = frameA.ToLocal(frame.ToWorld(localLightDir)); const Vector eyeDirA = frameA.ToLocal(frame.ToWorld(localEyeDir)); float directPdfWMatA, reversePdfWMatA; const Spectrum matAResult = matA->Evaluate(hitPointA, lightDirA, eyeDirA, &eventMatA, &directPdfWMatA, &reversePdfWMatA); if (!matAResult.Black()) { result += weight1 * matAResult; if (directPdfW) *directPdfW += weight1 * directPdfWMatA; if (reversePdfW) *reversePdfW += weight1 * reversePdfWMatA; } } BSDFEvent eventMatB = NONE; if (weight2 > 0.f) { HitPoint hitPointB(hitPoint); matB->Bump(&hitPointB); const Frame frameB(hitPointB.GetFrame()); const Vector lightDirB = frameB.ToLocal(frame.ToWorld(localLightDir)); const Vector eyeDirB = frameB.ToLocal(frame.ToWorld(localEyeDir)); float directPdfWMatB, reversePdfWMatB; const Spectrum matBResult = matB->Evaluate(hitPointB, lightDirB, eyeDirB, &eventMatB, &directPdfWMatB, &reversePdfWMatB); if (!matBResult.Black()) { result += weight2 * matBResult; if (directPdfW) *directPdfW += weight2 * directPdfWMatB; if (reversePdfW) *reversePdfW += weight2 * reversePdfWMatB; } } *event = eventMatA | eventMatB; return result; }
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; } } } } }
bool PathHybridState::FinalizeRay(const PathHybridRenderThread *renderThread, const Ray *ray, const RayHit *rayHit, BSDF *bsdf, const float u0, Spectrum *radiance) { if (rayHit->Miss()) return true; else { PathHybridRenderEngine *renderEngine = (PathHybridRenderEngine *)renderThread->renderEngine; Scene *scene = renderEngine->renderConfig->scene; // I have to check if it is an hit over a pass-through point bsdf->Init(false, *scene, *ray, *rayHit, u0); // Check if it is pass-through point Spectrum t = bsdf->GetPassThroughTransparency(); if (!t.Black()) { *radiance *= t; // It is a pass-through material, continue to trace the ray. I do // this on the CPU. Ray newRay(*ray); newRay.mint = rayHit->t + MachineEpsilon::E(rayHit->t); RayHit newRayHit; Spectrum connectionThroughput; if (scene->Intersect(renderThread->device, false, u0, &newRay, &newRayHit, bsdf, &connectionThroughput)) { // Something was hit return false; } else { *radiance *= connectionThroughput; return true; } } else return false; } }
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; } }
Spectrum EstimateIrradianceDirect(const Scene* scene, const Light* light, const Point& p, const Normal& n, int lightSamp, u_int sampleNum) { Spectrum Ed(0.); // direct irradiance // Find light and BSDF sample values for direct lighting estimate float ls1, ls2; ls1 = RandomFloat(); ls2 = RandomFloat(); // Sample light source with multiple importance sampling Vector wi; float lightPdf; VisibilityTester visibility; Spectrum Li = light->Sample_L(p, n, ls1, ls2, &wi, &lightPdf, &visibility); //printf("got light sample "); //Li.printSelf(); if (lightPdf > 0. && !Li.Black()) { if (visibility.Unoccluded(scene)) { // Add light's contribution to reflected radiance Li *= visibility.Transmittance(scene); Ed += Li * AbsDot(wi, n) / lightPdf; } } return Ed; }
void PathHybridState::DirectLightSampling(const PathHybridRenderThread *renderThread, const float u0, const float u1, const float u2, const float u3, const BSDF &bsdf) { if (!bsdf.IsDelta()) { PathHybridRenderEngine *renderEngine = (PathHybridRenderEngine *)renderThread->renderEngine; Scene *scene = renderEngine->renderConfig->scene; // 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.p, 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.p), MachineEpsilon::E(distance)); directLightRay = Ray(bsdf.hitPoint.p, lightRayDir, epsilon, distance - epsilon); const float cosThetaToLight = AbsDot(lightRayDir, bsdf.hitPoint.shadeN); const float directLightSamplingPdfW = directPdfW * lightPickPdf; const float factor = cosThetaToLight / directLightSamplingPdfW; if (depth >= renderEngine->rrDepth) { // Russian Roulette bsdfPdfW *= RenderEngine::RussianRouletteProb(bsdfEval, renderEngine->rrImportanceCap); } // MIS between direct light sampling and BSDF sampling const float weight = PowerHeuristic(directLightSamplingPdfW, bsdfPdfW); directLightRadiance = (weight * factor) * throuput * lightRadiance * bsdfEval; } else directLightRadiance = Spectrum(); } else directLightRadiance = Spectrum(); } else directLightRadiance = Spectrum(); }
// BSDF Method Definitions Spectrum BSDF::Sample_f(const Vector &wo, Vector *wi, BxDFType flags, BxDFType *sampledType) const { float pdf; Spectrum f = Sample_f(wo, wi, RandomFloat(), RandomFloat(), RandomFloat(), &pdf, flags, sampledType); if (!f.Black() && pdf > 0.) f /= pdf; return f; }
// Mirror Method Definitions BSDF *Mirror::GetBSDF(const DifferentialGeometry &dgGeom, const DifferentialGeometry &dgShading) const { // Allocate _BSDF_, possibly doing bump-mapping with _bumpMap_ DifferentialGeometry dgs; if (bumpMap) Bump(bumpMap, dgGeom, dgShading, &dgs); else dgs = dgShading; BSDF *bsdf = BSDF_ALLOC(BSDF)(dgs, dgGeom.nn); Spectrum R = Kr->Evaluate(dgs).Clamp(); if (!R.Black()) bsdf->Add(BSDF_ALLOC(SpecularReflection)(R, BSDF_ALLOC(FresnelNoOp)())); return bsdf; }
void PathHybridState::DirectHitFiniteLight(const Scene *scene, const float distance, const BSDF &bsdf) { float directPdfA; const Spectrum emittedRadiance = bsdf.GetEmittedRadiance(&directPdfA); if (!emittedRadiance.Black()) { float weight; if (!lastSpecular) { const float lightPickProb = scene->PickLightPdf(); const float directPdfW = PdfAtoW(directPdfA, distance, AbsDot(bsdf.hitPoint.fixedDir, bsdf.hitPoint.shadeN)); // MIS between BSDF sampling and direct light sampling weight = PowerHeuristic(lastPdfW, directPdfW * lightPickProb); } else weight = 1.f; sampleResults[0].radiance += throuput * weight * emittedRadiance; } }
void LightCPURenderThread::ConnectToEye(const float u0, const BSDF &bsdf, const Point &lensPoint, const Spectrum &flux, vector<SampleResult> &sampleResults) { LightCPURenderEngine *engine = (LightCPURenderEngine *)renderEngine; Scene *scene = engine->renderConfig->scene; Vector eyeDir(bsdf.hitPoint - lensPoint); const float eyeDistance = eyeDir.Length(); eyeDir /= eyeDistance; BSDFEvent event; Spectrum bsdfEval = bsdf.Evaluate(-eyeDir, &event); if (!bsdfEval.Black()) { const float epsilon = Max(MachineEpsilon::E(lensPoint), MachineEpsilon::E(eyeDistance)); Ray eyeRay(lensPoint, eyeDir, epsilon, eyeDistance - epsilon); float scrX, scrY; if (scene->camera->GetSamplePosition(lensPoint, eyeDir, eyeDistance, &scrX, &scrY)) { RayHit eyeRayHit; BSDF bsdfConn; Spectrum connectionThroughput; if (!scene->Intersect(device, true, u0, &eyeRay, &eyeRayHit, &bsdfConn, &connectionThroughput)) { // Nothing was hit, the light path vertex is visible const float cosToCamera = Dot(bsdf.shadeN, -eyeDir); const float cosAtCamera = Dot(scene->camera->GetDir(), eyeDir); const float cameraPdfW = 1.f / (cosAtCamera * cosAtCamera * cosAtCamera * scene->camera->GetPixelArea()); const float cameraPdfA = PdfWtoA(cameraPdfW, eyeDistance, cosToCamera); const float fluxToRadianceFactor = cameraPdfA; const Spectrum radiance = connectionThroughput * flux * fluxToRadianceFactor * bsdfEval; AddSampleResult(sampleResults, PER_SCREEN_NORMALIZED, scrX, scrY, radiance, 1.f); } } } }
int BidirIntegrator::generatePath(const Scene *scene, const Ray &r, const Sample *sample, const int *bsdfOffset, const int *bsdfCompOffset, BidirVertex *vertices, int maxVerts) const { int nVerts = 0; RayDifferential ray(r.o, r.d); while (nVerts < maxVerts) { // Find next vertex in path and initialize _vertices_ Intersection isect; if (!scene->Intersect(ray, &isect)) break; BidirVertex &v = vertices[nVerts]; v.bsdf = isect.GetBSDF(ray); // do before Ns is set! v.p = isect.dg.p; v.ng = isect.dg.nn; v.ns = v.bsdf->dgShading.nn; v.wi = -ray.d; ++nVerts; // Possibly terminate bidirectional path sampling if (nVerts > 2) { float rrProb = .2f; if (RandomFloat() > rrProb) break; v.rrWeight = 1.f / rrProb; } // Initialize _ray_ for next segment of path float u1 = sample->twoD[bsdfOffset[nVerts-1]][0]; float u2 = sample->twoD[bsdfOffset[nVerts-1]][1]; float u3 = sample->oneD[bsdfCompOffset[nVerts-1]][0]; Spectrum fr = v.bsdf->Sample_f(v.wi, &v.wo, u1, u2, u3, &v.bsdfWeight, BSDF_ALL, &v.flags); if (fr.Black() && v.bsdfWeight == 0.f) break; ray = RayDifferential(v.p, v.wo); } // Initialize additional values in _vertices_ for (int i = 0; i < nVerts-1; ++i) vertices[i].dAWeight = vertices[i].bsdfWeight * AbsDot(-vertices[i].wo, vertices[i+1].ng) / DistanceSquared(vertices[i].p, vertices[i+1].p); return nVerts; }
Spectrum IrradianceCache::IndirectLo(const Point &p, const Normal &n, const Vector &wo, BSDF *bsdf, BxDFType flags, const Sample *sample, const Scene *scene) const { if (bsdf->NumComponents(flags) == 0) return Spectrum(0.); Spectrum E; if (!InterpolateIrradiance(scene, p, n, &E)) { // Compute irradiance at current point u_int scramble[2] = { RandomUInt(), RandomUInt() }; float sumInvDists = 0.; for (int i = 0; i < nSamples; ++i) { // Trace ray to sample radiance for irradiance estimate // Update irradiance statistics for rays traced static StatsCounter nIrradiancePaths("Irradiance Cache", "Paths followed for irradiance estimates"); ++nIrradiancePaths; float u[2]; Sample02(i, scramble, u); Vector w = CosineSampleHemisphere(u[0], u[1]); RayDifferential r(p, bsdf->LocalToWorld(w)); if (Dot(r.d, n) < 0) r.d = -r.d; Spectrum L(0.); // Do path tracing to compute radiance along ray for estimate { // Declare common path integration variables Spectrum pathThroughput = 1.; RayDifferential ray(r); bool specularBounce = false; for (int pathLength = 0; ; ++pathLength) { // Find next vertex of path Intersection isect; if (!scene->Intersect(ray, &isect)) break; if (pathLength == 0) r.maxt = ray.maxt; pathThroughput *= scene->Transmittance(ray); // Possibly add emitted light at path vertex if (specularBounce) L += pathThroughput * isect.Le(-ray.d); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray); // Sample illumination from lights to find path contribution const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; L += pathThroughput * UniformSampleOneLight(scene, p, n, wo, bsdf, sample); if (pathLength+1 == maxIndirectDepth) break; // Sample BSDF to get new path direction // Get random numbers for sampling new direction, \mono{bs1}, \mono{bs2}, and \mono{bcs} float bs1 = RandomFloat(), bs2 = RandomFloat(), bcs = RandomFloat(); Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, bs1, bs2, bcs, &pdf, BSDF_ALL, &flags); if (f.Black() || pdf == 0.) break; specularBounce = (flags & BSDF_SPECULAR) != 0; pathThroughput *= f * AbsDot(wi, n) / pdf; ray = RayDifferential(p, wi); // Possibly terminate the path if (pathLength > 3) { float continueProbability = .5f; if (RandomFloat() > continueProbability) break; pathThroughput /= continueProbability; } } } E += L; float dist = r.maxt * r.d.Length(); sumInvDists += 1.f / dist; } E *= M_PI / float(nSamples); // Add computed irradiance value to cache // Update statistics for new irradiance sample static StatsCounter nSamplesComputed("Irradiance Cache", "Irradiance estimates computed"); ++nSamplesComputed; // Compute bounding box of irradiance sample's contribution region static float minMaxDist = .001f * powf(scene->WorldBound().Volume(), 1.f/3.f); static float maxMaxDist = .125f * powf(scene->WorldBound().Volume(), 1.f/3.f); float maxDist = nSamples / sumInvDists; if (minMaxDist > 0.f) maxDist = Clamp(maxDist, minMaxDist, maxMaxDist); maxDist *= maxError; BBox sampleExtent(p); sampleExtent.Expand(maxDist); octree->Add(IrradianceSample(E, p, n, maxDist), sampleExtent); } return .5f * bsdf->rho(wo, flags) * E; }
Spectrum IrradianceCache::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 direct lighting for irradiance cache L += isect.Le(wo); L += UniformSampleAllLights(scene, p, n, wo, bsdf, sample, lightSampleOffset, bsdfSampleOffset, bsdfComponentOffset); // Compute indirect lighting for irradiance cache 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; // Estimate indirect lighting with irradiance cache Normal ng = isect.dg.nn; if (Dot(wo, ng) < 0.f) ng = -ng; BxDFType flags = BxDFType(BSDF_REFLECTION | BSDF_DIFFUSE | BSDF_GLOSSY); L += IndirectLo(p, ng, wo, bsdf, flags, sample, scene); flags = BxDFType(BSDF_TRANSMISSION | BSDF_DIFFUSE | BSDF_GLOSSY); L += IndirectLo(p, -ng, wo, bsdf, flags, sample, scene); } 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; }
u_int64_t CPU_Worker::AdvancePhotonPath(u_int64_t photonTarget) { uint todoPhotonCount = 0; PhotonPath* livePhotonPaths = new PhotonPath[rayBuffer->GetSize()]; rayBuffer->Reset(); size_t initc = min((int) rayBuffer->GetSize(), (int) photonTarget); double start = WallClockTime(); for (size_t i = 0; i < initc; ++i) { int p = rayBuffer->ReserveRay(); Ray * b = &(rayBuffer->GetRayBuffer())[p]; engine->InitPhotonPath(engine->ss, &livePhotonPaths[i], b, seedBuffer[i]); } while (todoPhotonCount < photonTarget) { Intersect(rayBuffer); #ifndef __DEBUG omp_set_num_threads(config->max_threads); #pragma omp parallel for schedule(guided) #endif for (unsigned int i = 0; i < rayBuffer->GetRayCount(); ++i) { PhotonPath *photonPath = &livePhotonPaths[i]; Ray *ray = &rayBuffer->GetRayBuffer()[i]; RayHit *rayHit = &rayBuffer->GetHitBuffer()[i]; if (photonPath->done == true) { continue; } if (rayHit->Miss()) { photonPath->done = true; } else { // Something was hit Point hitPoint; Spectrum surfaceColor; Normal N, shadeN; if (engine->GetHitPointInformation(engine->ss, ray, rayHit, hitPoint, surfaceColor, N, shadeN)) continue; const unsigned int currentTriangleIndex = rayHit->index; const unsigned int currentMeshIndex = engine->ss->meshIDs[currentTriangleIndex]; POINTERFREESCENE::Material *hitPointMat = &engine->ss->materials[engine->ss->meshMats[currentMeshIndex]]; uint matType = hitPointMat->type; if (matType == MAT_AREALIGHT) { photonPath->done = true; } else { float fPdf; Vector wi; Vector wo = -ray->d; bool specularBounce = true; float u0 = getFloatRNG(seedBuffer[i]); float u1 = getFloatRNG(seedBuffer[i]); float u2 = getFloatRNG(seedBuffer[i]); Spectrum f; switch (matType) { case MAT_MATTE: engine->ss->Matte_Sample_f(&hitPointMat->param.matte, &wo, &wi, &fPdf, &f, &shadeN, u0, u1, &specularBounce); f *= surfaceColor; break; case MAT_MIRROR: engine->ss->Mirror_Sample_f(&hitPointMat->param.mirror, &wo, &wi, &fPdf, &f, &shadeN, &specularBounce); f *= surfaceColor; break; case MAT_GLASS: engine->ss->Glass_Sample_f(&hitPointMat->param.glass, &wo, &wi, &fPdf, &f, &N, &shadeN, u0, &specularBounce); f *= surfaceColor; break; case MAT_MATTEMIRROR: engine->ss->MatteMirror_Sample_f(&hitPointMat->param.matteMirror, &wo, &wi, &fPdf, &f, &shadeN, u0, u1, u2, &specularBounce); f *= surfaceColor; break; case MAT_METAL: engine->ss->Metal_Sample_f(&hitPointMat->param.metal, &wo, &wi, &fPdf, &f, &shadeN, u0, u1, &specularBounce); f *= surfaceColor; break; case MAT_MATTEMETAL: engine->ss->MatteMetal_Sample_f(&hitPointMat->param.matteMetal, &wo, &wi, &fPdf, &f, &shadeN, u0, u1, u2, &specularBounce); f *= surfaceColor; break; case MAT_ALLOY: engine->ss->Alloy_Sample_f(&hitPointMat->param.alloy, &wo, &wi, &fPdf, &f, &shadeN, u0, u1, u2, &specularBounce); f *= surfaceColor; break; case MAT_ARCHGLASS: engine->ss->ArchGlass_Sample_f(&hitPointMat->param.archGlass, &wo, &wi, &fPdf, &f, &N, &shadeN, u0, &specularBounce); f *= surfaceColor; break; case MAT_NULL: wi = ray->d; specularBounce = 1; fPdf = 1.f; break; default: // Huston, we have a problem... specularBounce = 1; fPdf = 0.f; break; } if (!specularBounce) // if difuse lookupA->AddFlux(engine->ss, engine->alpha, hitPoint, shadeN, -ray->d, photonPath->flux, currentPhotonRadius2); if (photonPath->depth < MAX_PHOTON_PATH_DEPTH) { // Build the next vertex path ray if ((fPdf <= 0.f) || f.Black()) { photonPath->done = true; } else { photonPath->depth++; photonPath->flux *= f / fPdf; // Russian Roulette const float p = 0.75f; if (photonPath->depth < 3) { *ray = Ray(hitPoint, wi); } else if (getFloatRNG(seedBuffer[i]) < p) { photonPath->flux /= p; *ray = Ray(hitPoint, wi); } else { photonPath->done = true; } } } else { photonPath->done = true; } } } } uint oldc = rayBuffer->GetRayCount(); rayBuffer->Reset(); for (unsigned int i = 0; i < oldc; ++i) { PhotonPath *photonPath = &livePhotonPaths[i]; Ray *ray = &rayBuffer->GetRayBuffer()[i]; if (photonPath->done && todoPhotonCount < photonTarget) { todoPhotonCount++; Ray n; engine->InitPhotonPath(engine->ss, photonPath, &n, seedBuffer[i]); livePhotonPaths[i].done = false; size_t p = rayBuffer->AddRay(n); livePhotonPaths[p] = *photonPath; } else if (!photonPath->done) { rayBuffer->AddRay(*ray); } } } // float MPhotonsSec = todoPhotonCount / ((WallClockTime()-start) * 1000000.f); //printf("\nRate: %.3f MPhotons/sec\n",MPhotonsSec); profiler->addPhotonTracingTime(WallClockTime() - start); profiler->addPhotonsTraced(todoPhotonCount); rayBuffer->Reset(); return todoPhotonCount; }
void CPU_Worker::AdvanceEyePaths( RayBuffer *rayBuffer, EyePath* todoEyePaths, uint* eyePathIndexes) { const uint max = rayBuffer->GetRayCount(); omp_set_num_threads(config->max_threads); #pragma omp parallel for schedule(guided) for (uint i = 0; i < max; i++) { EyePath *eyePath = &todoEyePaths[eyePathIndexes[i]]; const RayHit *rayHit = &rayBuffer->GetHitBuffer()[i]; if (rayHit->Miss()) { // Add an hit point //HitPointInfo &hp = *(engine->GetHitPointInfo(eyePath->pixelIndex)); HitPointStaticInfo &hp = hitPointsStaticInfo_iterationCopy[eyePath->sampleIndex]; //HitPoint &hp = GetHitPoint(hitPointsIndex++); hp.type = CONSTANT_COLOR; hp.scrX = eyePath->scrX; hp.scrY = eyePath->scrY; // if (scene->infiniteLight) // hp.throughput = scene->infiniteLight->Le( // eyePath->ray.d) * eyePath->throughput; // else // hp.throughput = Spectrum(); if (ss->infiniteLight || ss->sunLight || ss->skyLight) { // hp.throughput = scene->infiniteLight->Le(eyePath->ray.d) * eyePath->throughput; if (ss->infiniteLight) ss->InfiniteLight_Le(&hp.throughput, &eyePath->ray.d, ss->infiniteLight, ss->infiniteLightMap); if (ss->sunLight) ss->SunLight_Le(&hp.throughput, &eyePath->ray.d, ss->sunLight); if (ss->skyLight) ss->SkyLight_Le(&hp.throughput, &eyePath->ray.d, ss->skyLight); hp.throughput *= eyePath->throughput; } else hp.throughput = Spectrum(); // Free the eye path //ihp.accumPhotonCount = 0; //ihp.accumReflectedFlux = Spectrum(); //ihp.photonCount = 0; //hp.reflectedFlux = Spectrum(); eyePath->done = true; //--todoEyePathCount; } else { // Something was hit Point hitPoint; Spectrum surfaceColor; Normal N, shadeN; if (engine->GetHitPointInformation(ss, &eyePath->ray, rayHit, hitPoint, surfaceColor, N, shadeN)) continue; // Get the material const unsigned int currentTriangleIndex = rayHit->index; const unsigned int currentMeshIndex = ss->meshIDs[currentTriangleIndex]; const uint materialIndex = ss->meshMats[currentMeshIndex]; POINTERFREESCENE::Material *hitPointMat = &ss->materials[materialIndex]; uint matType = hitPointMat->type; if (matType == MAT_AREALIGHT) { // Add an hit point //HitPointInfo &hp = *(engine->GetHitPointInfo( // eyePath->pixelIndex)); HitPointStaticInfo &hp = hitPointsStaticInfo_iterationCopy[eyePath->sampleIndex]; hp.type = CONSTANT_COLOR; hp.scrX = eyePath->scrX; hp.scrY = eyePath->scrY; //ihp.accumPhotonCount = 0; //ihp.accumReflectedFlux = Spectrum(); //ihp.photonCount = 0; //hp.reflectedFlux = Spectrum(); Vector md = -eyePath->ray.d; ss->AreaLight_Le(&hitPointMat->param.areaLight, &md, &N, &hp.throughput); hp.throughput *= eyePath->throughput; // Free the eye path eyePath->done = true; //--todoEyePathCount; } else { Vector wo = -eyePath->ray.d; float materialPdf; Vector wi; bool specularMaterial = true; float u0 = getFloatRNG(seedBuffer[eyePath->sampleIndex]); float u1 = getFloatRNG(seedBuffer[eyePath->sampleIndex]); float u2 = getFloatRNG(seedBuffer[eyePath->sampleIndex]); Spectrum f; switch (matType) { case MAT_MATTE: ss->Matte_Sample_f(&hitPointMat->param.matte, &wo, &wi, &materialPdf, &f, &shadeN, u0, u1, &specularMaterial); f *= surfaceColor; break; case MAT_MIRROR: ss->Mirror_Sample_f(&hitPointMat->param.mirror, &wo, &wi, &materialPdf, &f, &shadeN, &specularMaterial); f *= surfaceColor; break; case MAT_GLASS: ss->Glass_Sample_f(&hitPointMat->param.glass, &wo, &wi, &materialPdf, &f, &N, &shadeN, u0, &specularMaterial); f *= surfaceColor; break; case MAT_MATTEMIRROR: ss->MatteMirror_Sample_f(&hitPointMat->param.matteMirror, &wo, &wi, &materialPdf, &f, &shadeN, u0, u1, u2, &specularMaterial); f *= surfaceColor; break; case MAT_METAL: ss->Metal_Sample_f(&hitPointMat->param.metal, &wo, &wi, &materialPdf, &f, &shadeN, u0, u1, &specularMaterial); f *= surfaceColor; break; case MAT_MATTEMETAL: ss->MatteMetal_Sample_f(&hitPointMat->param.matteMetal, &wo, &wi, &materialPdf, &f, &shadeN, u0, u1, u2, &specularMaterial); f *= surfaceColor; break; case MAT_ALLOY: ss->Alloy_Sample_f(&hitPointMat->param.alloy, &wo, &wi, &materialPdf, &f, &shadeN, u0, u1, u2, &specularMaterial); f *= surfaceColor; break; case MAT_ARCHGLASS: ss->ArchGlass_Sample_f(&hitPointMat->param.archGlass, &wo, &wi, &materialPdf, &f, &N, &shadeN, u0, &specularMaterial); f *= surfaceColor; break; case MAT_NULL: wi = eyePath->ray.d; specularMaterial = 1; materialPdf = 1.f; // I have also to restore the original throughput //throughput = prevThroughput; break; default: // Huston, we have a problem... specularMaterial = 1; materialPdf = 0.f; break; } // if (f.r != f2.r || f.g != f2.g || f.b != f2.b) { // printf("d"); // } if ((materialPdf <= 0.f) || f.Black()) { // Add an hit point //HitPointInfo &hp = *(engine->GetHitPointInfo( // eyePath->pixelIndex)); HitPointStaticInfo &hp = hitPointsStaticInfo_iterationCopy[eyePath->sampleIndex]; hp.type = CONSTANT_COLOR; hp.scrX = eyePath->scrX; hp.scrY = eyePath->scrY; hp.throughput = Spectrum(); //ihp.accumPhotonCount = 0; //ihp.accumReflectedFlux = Spectrum(); //ihp.photonCount = 0; //hp.reflectedFlux = Spectrum(); // Free the eye path eyePath->done = true; //--todoEyePathCount; } else if (specularMaterial || (!hitPointMat->difuse)) { eyePath->throughput *= f / materialPdf; eyePath->ray = Ray(hitPoint, wi); } else { // Add an hit point //HitPointInfo &hp = *(engine->GetHitPointInfo( // eyePath->pixelIndex)); HitPointStaticInfo &hp = hitPointsStaticInfo_iterationCopy[eyePath->sampleIndex]; hp.type = SURFACE; hp.scrX = eyePath->scrX; hp.scrY = eyePath->scrY; //hp.material // = (SurfaceMaterial *) triMat; //ihp.accumPhotonCount = 0; //ihp.accumReflectedFlux = Spectrum(); //ihp.photonCount = 0; //hp.reflectedFlux = Spectrum(); hp.materialSS = materialIndex; hp.throughput = eyePath->throughput * surfaceColor; hp.position = hitPoint; hp.wo = -eyePath->ray.d; hp.normal = shadeN; // Free the eye path eyePath->done = true; //--todoEyePathCount; } } } } }
void LightCPURenderThread::TraceEyePath(Sampler *sampler, vector<SampleResult> *sampleResults) { LightCPURenderEngine *engine = (LightCPURenderEngine *)renderEngine; Scene *scene = engine->renderConfig->scene; PerspectiveCamera *camera = scene->camera; Film *film = threadFilm; const u_int filmWidth = film->GetWidth(); const u_int filmHeight = film->GetHeight(); // Sample offsets const u_int sampleBootSize = 11; const u_int sampleEyeStepSize = 3; Ray eyeRay; const float screenX = min(sampler->GetSample(0) * filmWidth, (float)(filmWidth - 1)); const float screenY = min(sampler->GetSample(1) * filmHeight, (float)(filmHeight - 1)); camera->GenerateRay(screenX, screenY, &eyeRay, sampler->GetSample(9), sampler->GetSample(10)); Spectrum radiance, eyePathThroughput(1.f, 1.f, 1.f); int depth = 1; while (depth <= engine->maxPathDepth) { const u_int sampleOffset = sampleBootSize + (depth - 1) * sampleEyeStepSize; RayHit eyeRayHit; BSDF bsdf; Spectrum connectionThroughput; const bool somethingWasHit = scene->Intersect(device, false, sampler->GetSample(sampleOffset), &eyeRay, &eyeRayHit, &bsdf, &connectionThroughput); if (!somethingWasHit) { // Nothing was hit, check infinite lights (including sun) const Spectrum throughput = eyePathThroughput * connectionThroughput; if (scene->envLight) radiance += throughput * scene->envLight->GetRadiance(scene, -eyeRay.d); if (scene->sunLight) radiance += throughput * scene->sunLight->GetRadiance(scene, -eyeRay.d); break; } else { // Something was hit, check if it is a light source if (bsdf.IsLightSource()) radiance = eyePathThroughput * connectionThroughput * bsdf.GetEmittedRadiance(scene); else { // Check if it is a specular bounce float bsdfPdf; Vector sampledDir; BSDFEvent event; float cosSampleDir; const Spectrum bsdfSample = bsdf.Sample(&sampledDir, sampler->GetSample(sampleOffset + 1), sampler->GetSample(sampleOffset + 2), &bsdfPdf, &cosSampleDir, &event); if (bsdfSample.Black() || ((depth == 1) && !(event & SPECULAR))) break; // If depth = 1 and it is a specular bounce, I continue to trace the // eye path looking for a light source eyePathThroughput *= connectionThroughput * bsdfSample * (cosSampleDir / bsdfPdf); assert (!eyePathThroughput.IsNaN() && !eyePathThroughput.IsInf()); eyeRay = Ray(bsdf.hitPoint, sampledDir); } ++depth; } } // Add a sample even if it is black in order to avoid aliasing problems // between sampled pixel and not sampled one (in PER_PIXEL_NORMALIZED buffer) AddSampleResult(*sampleResults, PER_PIXEL_NORMALIZED, screenX, screenY, radiance, (depth == 1) ? 1.f : 0.f); }
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 }
void LightCPURenderThread::RenderFunc() { //SLG_LOG("[LightCPURenderThread::" << threadIndex << "] Rendering thread started"); //-------------------------------------------------------------------------- // Initialization //-------------------------------------------------------------------------- LightCPURenderEngine *engine = (LightCPURenderEngine *)renderEngine; RandomGenerator *rndGen = new RandomGenerator(engine->seedBase + threadIndex); Scene *scene = engine->renderConfig->scene; PerspectiveCamera *camera = scene->camera; Film *film = threadFilm; // Setup the sampler double metropolisSharedTotalLuminance, metropolisSharedSampleCount; Sampler *sampler = engine->renderConfig->AllocSampler(rndGen, film, &metropolisSharedTotalLuminance, &metropolisSharedSampleCount); const u_int sampleBootSize = 11; const u_int sampleEyeStepSize = 4; const u_int sampleLightStepSize = 5; const u_int sampleSize = sampleBootSize + // To generate the initial setup engine->maxPathDepth * sampleEyeStepSize + // For each eye vertex engine->maxPathDepth * sampleLightStepSize; // For each light vertex sampler->RequestSamples(sampleSize); //-------------------------------------------------------------------------- // Trace light paths //-------------------------------------------------------------------------- vector<SampleResult> sampleResults; while (!boost::this_thread::interruption_requested()) { sampleResults.clear(); // Select one light source float lightPickPdf; const LightSource *light = scene->SampleAllLights(sampler->GetSample(2), &lightPickPdf); // Initialize the light path float lightEmitPdfW; Ray nextEventRay; Spectrum lightPathFlux = light->Emit(scene, sampler->GetSample(3), sampler->GetSample(4), sampler->GetSample(5), sampler->GetSample(6), &nextEventRay.o, &nextEventRay.d, &lightEmitPdfW); if (lightPathFlux.Black()) { sampler->NextSample(sampleResults); continue; } lightPathFlux /= lightEmitPdfW * lightPickPdf; assert (!lightPathFlux.IsNaN() && !lightPathFlux.IsInf()); // Sample a point on the camera lens Point lensPoint; if (!camera->SampleLens(sampler->GetSample(7), sampler->GetSample(8), &lensPoint)) { sampler->NextSample(sampleResults); continue; } //---------------------------------------------------------------------- // I don't try to connect the light vertex directly with the eye // because InfiniteLight::Emit() returns a point on the scene bounding // sphere. Instead, I trace a ray from the camera like in BiDir. // This is also a good why to test the Film Per-Pixel-Normalization and // the Per-Screen-Normalization Buffers used by BiDir. //---------------------------------------------------------------------- TraceEyePath(sampler, &sampleResults); //---------------------------------------------------------------------- // Trace the light path //---------------------------------------------------------------------- int depth = 1; while (depth <= engine->maxPathDepth) { const u_int sampleOffset = sampleBootSize + sampleEyeStepSize * engine->maxPathDepth + (depth - 1) * sampleLightStepSize; RayHit nextEventRayHit; BSDF bsdf; Spectrum connectionThroughput; if (scene->Intersect(device, true, sampler->GetSample(sampleOffset), &nextEventRay, &nextEventRayHit, &bsdf, &connectionThroughput)) { // Something was hit lightPathFlux *= connectionThroughput; //-------------------------------------------------------------- // Try to connect the light path vertex with the eye //-------------------------------------------------------------- ConnectToEye(sampler->GetSample(sampleOffset + 1), bsdf, lensPoint, lightPathFlux, sampleResults); if (depth >= engine->maxPathDepth) break; //-------------------------------------------------------------- // Build the next vertex path ray //-------------------------------------------------------------- float bsdfPdf; Vector sampledDir; BSDFEvent event; float cosSampleDir; const Spectrum bsdfSample = bsdf.Sample(&sampledDir, sampler->GetSample(sampleOffset + 2), sampler->GetSample(sampleOffset + 3), &bsdfPdf, &cosSampleDir, &event); if (bsdfSample.Black()) break; if (depth >= engine->rrDepth) { // Russian Roulette const float prob = Max(bsdfSample.Filter(), engine->rrImportanceCap); if (sampler->GetSample(sampleOffset + 4) < prob) bsdfPdf *= prob; else break; } lightPathFlux *= bsdfSample * (cosSampleDir / bsdfPdf); assert (!lightPathFlux.IsNaN() && !lightPathFlux.IsInf()); nextEventRay = Ray(bsdf.hitPoint, sampledDir); ++depth; } else { // Ray lost in space... break; } } sampler->NextSample(sampleResults); #ifdef WIN32 // Work around Windows bad scheduling renderThread->yield(); #endif } delete sampler; delete rndGen; //SLG_LOG("[LightCPURenderThread::" << threadIndex << "] Rendering thread halted"); }
// 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; }
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; }
void Path::AdvancePath(PathRenderEngine *renderEngine, Sampler *sampler, const RayBuffer *rayBuffer, SampleBuffer *sampleBuffer) { SLGScene *scene = renderEngine->scene; //-------------------------------------------------------------------------- // Select the path ray hit //-------------------------------------------------------------------------- const RayHit *rayHit = NULL; switch (state) { case EYE_VERTEX: if (scene->volumeIntegrator) { rayHit = rayBuffer->GetRayHit(currentPathRayIndex); // Use Russian Roulette to check if I have to do participating media computation or not if (sample.GetLazyValue() <= scene->volumeIntegrator->GetRRProbability()) { Ray volumeRay(pathRay.o, pathRay.d, 0.f, rayHit->Miss() ? std::numeric_limits<float>::infinity() : rayHit->t); scene->volumeIntegrator->GenerateLiRays(scene, &sample, volumeRay, volumeComp); radiance += volumeComp->GetEmittedLight(); if (volumeComp->GetRayCount() > 0) { // Do the EYE_VERTEX_VOLUME_STEP state = EYE_VERTEX_VOLUME_STEP; eyeHit = *(rayBuffer->GetRayHit(currentPathRayIndex)); return; } } } else rayHit = rayBuffer->GetRayHit(currentPathRayIndex); break; case NEXT_VERTEX: rayHit = rayBuffer->GetRayHit(currentPathRayIndex); break; case EYE_VERTEX_VOLUME_STEP: // Add scattered light radiance += throughput * volumeComp->CollectResults(rayBuffer) / scene->volumeIntegrator->GetRRProbability(); rayHit = &eyeHit; break; case TRANSPARENT_SHADOW_RAYS_STEP: rayHit = &eyeHit; break; case TRANSPARENT_ONLY_SHADOW_RAYS_STEP: case ONLY_SHADOW_RAYS: // Nothing break; default: assert(false); break; } //-------------------------------------------------------------------------- // Finish direct light sampling //-------------------------------------------------------------------------- if (((state == NEXT_VERTEX) || (state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_SHADOW_RAYS_STEP) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP)) && (tracedShadowRayCount > 0)) { unsigned int leftShadowRaysToTrace = 0; for (unsigned int i = 0; i < tracedShadowRayCount; ++i) { const RayHit *shadowRayHit = rayBuffer->GetRayHit(currentShadowRayIndex[i]); if (shadowRayHit->Miss()) { // Nothing was hit, light is visible radiance += lightColor[i] / lightPdf[i]; } else { // Something was hit check if it is transparent const unsigned int currentShadowTriangleIndex = shadowRayHit->index; const unsigned int currentShadowMeshIndex = scene->dataSet->GetMeshID(currentShadowTriangleIndex); Material *triMat = scene->objectMaterials[currentShadowMeshIndex]; if (triMat->IsShadowTransparent()) { // It is shadow transparent, I need to continue to trace the ray shadowRay[leftShadowRaysToTrace] = Ray( shadowRay[i](shadowRayHit->t), shadowRay[i].d, shadowRay[i].mint, shadowRay[i].maxt - shadowRayHit->t); lightColor[leftShadowRaysToTrace] = lightColor[i] * triMat->GetSahdowTransparency(); lightPdf[leftShadowRaysToTrace] = lightPdf[i]; leftShadowRaysToTrace++; } else { // Check if there is a texture with alpha TexMapInstance *tm = scene->objectTexMaps[currentShadowMeshIndex]; if (tm) { const TextureMap *map = tm->GetTexMap(); if (map->HasAlpha()) { const ExtMesh *mesh = scene->objects[currentShadowMeshIndex]; const UV triUV = mesh->InterpolateTriUV(scene->dataSet->GetMeshTriangleID(currentShadowTriangleIndex), shadowRayHit->b1, shadowRayHit->b2); const float alpha = map->GetAlpha(triUV); if (alpha < 1.f) { // It is shadow transparent, I need to continue to trace the ray shadowRay[leftShadowRaysToTrace] = Ray( shadowRay[i](shadowRayHit->t), shadowRay[i].d, shadowRay[i].mint, shadowRay[i].maxt - shadowRayHit->t); lightColor[leftShadowRaysToTrace] = lightColor[i] * (1.f - alpha); lightPdf[leftShadowRaysToTrace] = lightPdf[i]; leftShadowRaysToTrace++; } } } } } } if (leftShadowRaysToTrace > 0) { tracedShadowRayCount = leftShadowRaysToTrace; if ((state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP)) state = TRANSPARENT_ONLY_SHADOW_RAYS_STEP; else { eyeHit = *rayHit; state = TRANSPARENT_SHADOW_RAYS_STEP; } return; } } //-------------------------------------------------------------------------- // Calculate next step //-------------------------------------------------------------------------- depth++; const bool missed = rayHit ? rayHit->Miss() : false; if (missed || (state == ONLY_SHADOW_RAYS) || (state == TRANSPARENT_ONLY_SHADOW_RAYS_STEP) || (depth >= renderEngine->maxPathDepth)) { if (missed && scene->infiniteLight && (scene->useInfiniteLightBruteForce || specularBounce)) { // Add the light emitted by the infinite light radiance += scene->infiniteLight->Le(pathRay.d) * throughput; } // Hit nothing/only shadow rays/maxdepth, terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); return; } // Something was hit const unsigned int currentTriangleIndex = rayHit->index; const unsigned int currentMeshIndex = scene->dataSet->GetMeshID(currentTriangleIndex); // Get the triangle const ExtMesh *mesh = scene->objects[currentMeshIndex]; const unsigned int triIndex = scene->dataSet->GetMeshTriangleID(currentTriangleIndex); // Get the material const Material *triMat = scene->objectMaterials[currentMeshIndex]; // Check if it is a light source if (triMat->IsLightSource()) { if (specularBounce) { // Only TriangleLight can be directly hit const LightMaterial *mLight = (LightMaterial *) triMat; Spectrum Le = mLight->Le(mesh, triIndex, -pathRay.d); radiance += Le * throughput; } // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); return; } //-------------------------------------------------------------------------- // Build the shadow rays (if required) //-------------------------------------------------------------------------- // Interpolate face normal Normal N = mesh->InterpolateTriNormal(triIndex, rayHit->b1, rayHit->b2); const SurfaceMaterial *triSurfMat = (SurfaceMaterial *) triMat; const Point hitPoint = pathRay(rayHit->t); const Vector wo = -pathRay.d; Spectrum surfaceColor; if (mesh->HasColors()) surfaceColor = mesh->InterpolateTriColor(triIndex, rayHit->b1, rayHit->b2); else surfaceColor = Spectrum(1.f, 1.f, 1.f); // Check if I have to apply texture mapping or normal mapping TexMapInstance *tm = scene->objectTexMaps[currentMeshIndex]; BumpMapInstance *bm = scene->objectBumpMaps[currentMeshIndex]; NormalMapInstance *nm = scene->objectNormalMaps[currentMeshIndex]; if (tm || bm || nm) { // Interpolate UV coordinates if required const UV triUV = mesh->InterpolateTriUV(triIndex, rayHit->b1, rayHit->b2); // Check if there is an assigned texture map if (tm) { const TextureMap *map = tm->GetTexMap(); // Apply texture mapping surfaceColor *= map->GetColor(triUV); // Check if the texture map has an alpha channel if (map->HasAlpha()) { const float alpha = map->GetAlpha(triUV); if ((alpha == 0.0f) || ((alpha < 1.f) && (sample.GetLazyValue() > alpha))) { pathRay = Ray(pathRay(rayHit->t), pathRay.d, RAY_EPSILON, pathRay.maxt - rayHit->t); state = NEXT_VERTEX; tracedShadowRayCount = 0; return; } } } // Check if there is an assigned bump/normal map if (bm || nm) { if (nm) { // Apply normal mapping const Spectrum color = nm->GetTexMap()->GetColor(triUV); const float x = 2.f * (color.r - 0.5f); const float y = 2.f * (color.g - 0.5f); const float z = 2.f * (color.b - 0.5f); Vector v1, v2; CoordinateSystem(Vector(N), &v1, &v2); N = Normalize(Normal( v1.x * x + v2.x * y + N.x * z, v1.y * x + v2.y * y + N.y * z, v1.z * x + v2.z * y + N.z * z)); } if (bm) { // Apply bump mapping const TextureMap *map = bm->GetTexMap(); const UV &dudv = map->GetDuDv(); const float b0 = map->GetColor(triUV).Filter(); const UV uvdu(triUV.u + dudv.u, triUV.v); const float bu = map->GetColor(uvdu).Filter(); const UV uvdv(triUV.u, triUV.v + dudv.v); const float bv = map->GetColor(uvdv).Filter(); const float scale = bm->GetScale(); const Vector bump(scale * (bu - b0), scale * (bv - b0), 1.f); Vector v1, v2; CoordinateSystem(Vector(N), &v1, &v2); N = Normalize(Normal( v1.x * bump.x + v2.x * bump.y + N.x * bump.z, v1.y * bump.x + v2.y * bump.y + N.y * bump.z, v1.z * bump.x + v2.z * bump.y + N.z * bump.z)); } } } // Flip the normal if required Normal shadeN = (Dot(pathRay.d, N) > 0.f) ? -N : N; tracedShadowRayCount = 0; const bool skipInfiniteLight = !renderEngine->onlySampleSpecular; const unsigned int lightCount = scene->GetLightCount(skipInfiniteLight); if (triSurfMat->IsDiffuse() && (scene->GetLightCount(skipInfiniteLight) > 0)) { // Direct light sampling: trace shadow rays switch (renderEngine->lightStrategy) { case ALL_UNIFORM: { // ALL UNIFORM direct sampling light strategy const Spectrum lightThroughtput = throughput * surfaceColor; for (unsigned int j = 0; j < lightCount; ++j) { // Select the light to sample const LightSource *light = scene->GetLight(j, skipInfiniteLight); for (unsigned int i = 0; i < renderEngine->shadowRayCount; ++i) { // Select a point on the light surface lightColor[tracedShadowRayCount] = light->Sample_L( scene, hitPoint, &shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), &lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]); // Scale light pdf for ALL_UNIFORM strategy lightPdf[tracedShadowRayCount] *= renderEngine->shadowRayCount; if (lightPdf[tracedShadowRayCount] <= 0.0f) continue; const Vector lwi = shadowRay[tracedShadowRayCount].d; lightColor[tracedShadowRayCount] *= lightThroughtput * Dot(shadeN, lwi) * triSurfMat->f(wo, lwi, shadeN); if (!lightColor[tracedShadowRayCount].Black()) tracedShadowRayCount++; } } break; } case ONE_UNIFORM: { // ONE UNIFORM direct sampling light strategy const Spectrum lightThroughtput = throughput * surfaceColor; for (unsigned int i = 0; i < renderEngine->shadowRayCount; ++i) { // Select the light to sample float lightStrategyPdf; const LightSource *light = scene->SampleAllLights(sample.GetLazyValue(), &lightStrategyPdf, skipInfiniteLight); // Select a point on the light surface lightColor[tracedShadowRayCount] = light->Sample_L( scene, hitPoint, &shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), &lightPdf[tracedShadowRayCount], &shadowRay[tracedShadowRayCount]); if (lightPdf[tracedShadowRayCount] <= 0.f) continue; const Vector lwi = shadowRay[tracedShadowRayCount].d; lightColor[tracedShadowRayCount] *= lightThroughtput * Dot(shadeN, lwi) * triSurfMat->f(wo, lwi, shadeN); if (!lightColor[tracedShadowRayCount].Black()) { lightPdf[tracedShadowRayCount] *= lightStrategyPdf * renderEngine->shadowRayCount; tracedShadowRayCount++; } } break; } default: assert(false); } } //-------------------------------------------------------------------------- // Build the next vertex path ray //-------------------------------------------------------------------------- float fPdf; Vector wi; const Spectrum f = triSurfMat->Sample_f(wo, &wi, N, shadeN, sample.GetLazyValue(), sample.GetLazyValue(), sample.GetLazyValue(), renderEngine->onlySampleSpecular, &fPdf, specularBounce) * surfaceColor; if ((fPdf <= 0.f) || f.Black()) { if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } throughput *= f / fPdf; if (depth > renderEngine->rrDepth) { // Russian Roulette switch (renderEngine->rrStrategy) { case PROBABILITY: { if (renderEngine->rrProb >= sample.GetLazyValue()) throughput /= renderEngine->rrProb; else { // Check if terminate the path or I have still to trace shadow rays if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } break; } case IMPORTANCE: { const float prob = Max(throughput.Filter(), renderEngine->rrImportanceCap); if (prob >= sample.GetLazyValue()) throughput /= prob; else { // Check if terminate the path or I have still to trace shadow rays if (tracedShadowRayCount > 0) state = ONLY_SHADOW_RAYS; else { // Terminate the path sampleBuffer->SplatSample(sample.screenX, sample.screenY, radiance); // Restart the path Init(renderEngine, sampler); } return; } break; } default: assert(false); } } pathRay.o = hitPoint; pathRay.d = wi; state = NEXT_VERTEX; }
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 PathCPURenderThread::RenderFunc() { //SLG_LOG("[PathCPURenderEngine::" << threadIndex << "] Rendering thread started"); //-------------------------------------------------------------------------- // Initialization //-------------------------------------------------------------------------- PathCPURenderEngine *engine = (PathCPURenderEngine *)renderEngine; RandomGenerator *rndGen = new RandomGenerator(engine->seedBase + threadIndex); Scene *scene = engine->renderConfig->scene; PerspectiveCamera *camera = scene->camera; Film * film = threadFilm; const unsigned int filmWidth = film->GetWidth(); const unsigned int filmHeight = film->GetHeight(); // Setup the sampler double metropolisSharedTotalLuminance, metropolisSharedSampleCount; Sampler *sampler = engine->renderConfig->AllocSampler(rndGen, film, &metropolisSharedTotalLuminance, &metropolisSharedSampleCount); const unsigned int sampleBootSize = 4; const unsigned int sampleStepSize = 9; const unsigned int sampleSize = sampleBootSize + // To generate eye ray engine->maxPathDepth * sampleStepSize; // For each path vertex sampler->RequestSamples(sampleSize); //-------------------------------------------------------------------------- // Trace paths //-------------------------------------------------------------------------- vector<SampleResult> sampleResults(1); sampleResults[0].type = PER_PIXEL_NORMALIZED; while (!boost::this_thread::interruption_requested()) { float alpha = 1.f; Ray eyeRay; const float screenX = min(sampler->GetSample(0) * filmWidth, (float)(filmWidth - 1)); const float screenY = min(sampler->GetSample(1) * filmHeight, (float)(filmHeight - 1)); camera->GenerateRay(screenX, screenY, &eyeRay, sampler->GetSample(2), sampler->GetSample(3)); int depth = 1; bool lastSpecular = true; float lastPdfW = 1.f; Spectrum radiance; Spectrum pathThrouput(1.f, 1.f, 1.f); BSDF bsdf; while (depth <= engine->maxPathDepth) { const unsigned int sampleOffset = sampleBootSize + (depth - 1) * sampleStepSize; RayHit eyeRayHit; Spectrum connectionThroughput; if (!scene->Intersect(device, false, sampler->GetSample(sampleOffset), &eyeRay, &eyeRayHit, &bsdf, &connectionThroughput)) { // Nothing was hit, look for infinitelight DirectHitInfiniteLight(lastSpecular, pathThrouput * connectionThroughput, eyeRay.d, lastPdfW, &radiance); if (depth == 1) alpha = 0.f; break; } pathThrouput *= connectionThroughput; // Something was hit // Check if it is a light source if (bsdf.IsLightSource()) { DirectHitFiniteLight(lastSpecular, pathThrouput, eyeRayHit.t, bsdf, lastPdfW, &radiance); } // Note: pass-through check is done inside SceneIntersect() //------------------------------------------------------------------ // Direct light sampling //------------------------------------------------------------------ DirectLightSampling(sampler->GetSample(sampleOffset + 1), sampler->GetSample(sampleOffset + 2), sampler->GetSample(sampleOffset + 3), sampler->GetSample(sampleOffset + 4), sampler->GetSample(sampleOffset + 5), pathThrouput, bsdf, depth, &radiance); //------------------------------------------------------------------ // Build the next vertex path ray //------------------------------------------------------------------ Vector sampledDir; BSDFEvent event; float cosSampledDir; const Spectrum bsdfSample = bsdf.Sample(&sampledDir, sampler->GetSample(sampleOffset + 6), sampler->GetSample(sampleOffset + 7), &lastPdfW, &cosSampledDir, &event); if (bsdfSample.Black()) break; lastSpecular = ((event & SPECULAR) != 0); if ((depth >= engine->rrDepth) && !lastSpecular) { // Russian Roulette const float prob = Max(bsdfSample.Filter(), engine->rrImportanceCap); if (sampler->GetSample(sampleOffset + 8) < prob) lastPdfW *= prob; else break; } pathThrouput *= bsdfSample * (cosSampledDir / lastPdfW); assert (!pathThrouput.IsNaN() && !pathThrouput.IsInf()); eyeRay = Ray(bsdf.hitPoint, sampledDir); ++depth; } assert (!radiance.IsNaN() && !radiance.IsInf()); sampleResults[0].screenX = screenX; sampleResults[0].screenY = screenY; sampleResults[0].radiance = radiance; sampleResults[0].alpha = alpha; sampler->NextSample(sampleResults); #ifdef WIN32 // Work around Windows bad scheduling renderThread->yield(); #endif } delete sampler; delete rndGen; //SLG_LOG("[PathCPURenderEngine::" << threadIndex << "] Rendering thread halted"); }
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 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 = Spectrum(0.f); // 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()) { if(bsdf->NumComponents(BSDF_FLUORESCENT) > 0) { Bispectrum * fluoro = (Bispectrum*)bsdf->f_ptr(wo, wi, BxDFType(BSDF_FLUORESCENT)); if (visibility.Unoccluded(scene)) { // Add light's contribution to reflected radiance Li *= visibility.Transmittance(scene); if (light->IsDeltaLight()) Ld += fluoro->output(Li, true, false) * AbsDot(wi, n); else { bsdfPdf = bsdf->Pdf(wo, wi); //float weight = PowerHeuristic(1, lightPdf, 1, bsdfPdf); Ld += fluoro->output(Li, true, false) * AbsDot(wi, n)*bsdfPdf; } } }else{ Spectrum f = bsdf->f(wo, wi, BxDFType(BSDF_ALL & ~BSDF_FLUORESCENT)); 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); else { bsdfPdf = bsdf->Pdf(wo, wi); //float weight = PowerHeuristic(1, lightPdf, 1, bsdfPdf); Ld += f * Li * AbsDot(wi, n)*bsdfPdf; } } } }/* // Sample BSDF with multiple importance sampling if (!light->IsDeltaLight()) { BxDFType flags = BxDFType(BSDF_ALL & ~BSDF_SPECULAR & ~BSDF_FLUORESCENT); if(bsdf->NumComponents(BSDF_FLUORESCENT) > 0) { Bispectrum * fluoro = (Bispectrum*)bsdf->Sample_f_ptr(wo, &wi, bs1, bs2, bcs, &bsdfPdf, BxDFType(BSDF_FLUORESCENT)); if (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 += fluoro->output(Li, true, false) * AbsDot(wi, n)* weight / bsdfPdf; } } } }else{ 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; }
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 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; }