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 PhotonVolumeIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Sample *sample, RNG &rng, Spectrum *T, MemoryArena &arena) const { VolumeRegion *vr = scene->volumeRegion; RainbowVolume* rv = dynamic_cast<RainbowVolume*>(vr); KdTree<Photon>* volumeMap = photonShooter->volumeMap; float t0, t1; if (!vr || !vr->IntersectP(ray, &t0, &t1) || (t1-t0) == 0.f){ *T = 1.f; return 0.f; } // Do single scattering & photon multiple scattering volume integration in _vr_ Spectrum Lv(0.); // Prepare for volume integration stepping int nSamples = Ceil2Int((t1-t0) / stepSize); float step = (t1 - t0) / nSamples; Spectrum Tr(1.f); Point p = ray(t0), pPrev; Vector w = -ray.d; t0 += sample->oneD[scatterSampleOffset][0] * step; float *lightNum = arena.Alloc<float>(nSamples); LDShuffleScrambled1D(1, nSamples, lightNum, rng); float *lightComp = arena.Alloc<float>(nSamples); LDShuffleScrambled1D(1, nSamples, lightComp, rng); float *lightPos = arena.Alloc<float>(2*nSamples); LDShuffleScrambled2D(1, nSamples, lightPos, rng); int sampOffset = 0; ClosePhoton *lookupBuf = new ClosePhoton[nSamples]; for (int i = 0; i < nSamples; ++i, t0 += step) { // Advance to sample at _t0_ and update _T_ pPrev = p; p = ray(t0); Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth); Spectrum stepTau = vr->tau(tauRay,.5f * stepSize, rng.RandomFloat()); Tr = Exp(-stepTau); // Possibly terminate raymarching if transmittance is small. if (Tr.y() < 1e-3) { const float continueProb = .5f; if (rng.RandomFloat() > continueProb){ Tr = 0.f; break; } Tr /= continueProb; } // Compute single-scattering source term at _p_ & photon mapped MS Spectrum L_i(0.); Spectrum L_d(0.); Spectrum L_ii(0.); // Lv += Tr*vr->Lve(p, w, ray.time); Spectrum ss = vr->sigma_s(p, w, ray.time); Spectrum sa = vr->sigma_a(p, w, ray.time); if (!ss.IsBlack() && scene->lights.size() > 0) { int nLights = scene->lights.size(); int ln = min(Floor2Int(lightNum[sampOffset] * nLights), nLights-1); Light *light = scene->lights[ln]; // Add contribution of _light_ due to scattering at _p_ float pdf; VisibilityTester vis; Vector wo; LightSample ls(lightComp[sampOffset], lightPos[2*sampOffset], lightPos[2*sampOffset+1]); Spectrum L = light->Sample_L(p, 0.f, ls, ray.time, &wo, &pdf, &vis); if (!L.IsBlack() && pdf > 0.f && vis.Unoccluded(scene)) { Spectrum Ld = L * vis.Transmittance(scene,renderer, NULL, rng, arena); if(rv){ L_d = rv->rainbowReflection(Ld, ray.d, wo); } else { L_d = vr->p(p, w, -wo, ray.time) * Ld * float(nLights)/pdf; } } } // Compute 'indirect' in-scattered radiance from photon map if(!rv){ L_ii += LPhoton(volumeMap, nUsed, lookupBuf, w, p, vr, maxDistSquared, ray.time); } // Compute total in-scattered radiance if (sa.y()!=0.0 || ss.y()!=0.0) L_i = L_d + (ss/(sa+ss))*L_ii; else L_i = L_d; Spectrum nLv = (sa*vr->Lve(p,w,ray.time)*step) + (ss*L_i*step) + (Tr * Lv) ; Lv = nLv; sampOffset++; } *T = Tr; return Lv; }
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; }