static Spectrum L(const Scene *scene, const Renderer *renderer, const Camera *camera, MemoryArena &arena, RNG &rng, int maxDepth, bool ignoreDirect, const MLTSample &sample) { // Generate camera ray from Metropolis sample RayDifferential ray; float cameraWeight = camera->GenerateRayDifferential(sample.cameraSample, &ray); Spectrum pathThroughput = cameraWeight, L = 0.; bool specularBounce = false, allSpecular = true; for (int pathLength = 0; pathLength < maxDepth; ++pathLength) { // Find next intersection in Metropolis light path Intersection isect; if (!scene->Intersect(ray, &isect)) { bool includeLe = ignoreDirect ? (specularBounce && !allSpecular) : (pathLength == 0 || specularBounce); if (includeLe) for (uint32_t i = 0; i < scene->lights.size(); ++i) L += pathThroughput * scene->lights[i]->Le(ray); break; } if (ignoreDirect ? (specularBounce && !allSpecular) : (specularBounce || pathLength == 0)) L += pathThroughput * isect.Le(-ray.d); BSDF *bsdf = isect.GetBSDF(ray, arena); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; const PathSample &ps = sample.pathSamples[pathLength]; // Sample direct illumination for Metropolis path vertex if (!ignoreDirect || pathLength > 0) { LightSample lightSample(ps.lightDir0, ps.lightDir1, ps.lightNum0); BSDFSample bsdfSample(ps.bsdfLightDir0, ps.bsdfLightDir1, ps.bsdfLightComponent); uint32_t lightNum = Floor2Int(ps.lightNum1 * scene->lights.size()); lightNum = min(lightNum, (uint32_t)(scene->lights.size()-1)); const Light *light = scene->lights[lightNum]; L += pathThroughput * EstimateDirect(scene, renderer, arena, light, p, n, wo, isect.rayEpsilon, sample.cameraSample.time, bsdf, rng, lightSample, bsdfSample); } // Sample direction for outgoing Metropolis path direction BSDFSample outgoingBSDFSample(ps.bsdfDir0, ps.bsdfDir1, ps.bsdfComponent); Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, outgoingBSDFSample, &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.) break; specularBounce = (flags & BSDF_SPECULAR) != 0; allSpecular &= specularBounce; pathThroughput *= f * AbsDot(wi, n) / pdf; ray = RayDifferential(p, wi, ray, isect.rayEpsilon); //pathThroughput *= renderer->Transmittance(scene, ray, NULL, rng, arena); } return L; }
Spectrum SampleIntegrator::E(const Scene *scene, const Point &p, const Normal &n, Float time, const Medium *medium, Sampler *sampler, int nSamples, bool handleIndirect) const { Spectrum E(0.0f); LuminaireSamplingRecord lRec; RadianceQueryRecord rRec(scene, sampler); Frame frame(n); sampler->generate(); for (int i=0; i<nSamples; i++) { rRec.newQuery(RadianceQueryRecord::ERadianceNoEmission, medium); /* Direct */ if (scene->sampleAttenuatedLuminaire(p, time, medium, lRec, rRec.nextSample2D())) { Float dp = dot(lRec.d, n); if (dp < 0) E -= lRec.value * dp; } /* Indirect */ if (handleIndirect) { Vector d = frame.toWorld(squareToHemispherePSA(rRec.nextSample2D())); ++rRec.depth; E += Li(RayDifferential(p, d, time), rRec) * M_PI; } sampler->advance(); } return E / (Float) nSamples; }
Spectrum E(const Scene *scene, const Intersection &its, const Medium *medium, Sampler *sampler, int nSamples, bool handleIndirect) const { Spectrum EDir(0.0f), EIndir(0.0f); DirectSamplingRecord dRec(its); /* Sample the direct illumination component */ for (int i=0; i<nSamples; i++) { int maxIntermediateInteractions = -1; Spectrum directRadiance = scene->sampleAttenuatedEmitterDirect( dRec, its, medium, maxIntermediateInteractions, sampler->next2D()); if (!directRadiance.isZero()) { Float dp = dot(dRec.d, its.shFrame.n); if (dp > 0) EDir += directRadiance * dp; } } if (handleIndirect) { RadianceQueryRecord rRec(scene, sampler); rRec.newQuery(RadianceQueryRecord::ERadianceNoEmission, medium); rRec.its = its; if (!m_irrCache->get(rRec.its, EIndir)) handleMiss(RayDifferential(), rRec, EIndir); } return (EDir / (Float) nSamples) + EIndir; }
float OrthoCamera::GenerateRayDifferential(const CameraSample &sample, RayDifferential *ray) const { // Compute main orthographic viewing ray // Generate raster and camera samples PbrtPoint Pras(sample.imageX, sample.imageY, 0); PbrtPoint Pcamera; RasterToCamera(Pras, &Pcamera); *ray = RayDifferential(Pcamera, Vector(0,0,1), 0., INFINITY); // Modify ray for depth of field if (lensRadius > 0.) { // Sample point on lens float lensU, lensV; ConcentricSampleDisk(sample.lensU, sample.lensV, &lensU, &lensV); lensU *= lensRadius; lensV *= lensRadius; // Compute point on plane of focus float ft = focalDistance / ray->d.z; PbrtPoint Pfocus = (*ray)(ft); // Update ray for effect of lens ray->o = PbrtPoint(lensU, lensV, 0.f); ray->d = Normalize(Pfocus - ray->o); } ray->time = Lerp(sample.time, shutterOpen, shutterClose); ray->rxOrigin = ray->o + dxCamera; ray->ryOrigin = ray->o + dyCamera; ray->rxDirection = ray->ryDirection = ray->d; ray->hasDifferentials = true; CameraToWorld(*ray, ray); return 1.f; }
float PerspectiveCamera::GenerateRayDifferential(const CameraSample &sample, RayDifferential *ray) const { // Generate raster and camera samples Point Pras(sample.imageX, sample.imageY, 0); Point Pcamera; RasterToCamera(Pras, &Pcamera); Vector dir = Normalize(Vector(Pcamera.x, Pcamera.y, Pcamera.z)); *ray = RayDifferential(Point(0,0,0), dir, 0.f, INFINITY); // Modify ray for depth of field if (lensRadius > 0.) { // Sample point on lens float lensU, lensV; ConcentricSampleDisk(sample.lensU, sample.lensV, &lensU, &lensV); lensU *= lensRadius; lensV *= lensRadius; // Compute point on plane of focus float ft = focalDistance / ray->d.z; Point Pfocus = (*ray)(ft); // Update ray for effect of lens ray->o = Point(lensU, lensV, 0.f); ray->d = Normalize(Pfocus - ray->o); } // Compute offset rays for _PerspectiveCamera_ ray differentials ray->rxOrigin = ray->ryOrigin = ray->o; ray->rxDirection = Normalize(Vector(Pcamera) + dxCamera); ray->ryDirection = Normalize(Vector(Pcamera) + dyCamera); ray->time = Lerp(sample.time, shutterOpen, shutterClose); CameraToWorld(*ray, ray); ray->hasDifferentials = true; return 1.f; }
Spectrum SamplingIntegrator::E(const Scene *scene, const Intersection &its, const Medium *medium, Sampler *sampler, int nSamples, bool handleIndirect) const { Spectrum E(0.0f); RadianceQueryRecord query(scene, sampler); DirectSamplingRecord dRec(its); Frame frame(its.shFrame.n); sampler->generate(Point2i(0)); for (int i=0; i<nSamples; i++) { /* Sample the direct illumination component */ int maxIntermediateInteractions = -1; Spectrum directRadiance = scene->sampleAttenuatedEmitterDirect( dRec, its, medium, maxIntermediateInteractions, query.nextSample2D()); if (!directRadiance.isZero()) { Float dp = dot(dRec.d, its.shFrame.n); if (dp > 0) E += directRadiance * dp; } /* Sample the indirect illumination component */ if (handleIndirect) { query.newQuery(RadianceQueryRecord::ERadianceNoEmission, medium); Vector d = frame.toWorld(Warp::squareToCosineHemisphere(query.nextSample2D())); ++query.depth; query.medium = medium; E += Li(RayDifferential(its.p, d, its.time), query) * M_PI; } sampler->advance(); } return E / (Float) nSamples; }
void process(const WorkUnit *workUnit, WorkResult *workResult, const bool &stop) { const RectangularWorkUnit *rect = static_cast<const RectangularWorkUnit *>(workUnit); IrradianceRecordVector *result = static_cast<IrradianceRecordVector *>(workResult); const SamplingIntegrator *integrator = m_subIntegrator.get(); Intersection its; RadianceQueryRecord rRec(m_scene, m_sampler); const int sx = rect->getOffset().x, sy = rect->getOffset().y, ex = sx + rect->getSize().x, ey = sy + rect->getSize().y; result->clear(); for (int y = sy; y < ey; y++) { for (int x = sx; x < ex; x++) { if (stop) break; Point2 pixelSample(x + .5f, y + .5f); RayDifferential ray; if (m_sensor->sampleRayDifferential(ray, pixelSample, Point2(0.5f), 0.5f).isZero()) continue; if (m_scene->rayIntersect(ray, its)) { const BSDF *bsdf = its.getBSDF(); if ((bsdf->getType() & BSDF::EAll) != BSDF::EDiffuseReflection) continue; Spectrum E; if (m_irrCache->get(its, E)) continue; /* Irradiance cache miss - create a record. The following generates stratified cosine-weighted samples and computes rotational + translational gradients */ m_hs->generateDirections(its); m_sampler->generate(Point2i(0)); for (unsigned int j=0; j<m_hs->getM(); j++) { for (unsigned int k=0; k<m_hs->getN(); k++) { HemisphereSampler::SampleEntry &entry = (*m_hs)(j, k); entry.dist = std::numeric_limits<Float>::infinity(); rRec.newQuery(RadianceQueryRecord::ERadianceNoEmission | RadianceQueryRecord::EDistance, m_sensor->getMedium()); rRec.extra = RadianceQueryRecord::ECacheQuery; rRec.depth = 2; entry.L = integrator->Li(RayDifferential(its.p, entry.d, 0.0f), rRec); entry.dist = rRec.dist; m_sampler->advance(); } } m_hs->process(its); result->put(m_irrCache->put(ray, its, *m_hs)); } } } }
Spectrum MetropolisRenderer::PathL(const MLTSample &sample, const Scene *scene, MemoryArena &arena, const Camera *camera, const Distribution1D *lightDistribution, PathVertex *cameraPath, PathVertex *lightPath, RNG &rng) const { // Generate camera path from camera path samples PBRT_STARTED_GENERATING_CAMERA_RAY((CameraSample *)(&sample.cameraSample)); RayDifferential cameraRay; float cameraWt = camera->GenerateRayDifferential(sample.cameraSample, &cameraRay); cameraRay.ScaleDifferentials(1.f / sqrtf(nPixelSamples)); PBRT_FINISHED_GENERATING_CAMERA_RAY((CameraSample *)(&sample.cameraSample), &cameraRay, cameraWt); RayDifferential escapedRay; Spectrum escapedAlpha; uint32_t cameraLength = GeneratePath(cameraRay, cameraWt, scene, arena, sample.cameraPathSamples, cameraPath, &escapedRay, &escapedAlpha); if (!bidirectional) { // Compute radiance along path using path tracing return Lpath(scene, cameraPath, cameraLength, arena, sample.lightingSamples, rng, sample.cameraSample.time, lightDistribution, escapedRay, escapedAlpha); } else { // Sample light ray and apply bidirectional path tracing // Choose light and sample ray to start light path PBRT_MLT_STARTED_SAMPLE_LIGHT_FOR_BIDIR(); float lightPdf, lightRayPdf; uint32_t lightNum = lightDistribution->SampleDiscrete(sample.lightNumSample, &lightPdf); const Light *light = scene->lights[lightNum]; Ray lightRay; Normal Nl; LightSample lrs(sample.lightRaySamples[0], sample.lightRaySamples[1], sample.lightRaySamples[2]); Spectrum lightWt = light->Sample_L(scene, lrs, sample.lightRaySamples[3], sample.lightRaySamples[4], sample.cameraSample.time, &lightRay, &Nl, &lightRayPdf); PBRT_MLT_FINISHED_SAMPLE_LIGHT_FOR_BIDIR(); if (lightWt.IsBlack() || lightRayPdf == 0.f) { // Compute radiance along path using path tracing return Lpath(scene, cameraPath, cameraLength, arena, sample.lightingSamples, rng, sample.cameraSample.time, lightDistribution, escapedRay, escapedAlpha); } else { // Compute radiance along paths using bidirectional path tracing lightWt *= AbsDot(Normalize(Nl), lightRay.d) / (lightPdf * lightRayPdf); uint32_t lightLength = GeneratePath(RayDifferential(lightRay), lightWt, scene, arena, sample.lightPathSamples, lightPath, NULL, NULL); return Lbidir(scene, cameraPath, cameraLength, lightPath, lightLength, arena, sample.lightingSamples, rng, sample.cameraSample.time, lightDistribution, escapedRay, escapedAlpha); } } }
// Metropolis Method Definitions static uint32_t GeneratePath(const RayDifferential &r, const Spectrum &a, const Scene *scene, MemoryArena &arena, const vector<PathSample> &samples, PathVertex *path, RayDifferential *escapedRay, Spectrum *escapedAlpha) { PBRT_MLT_STARTED_GENERATE_PATH(); RayDifferential ray = r; Spectrum alpha = a; if (escapedAlpha) *escapedAlpha = 0.f; uint32_t length = 0; for (; length < samples.size(); ++length) { // Try to generate next vertex of ray path PathVertex &v = path[length]; if (!scene->Intersect(ray, &v.isect)) { // Handle ray that leaves the scene during path generation if (escapedAlpha) *escapedAlpha = alpha; if (escapedRay) *escapedRay = ray; break; } // Record information for current path vertex v.alpha = alpha; BSDF *bsdf = v.isect.GetBSDF(ray, arena); v.bsdf = bsdf; v.wPrev = -ray.d; // Sample direction for outgoing Metropolis path direction float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(-ray.d, &v.wNext, samples[length].bsdfSample, &pdf, BSDF_ALL, &flags); v.specularBounce = (flags & BSDF_SPECULAR) != 0; v.nSpecularComponents = bsdf->NumComponents(BxDFType(BSDF_SPECULAR | BSDF_REFLECTION | BSDF_TRANSMISSION)); if (f.IsBlack() || pdf == 0.f) { PBRT_MLT_FINISHED_GENERATE_PATH(); return length+1; } // Terminate path with RR or prepare for finding next vertex const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Spectrum pathScale = f * AbsDot(v.wNext, n) / pdf; float rrSurviveProb = min(1.f, pathScale.y()); if (samples[length].rrSample > rrSurviveProb) { PBRT_MLT_FINISHED_GENERATE_PATH(); return length+1; } alpha *= pathScale / rrSurviveProb; //alpha *= renderer->Transmittance(scene, ray, NULL, rng, arena); ray = RayDifferential(p, v.wNext, ray, v.isect.rayEpsilon); } PBRT_MLT_FINISHED_GENERATE_PATH(); return length; }
Spectrum IrradianceCacheIntegrator::pathL(Ray &r, const Scene *scene, const Renderer *renderer, const Sample *sample, MemoryArena &arena) const { Spectrum L(0.f); Spectrum pathThroughput = 1.; RayDifferential ray(r); bool specularBounce = false; for (int pathLength = 0; ; ++pathLength) { // Find next vertex of path Intersection isect; if (!scene->Intersect(ray, &isect)) break; if (pathLength == 0) r.maxt = ray.maxt; else if (pathLength == 1) pathThroughput *= renderer->Transmittance(scene, ray, sample, arena, NULL); else pathThroughput *= renderer->Transmittance(scene, ray, NULL, arena, sample->rng); // Possibly add emitted light at path vertex if (specularBounce) L += pathThroughput * isect.Le(-ray.d); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena); // Sample illumination from lights to find path contribution const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; L += pathThroughput * UniformSampleOneLight(scene, renderer, arena, p, n, wo, isect.rayEpsilon, bsdf, sample); if (pathLength+1 == maxIndirectDepth) break; // Sample BSDF to get new path direction // Get random numbers for sampling new direction, \mono{bs1}, \mono{bs2}, and \mono{bcs} Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, BSDFSample(*sample->rng), &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.) break; specularBounce = (flags & BSDF_SPECULAR) != 0; pathThroughput *= f * AbsDot(wi, n) / pdf; ray = RayDifferential(p, wi, ray, isect.rayEpsilon); // Possibly terminate the path if (pathLength > 2) { float rrProb = min(1.f, pathThroughput.y()); if (sample->rng->RandomFloat() > rrProb) break; pathThroughput /= rrProb; } } return L; }
Float PerspectiveCamera::GenerateRayDifferential(const CameraSample &sample, RayDifferential *ray) const { ProfilePhase prof(Prof::GenerateCameraRay); // Compute raster and camera sample positions Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0); Point3f pCamera = RasterToCamera(pFilm); Vector3f dir = Normalize(Vector3f(pCamera.x, pCamera.y, pCamera.z)); *ray = RayDifferential(Point3f(0, 0, 0), dir); // Modify ray for depth of field if (lensRadius > 0) { // Sample point on lens Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens); // Compute point on plane of focus Float ft = focalDistance / ray->d.z; Point3f pFocus = (*ray)(ft); // Update ray for effect of lens ray->o = Point3f(pLens.x, pLens.y, 0); ray->d = Normalize(pFocus - ray->o); } // Compute offset rays for _PerspectiveCamera_ ray differentials if (lensRadius > 0) { // Compute _PerspectiveCamera_ ray differentials accounting for lens // Sample point on lens Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens); Vector3f dx = Normalize(Vector3f(pCamera + dxCamera)); Float ft = focalDistance / dx.z; Point3f pFocus = Point3f(0, 0, 0) + (ft * dx); ray->rxOrigin = Point3f(pLens.x, pLens.y, 0); ray->rxDirection = Normalize(pFocus - ray->rxOrigin); Vector3f dy = Normalize(Vector3f(pCamera + dyCamera)); ft = focalDistance / dy.z; pFocus = Point3f(0, 0, 0) + (ft * dy); ray->ryOrigin = Point3f(pLens.x, pLens.y, 0); ray->ryDirection = Normalize(pFocus - ray->ryOrigin); } else { ray->rxOrigin = ray->ryOrigin = ray->o; ray->rxDirection = Normalize(Vector3f(pCamera) + dxCamera); ray->ryDirection = Normalize(Vector3f(pCamera) + dyCamera); } ray->time = Lerp(sample.time, shutterOpen, shutterClose); ray->medium = medium; *ray = CameraToWorld(*ray); ray->hasDifferentials = true; return 1; }
Float OrthographicCamera::GenerateRayDifferential(const CameraSample &sample, RayDifferential *ray) const { ProfilePhase prof(Prof::GenerateCameraRay); // Compute main orthographic viewing ray // Compute raster and camera sample positions Point3f pFilm = Point3f(sample.pFilm.x, sample.pFilm.y, 0); Point3f pCamera = RasterToCamera(pFilm); *ray = RayDifferential(pCamera, Vector3f(0, 0, 1)); // Modify ray for depth of field if (lensRadius > 0) { // Sample point on lens Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens); // Compute point on plane of focus Float ft = focalDistance / ray->d.z; Point3f pFocus = (*ray)(ft); // Update ray for effect of lens ray->o = Point3f(pLens.x, pLens.y, 0); ray->d = Normalize(pFocus - ray->o); } // Compute ray differentials for _OrthographicCamera_ if (lensRadius > 0) { // Compute _OrthographicCamera_ ray differentials accounting for lens // Sample point on lens Point2f pLens = lensRadius * ConcentricSampleDisk(sample.pLens); Float ft = focalDistance / ray->d.z; Point3f pFocus = pCamera + dxCamera + (ft * Vector3f(0, 0, 1)); ray->rxOrigin = Point3f(pLens.x, pLens.y, 0); ray->rxDirection = Normalize(pFocus - ray->rxOrigin); pFocus = pCamera + dyCamera + (ft * Vector3f(0, 0, 1)); ray->ryOrigin = Point3f(pLens.x, pLens.y, 0); ray->ryDirection = Normalize(pFocus - ray->ryOrigin); } else { ray->rxOrigin = ray->o + dxCamera; ray->ryOrigin = ray->o + dyCamera; ray->rxDirection = ray->ryDirection = ray->d; } ray->time = Lerp(sample.time, shutterOpen, shutterClose); ray->hasDifferentials = true; ray->medium = medium; *ray = CameraToWorld(*ray); return 1; }
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; }
void handleMiss(RayDifferential ray, const RadianceQueryRecord &rRec, Spectrum &E) const { /* Handle an irradiance cache miss */ HemisphereSampler *hs = m_hemisphereSampler.get(); Sampler *sampler = m_sampleGenerator.get(); RadianceQueryRecord rRec2; if (hs == NULL) { Properties props("independent"); sampler = static_cast<Sampler *> (PluginManager::getInstance()-> createObject(MTS_CLASS(Sampler), props)); hs = new HemisphereSampler(m_resolution, 2 * m_resolution); m_hemisphereSampler.set(hs); m_sampleGenerator.set(sampler); } /* Generate stratified cosine-weighted samples and compute rotational + translational gradients */ hs->generateDirections(rRec.its); sampler->generate(Point2i(0)); for (unsigned int j=0; j<hs->getM(); j++) { for (unsigned int k=0; k<hs->getN(); k++) { HemisphereSampler::SampleEntry &entry = (*hs)(j, k); entry.dist = std::numeric_limits<Float>::infinity(); rRec2.recursiveQuery(rRec, RadianceQueryRecord::ERadianceNoEmission | RadianceQueryRecord::EDistance); rRec2.extra = 1; rRec2.sampler = sampler; entry.L = m_subIntegrator->Li(RayDifferential(rRec.its.p, entry.d, ray.time), rRec2); entry.dist = rRec2.dist; sampler->advance(); } } hs->process(rRec.its); /* Undo ray differential scaling done by the integrator */ if (ray.hasDifferentials) ray.scaleDifferential(m_diffScaleFactor); m_irrCache->put(ray, rRec.its, *hs); E = hs->getIrradiance(); }
Spectrum InfiniteAreaLightIS::Sample_L(const Scene *scene, float u1, float u2, float u3, float u4, Ray *ray, float *pdf) const { // Choose two points _p1_ and _p2_ on scene bounding sphere Point worldCenter; float worldRadius; scene->WorldBound().BoundingSphere(&worldCenter, &worldRadius); worldRadius *= 1.01f; Point p1 = worldCenter + worldRadius * UniformSampleSphere(u1, u2); Point p2 = worldCenter + worldRadius * UniformSampleSphere(u3, u4); // Construct ray between _p1_ and _p2_ ray->o = p1; ray->d = Normalize(p2-p1); // Compute _InfiniteAreaLightIS_ ray weight Vector to_center = Normalize(worldCenter - p1); float costheta = AbsDot(to_center,ray->d); *pdf = costheta / ((4.f * M_PI * worldRadius * worldRadius)); return Le(RayDifferential(ray->o, -ray->d)); }
void PhotonShootingTask::Run() { // Declare local variables for _PhotonShootingTask_ MemoryArena arena; RNG rng(31 * taskNum); vector<Photon> localDirectPhotons, localIndirectPhotons, localCausticPhotons; vector<RadiancePhoton> localRadiancePhotons; uint32_t totalPaths = 0; bool causticDone = (integrator->nCausticPhotonsWanted == 0); bool indirectDone = (integrator->nIndirectPhotonsWanted == 0); PermutedHalton halton(6, rng); vector<Spectrum> localRpReflectances, localRpTransmittances; while (true) { // Follow photon paths for a block of samples const uint32_t blockSize = 4096; for (uint32_t i = 0; i < blockSize; ++i) { float u[6]; halton.Sample(++totalPaths, u); // Choose light to shoot photon from float lightPdf; int lightNum = lightDistribution->SampleDiscrete(u[0], &lightPdf); const Light *light = scene->lights[lightNum]; // Generate _photonRay_ from light source and initialize _alpha_ RayDifferential photonRay; float pdf; LightSample ls(u[1], u[2], u[3]); Normal Nl; Spectrum Le = light->Sample_L(scene, ls, u[4], u[5], time, &photonRay, &Nl, &pdf); if (pdf == 0.f || Le.IsBlack()) continue; Spectrum alpha = (AbsDot(Nl, photonRay.d) * Le) / (pdf * lightPdf); if (!alpha.IsBlack()) { // Follow photon path through scene and record intersections PBRT_PHOTON_MAP_STARTED_RAY_PATH(&photonRay, &alpha); bool specularPath = true; Intersection photonIsect; int nIntersections = 0; while (scene->Intersect(photonRay, &photonIsect)) { ++nIntersections; // Handle photon/surface intersection alpha *= renderer->Transmittance(scene, photonRay, NULL, rng, arena); BSDF *photonBSDF = photonIsect.GetBSDF(photonRay, arena); BxDFType specularType = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_SPECULAR); bool hasNonSpecular = (photonBSDF->NumComponents() > photonBSDF->NumComponents(specularType)); Vector wo = -photonRay.d; if (hasNonSpecular) { // Deposit photon at surface Photon photon(photonIsect.dg.p, alpha, wo); bool depositedPhoton = false; if (specularPath && nIntersections > 1) { if (!causticDone) { PBRT_PHOTON_MAP_DEPOSITED_CAUSTIC_PHOTON(&photonIsect.dg, &alpha, &wo); depositedPhoton = true; localCausticPhotons.push_back(photon); } } else { // Deposit either direct or indirect photon // stop depositing direct photons once indirectDone is true; don't // want to waste memory storing too many if we're going a long time // trying to get enough caustic photons desposited. if (nIntersections == 1 && !indirectDone && integrator->finalGather) { PBRT_PHOTON_MAP_DEPOSITED_DIRECT_PHOTON(&photonIsect.dg, &alpha, &wo); depositedPhoton = true; localDirectPhotons.push_back(photon); } else if (nIntersections > 1 && !indirectDone) { PBRT_PHOTON_MAP_DEPOSITED_INDIRECT_PHOTON(&photonIsect.dg, &alpha, &wo); depositedPhoton = true; localIndirectPhotons.push_back(photon); } } // Possibly create radiance photon at photon intersection point if (depositedPhoton && integrator->finalGather && rng.RandomFloat() < .125f) { Normal n = photonIsect.dg.nn; n = Faceforward(n, -photonRay.d); localRadiancePhotons.push_back(RadiancePhoton(photonIsect.dg.p, n)); Spectrum rho_r = photonBSDF->rho(rng, BSDF_ALL_REFLECTION); localRpReflectances.push_back(rho_r); Spectrum rho_t = photonBSDF->rho(rng, BSDF_ALL_TRANSMISSION); localRpTransmittances.push_back(rho_t); } } if (nIntersections >= integrator->maxPhotonDepth) break; // Sample new photon ray direction Vector wi; float pdf; BxDFType flags; Spectrum fr = photonBSDF->Sample_f(wo, &wi, BSDFSample(rng), &pdf, BSDF_ALL, &flags); if (fr.IsBlack() || pdf == 0.f) break; Spectrum anew = alpha * fr * AbsDot(wi, photonBSDF->dgShading.nn) / pdf; // Possibly terminate photon path with Russian roulette float continueProb = min(1.f, anew.y() / alpha.y()); if (rng.RandomFloat() > continueProb) break; alpha = anew / continueProb; specularPath &= ((flags & BSDF_SPECULAR) != 0); if (indirectDone && !specularPath) break; photonRay = RayDifferential(photonIsect.dg.p, wi, photonRay, photonIsect.rayEpsilon); } PBRT_PHOTON_MAP_FINISHED_RAY_PATH(&photonRay, &alpha); } arena.FreeAll(); } // Merge local photon data with data in _PhotonIntegrator_ { MutexLock lock(mutex); // Give up if we're not storing enough photons if (abortTasks) return; if (nshot > 500000 && (unsuccessful(integrator->nCausticPhotonsWanted, causticPhotons.size(), blockSize) || unsuccessful(integrator->nIndirectPhotonsWanted, indirectPhotons.size(), blockSize))) { Error("Unable to store enough photons. Giving up.\n"); causticPhotons.erase(causticPhotons.begin(), causticPhotons.end()); indirectPhotons.erase(indirectPhotons.begin(), indirectPhotons.end()); radiancePhotons.erase(radiancePhotons.begin(), radiancePhotons.end()); abortTasks = true; return; } progress.Update(localIndirectPhotons.size() + localCausticPhotons.size()); nshot += blockSize; // Merge indirect photons into shared array if (!indirectDone) { integrator->nIndirectPaths += blockSize; for (uint32_t i = 0; i < localIndirectPhotons.size(); ++i) indirectPhotons.push_back(localIndirectPhotons[i]); localIndirectPhotons.erase(localIndirectPhotons.begin(), localIndirectPhotons.end()); if (indirectPhotons.size() >= integrator->nIndirectPhotonsWanted) indirectDone = true; nDirectPaths += blockSize; for (uint32_t i = 0; i < localDirectPhotons.size(); ++i) directPhotons.push_back(localDirectPhotons[i]); localDirectPhotons.erase(localDirectPhotons.begin(), localDirectPhotons.end()); } // Merge direct, caustic, and radiance photons into shared array if (!causticDone) { integrator->nCausticPaths += blockSize; for (uint32_t i = 0; i < localCausticPhotons.size(); ++i) causticPhotons.push_back(localCausticPhotons[i]); localCausticPhotons.erase(localCausticPhotons.begin(), localCausticPhotons.end()); if (causticPhotons.size() >= integrator->nCausticPhotonsWanted) causticDone = true; } for (uint32_t i = 0; i < localRadiancePhotons.size(); ++i) radiancePhotons.push_back(localRadiancePhotons[i]); localRadiancePhotons.erase(localRadiancePhotons.begin(), localRadiancePhotons.end()); for (uint32_t i = 0; i < localRpReflectances.size(); ++i) rpReflectances.push_back(localRpReflectances[i]); localRpReflectances.erase(localRpReflectances.begin(), localRpReflectances.end()); for (uint32_t i = 0; i < localRpTransmittances.size(); ++i) rpTransmittances.push_back(localRpTransmittances[i]); localRpTransmittances.erase(localRpTransmittances.begin(), localRpTransmittances.end()); } // Exit task if enough photons have been found if (indirectDone && causticDone) break; } }
void distributedRTPass(Scene *scene, std::vector<SerializableObject *> &samplers) { ref<Camera> camera = scene->getCamera(); bool needsLensSample = camera->needsLensSample(); bool needsTimeSample = camera->needsTimeSample(); ref<Film> film = camera->getFilm(); Vector2i cropSize = film->getCropSize(); Point2i cropOffset = film->getCropOffset(); /* Process the image in parallel using blocks for better memory locality */ Log(EInfo, "Creating %i gather points", cropSize.x*cropSize.y); #pragma omp parallel for schedule(dynamic) for (int i=-1; i<(int) m_gatherBlocks.size(); ++i) { std::vector<GatherPoint> &gatherPoints = m_gatherBlocks[i]; #if !defined(__OSX__) && defined(_OPENMP) Sampler *sampler = static_cast<Sampler *>(samplers[omp_get_thread_num()]); #else Sampler *sampler = static_cast<Sampler *>(samplers[0]); #endif int xofs = m_offset[i].x, yofs = m_offset[i].y; int index = 0; for (int yofsInt = 0; yofsInt < m_blockSize; ++yofsInt) { if (yofsInt + yofs - cropOffset.y >= cropSize.y) continue; for (int xofsInt = 0; xofsInt < m_blockSize; ++xofsInt) { if (xofsInt + xofs - cropOffset.x >= cropSize.x) continue; Point2 lensSample, sample; Float timeSample = 0.0f; GatherPoint &gatherPoint = gatherPoints[index++]; sampler->generate(); if (needsLensSample) lensSample = sampler->next2D(); if (needsTimeSample) timeSample = sampler->next1D(); gatherPoint.pos = Point2i(xofs + xofsInt, yofs + yofsInt); sample = sampler->next2D(); sample += Vector2((Float) gatherPoint.pos.x, (Float) gatherPoint.pos.y); RayDifferential ray; camera->generateRayDifferential(sample, lensSample, timeSample, ray); Spectrum weight(1.0f); int depth = 1; while (true) { if (depth > m_maxDepth) { gatherPoint.depth = -1; break; } if (scene->rayIntersect(ray, gatherPoint.its)) { const BSDF *bsdf = gatherPoint.its.shape->getBSDF(); /* Create hit point if this is a diffuse material or a glossy one, and there has been a previous interaction with a glossy material */ if (bsdf->getType() == BSDF::EDiffuseReflection || bsdf->getType() == BSDF::EDiffuseTransmission) { gatherPoint.weight = weight; gatherPoint.depth = depth; if (gatherPoint.its.isLuminaire()) gatherPoint.emission = gatherPoint.its.Le(-ray.d); else gatherPoint.emission = Spectrum(0.0f); break; } else { /* Recurse for dielectric materials and (specific to SPPM): recursive "final gathering" for glossy materials */ BSDFQueryRecord bRec(gatherPoint.its); weight *= bsdf->sampleCos(bRec, sampler->next2D()); if (weight.isZero()) { gatherPoint.depth = -1; break; } ray = RayDifferential(gatherPoint.its.p, gatherPoint.its.toWorld(bRec.wo), ray.time); ++depth; } } else { /* Generate an invalid sample */ gatherPoint.depth = -1; break; } } sampler->advance(); } } } }
void SurfacePointTask::Run() { // Declare common variables for _SurfacePointTask::Run()_ RNG rng(37 * taskNum); MemoryArena arena; vector<SurfacePoint> candidates; while (true) { int pathsTraced, raysTraced = 0; for (pathsTraced = 0; pathsTraced < 20000; ++pathsTraced) { // Follow ray path and attempt to deposit candidate sample points Vector dir = UniformSampleSphere(rng.RandomFloat(), rng.RandomFloat()); Ray ray(origin, dir, 0.f, INFINITY, time); while (ray.depth < 30) { // Find ray intersection with scene geometry or bounding sphere ++raysTraced; bool hitOnSphere = false; auto optIsect = scene.Intersect(ray); if (!optIsect) { optIsect = sphere.Intersect(ray); if (!optIsect) break; hitOnSphere = true; } DifferentialGeometry &hitGeometry = optIsect->dg; hitGeometry.nn = Faceforward(hitGeometry.nn, -ray.d); // Store candidate sample point at ray intersection if appropriate if (!hitOnSphere && ray.depth >= 3 && optIsect->GetBSSRDF(RayDifferential(ray), arena) != NULL) { float area = M_PI * (minSampleDist / 2.f) * (minSampleDist / 2.f); candidates.push_back(SurfacePoint(hitGeometry.p, hitGeometry.nn, area, optIsect->rayEpsilon)); } // Generate random ray from intersection point Vector dir = UniformSampleSphere(rng.RandomFloat(), rng.RandomFloat()); dir = Faceforward(dir, hitGeometry.nn); ray = Ray(hitGeometry.p, dir, ray, optIsect->rayEpsilon); } arena.FreeAll(); } // Make first pass through candidate points with reader lock vector<bool> candidateRejected; candidateRejected.reserve(candidates.size()); RWMutexLock lock(mutex, READ); for (uint32_t i = 0; i < candidates.size(); ++i) { PoissonCheck check(minSampleDist, candidates[i].p); octree.Lookup(candidates[i].p, check); candidateRejected.push_back(check.failed); } // Make second pass through points with writer lock and update octree lock.UpgradeToWrite(); if (repeatedFails >= maxFails) return; totalPathsTraced += pathsTraced; totalRaysTraced += raysTraced; int oldMaxRepeatedFails = maxRepeatedFails; for (uint32_t i = 0; i < candidates.size(); ++i) { if (candidateRejected[i]) { // Update for rejected candidate point ++repeatedFails; maxRepeatedFails = max(maxRepeatedFails, repeatedFails); if (repeatedFails >= maxFails) return; } else { // Recheck candidate point and possibly add to octree SurfacePoint &sp = candidates[i]; PoissonCheck check(minSampleDist, sp.p); octree.Lookup(sp.p, check); if (check.failed) { // Update for rejected candidate point ++repeatedFails; maxRepeatedFails = max(maxRepeatedFails, repeatedFails); if (repeatedFails >= maxFails) return; } else { ++numPointsAdded; repeatedFails = 0; Vector delta(minSampleDist, minSampleDist, minSampleDist); octree.Add(sp, BBox(sp.p-delta, sp.p+delta)); PBRT_SUBSURFACE_ADDED_POINT_TO_OCTREE(&sp, minSampleDist); surfacePoints.push_back(sp); } } } // Stop following paths if not finding new points if (repeatedFails > oldMaxRepeatedFails) { int delta = repeatedFails - oldMaxRepeatedFails; prog.Update(delta); } if (totalPathsTraced > 50000 && numPointsAdded == 0) { Warning("There don't seem to be any objects with BSSRDFs " "in this scene. Giving up."); return; } candidates.erase(candidates.begin(), candidates.end()); } }
void IGIIntegrator::Preprocess(const Scene *scene, const Camera *camera, const Renderer *renderer) { if (scene->lights.size() == 0) return; MemoryArena arena; RNG rng; // Compute samples for emitted rays from lights vector<float> lightNum(nLightPaths * nLightSets); vector<float> lightSampPos(2 * nLightPaths * nLightSets, 0.f); vector<float> lightSampComp(nLightPaths * nLightSets, 0.f); vector<float> lightSampDir(2 * nLightPaths * nLightSets, 0.f); LDShuffleScrambled1D(nLightPaths, nLightSets, &lightNum[0], rng); LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampPos[0], rng); LDShuffleScrambled1D(nLightPaths, nLightSets, &lightSampComp[0], rng); LDShuffleScrambled2D(nLightPaths, nLightSets, &lightSampDir[0], rng); // Precompute information for light sampling densities Distribution1D *lightDistribution = ComputeLightSamplingCDF(scene); for (uint32_t s = 0; s < nLightSets; ++s) { for (uint32_t i = 0; i < nLightPaths; ++i) { // Follow path _i_ from light to create virtual lights int sampOffset = s*nLightPaths + i; // Choose light source to trace virtual light path from float lightPdf; int ln = lightDistribution->SampleDiscrete(lightNum[sampOffset], &lightPdf); Light *light = scene->lights[ln]; // Sample ray leaving light source for virtual light path RayDifferential ray; float pdf; LightSample ls(lightSampPos[2*sampOffset], lightSampPos[2*sampOffset+1], lightSampComp[sampOffset]); Normal Nl; Spectrum alpha = light->Sample_L(scene, ls, lightSampDir[2*sampOffset], lightSampDir[2*sampOffset+1], camera->shutterOpen, &ray, &Nl, &pdf); if (pdf == 0.f || alpha.IsBlack()) continue; alpha /= pdf * lightPdf; Intersection isect; while (scene->Intersect(ray, &isect) && !alpha.IsBlack()) { // Create virtual light and sample new ray for path alpha *= renderer->Transmittance(scene, RayDifferential(ray), NULL, rng, arena); Vector wo = -ray.d; BSDF *bsdf = isect.GetBSDF(ray, arena); // Create virtual light at ray intersection point Spectrum contrib = alpha * bsdf->rho(wo, rng) / M_PI; virtualLights[s].push_back(VirtualLight(isect.dg.p, isect.dg.nn, contrib, isect.rayEpsilon)); // Sample new ray direction and update weight for virtual light path Vector wi; float pdf; BSDFSample bsdfSample(rng); Spectrum fr = bsdf->Sample_f(wo, &wi, bsdfSample, &pdf); if (fr.IsBlack() || pdf == 0.f) break; Spectrum contribScale = fr * AbsDot(wi, bsdf->dgShading.nn) / pdf; // Possibly terminate virtual light path with Russian roulette float rrProb = min(1.f, contribScale.y()); if (rng.RandomFloat() > rrProb) break; alpha *= contribScale / rrProb; ray = RayDifferential(isect.dg.p, wi, ray, isect.rayEpsilon); } arena.FreeAll(); } } delete lightDistribution; }
void PhotonIntegrator::Preprocess(const Scene *scene) { if (scene->lights.size() == 0) return; ProgressReporter progress(nCausticPhotons+nDirectPhotons+ // NOBOOK nIndirectPhotons, "Shooting photons"); // NOBOOK vector<Photon> causticPhotons; vector<Photon> directPhotons; vector<Photon> indirectPhotons; causticPhotons.reserve(nCausticPhotons); // NOBOOK directPhotons.reserve(nDirectPhotons); // NOBOOK indirectPhotons.reserve(nIndirectPhotons); // NOBOOK // Initialize photon shooting statistics static StatsCounter nshot("Photon Map", "Number of photons shot from lights"); bool causticDone = (nCausticPhotons == 0); bool directDone = (nDirectPhotons == 0); bool indirectDone = (nIndirectPhotons == 0); while (!causticDone || !directDone || !indirectDone) { ++nshot; // Give up if we're not storing enough photons if (nshot > 500000 && (unsuccessful(nCausticPhotons, causticPhotons.size(), nshot) || unsuccessful(nDirectPhotons, directPhotons.size(), nshot) || unsuccessful(nIndirectPhotons, indirectPhotons.size(), nshot))) { Error("Unable to store enough photons. Giving up.\n"); return; } // Trace a photon path and store contribution // Choose 4D sample values for photon float u[4]; u[0] = (float)RadicalInverse((int)nshot+1, 2); u[1] = (float)RadicalInverse((int)nshot+1, 3); u[2] = (float)RadicalInverse((int)nshot+1, 5); u[3] = (float)RadicalInverse((int)nshot+1, 7); // Choose light to shoot photon from int nLights = int(scene->lights.size()); int lightNum = min(Floor2Int(nLights * (float)RadicalInverse((int)nshot+1, 11)), nLights-1); Light *light = scene->lights[lightNum]; float lightPdf = 1.f / nLights; // Generate _photonRay_ from light source and initialize _alpha_ RayDifferential photonRay; float pdf; Spectrum alpha = light->Sample_L(scene, u[0], u[1], u[2], u[3], &photonRay, &pdf); if (pdf == 0.f || alpha.Black()) continue; alpha /= pdf * lightPdf; if (!alpha.Black()) { // Follow photon path through scene and record intersections bool specularPath = false; Intersection photonIsect; int nIntersections = 0; while (scene->Intersect(photonRay, &photonIsect)) { ++nIntersections; // Handle photon/surface intersection alpha *= scene->Transmittance(photonRay); Vector wo = -photonRay.d; BSDF *photonBSDF = photonIsect.GetBSDF(photonRay); BxDFType specularType = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_SPECULAR); bool hasNonSpecular = (photonBSDF->NumComponents() > photonBSDF->NumComponents(specularType)); if (hasNonSpecular) { // Deposit photon at surface Photon photon(photonIsect.dg.p, alpha, wo); if (nIntersections == 1) { // Process direct lighting photon intersection if (!directDone) { directPhotons.push_back(photon); if (directPhotons.size() == nDirectPhotons) { directDone = true; nDirectPaths = (int)nshot; directMap = new KdTree<Photon, PhotonProcess>(directPhotons); } progress.Update(); // NOBOOK } } else if (specularPath) { // Process caustic photon intersection if (!causticDone) { causticPhotons.push_back(photon); if (causticPhotons.size() == nCausticPhotons) { causticDone = true; nCausticPaths = (int)nshot; causticMap = new KdTree<Photon, PhotonProcess>(causticPhotons); } progress.Update(); } } else { // Process indirect lighting photon intersection if (!indirectDone) { indirectPhotons.push_back(photon); if (indirectPhotons.size() == nIndirectPhotons) { indirectDone = true; nIndirectPaths = (int)nshot; indirectMap = new KdTree<Photon, PhotonProcess>(indirectPhotons); } progress.Update(); } } } // Sample new photon ray direction Vector wi; float pdf; BxDFType flags; // Get random numbers for sampling outgoing photon direction float u1, u2, u3; if (nIntersections == 1) { u1 = (float)RadicalInverse((int)nshot+1, 13); u2 = (float)RadicalInverse((int)nshot+1, 17); u3 = (float)RadicalInverse((int)nshot+1, 19); } else { u1 = RandomFloat(); u2 = RandomFloat(); u3 = RandomFloat(); } Spectrum fr = photonBSDF->Sample_f(wo, &wi, u1, u2, u3, &pdf, BSDF_ALL, &flags); if (fr.Black() || pdf == 0.f) break; specularPath = (nIntersections == 1 || specularPath) && ((flags & BSDF_SPECULAR) != 0); alpha *= fr * AbsDot(wi, photonBSDF->dgShading.nn) / pdf; photonRay = RayDifferential(photonIsect.dg.p, wi); // Possibly terminate photon path if (nIntersections > 3) { float continueProbability = .5f; if (RandomFloat() > continueProbability) break; alpha /= continueProbability; } } } BSDF::FreeAll(); } progress.Done(); // NOBOOK }
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 } }
void IGIIntegrator::Preprocess(const Scene *scene) { if (scene->lights.size() == 0) return; // Compute samples for emitted rays from lights float *lightNum = new float[nLightPaths * nLightSets]; float *lightSamp0 = new float[2 * nLightPaths * nLightSets]; float *lightSamp1 = new float[2 * nLightPaths * nLightSets]; LDShuffleScrambled1D(nLightPaths, nLightSets, lightNum); LDShuffleScrambled2D(nLightPaths, nLightSets, lightSamp0); LDShuffleScrambled2D(nLightPaths, nLightSets, lightSamp1); // Precompute information for light sampling densities int nLights = int(scene->lights.size()); float *lightPower = (float *)alloca(nLights * sizeof(float)); float *lightCDF = (float *)alloca((nLights+1) * sizeof(float)); for (int i = 0; i < nLights; ++i) lightPower[i] = scene->lights[i]->Power(scene).y(); float totalPower; ComputeStep1dCDF(lightPower, nLights, &totalPower, lightCDF); for (u_int s = 0; s < nLightSets; ++s) { for (u_int i = 0; i < nLightPaths; ++i) { // Follow path _i_ from light to create virtual lights int sampOffset = s*nLightPaths + i; // Choose light source to trace path from float lightPdf; int lNum = Floor2Int(SampleStep1d(lightPower, lightCDF, totalPower, nLights, lightNum[sampOffset], &lightPdf) * nLights); // fprintf(stderr, "samp %f -> num %d\n", lightNum[sampOffset], lNum); Light *light = scene->lights[lNum]; // Sample ray leaving light source RayDifferential ray; float pdf; Spectrum alpha = light->Sample_L(scene, lightSamp0[2*sampOffset], lightSamp0[2*sampOffset+1], lightSamp1[2*sampOffset], lightSamp1[2*sampOffset+1], &ray, &pdf); if (pdf == 0.f || alpha.Black()) continue; alpha /= pdf * lightPdf; // fprintf(stderr, "initial alpha %f, light # %d\n", alpha.y(), lNum); Intersection isect; int nIntersections = 0; while (scene->Intersect(ray, &isect) && !alpha.Black()) { ++nIntersections; alpha *= scene->Transmittance(ray); Vector wo = -ray.d; BSDF *bsdf = isect.GetBSDF(ray); // Create virtual light at ray intersection point static StatsCounter vls("IGI Integrator", "Virtual Lights Created"); //NOBOOK ++vls; //NOBOOK Spectrum Le = alpha * bsdf->rho(wo) / M_PI; // fprintf(stderr, "\tmade light with le y %f\n", Le.y()); virtualLights[s].push_back(VirtualLight(isect.dg.p, isect.dg.nn, Le)); // Sample new ray direction and update weight Vector wi; float pdf; BxDFType flags; Spectrum fr = bsdf->Sample_f(wo, &wi, RandomFloat(), RandomFloat(), RandomFloat(), &pdf, BSDF_ALL, &flags); if (fr.Black() || pdf == 0.f) break; Spectrum anew = alpha * fr * AbsDot(wi, bsdf->dgShading.nn) / pdf; float r = anew.y() / alpha.y(); // fprintf(stderr, "\tr = %f\n", r); if (RandomFloat() > r) break; alpha = anew / r; // fprintf(stderr, "\tnew alpha %f\n", alpha.y()); ray = RayDifferential(isect.dg.p, wi); } BSDF::FreeAll(); } } delete[] lightNum; // NOBOOK delete[] lightSamp0; // NOBOOK delete[] lightSamp1; // NOBOOK }
Spectrum PathIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &r, const Intersection &isect, const Sample *sample, MemoryArena &arena) const { // Declare common path integration variables Spectrum pathThroughput = 1., L = 0.; RayDifferential ray(r); bool specularBounce = false; Intersection localIsect; const Intersection *isectp = &isect; for (int pathLength = 0; ; ++pathLength) { // Possibly add emitted light at path vertex if (pathLength == 0 || specularBounce) L += pathThroughput * isectp->Le(-ray.d); // Sample illumination from lights to find path contribution BSDF *bsdf = isectp->GetBSDF(ray, arena); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; if (pathLength < SAMPLE_DEPTH) L += pathThroughput * UniformSampleOneLight(scene, renderer, arena, p, n, wo, isectp->RayEpsilon, bsdf, sample, lightNumOffset[pathLength], &lightSampleOffsets[pathLength], &bsdfSampleOffsets[pathLength]); else L += pathThroughput * UniformSampleOneLight(scene, renderer, arena, p, n, wo, isectp->RayEpsilon, bsdf, sample); // Sample BSDF to get new path direction // Get _outgoingBSDFSample_ for sampling new path direction BSDFSample outgoingBSDFSample; if (pathLength < SAMPLE_DEPTH) outgoingBSDFSample = BSDFSample(sample, pathSampleOffsets[pathLength], 0); else outgoingBSDFSample = BSDFSample(*sample->rng); Vector wi; float pdf; BxDFType flags; Spectrum f = bsdf->Sample_f(wo, &wi, outgoingBSDFSample, &pdf, BSDF_ALL, &flags); if (f.IsBlack() || pdf == 0.) break; specularBounce = (flags & BSDF_SPECULAR) != 0; pathThroughput *= f * AbsDot(wi, n) / pdf; ray = RayDifferential(p, wi, ray, isectp->RayEpsilon); // Possibly terminate the path if (pathLength > 3) { float continueProbability = min(.5f, pathThroughput.y()); if (sample->rng->RandomFloat() > continueProbability) break; pathThroughput /= continueProbability; } if (pathLength == maxDepth) break; // Find next vertex of path if (!scene->Intersect(ray, &localIsect)) { if (specularBounce) { for (u_int i = 0; i < scene->lights.size(); ++i) L += pathThroughput * scene->lights[i]->Le(ray); } break; } if (pathLength > 1) pathThroughput *= renderer->Transmittance(scene, ray, NULL, arena, sample->rng); isectp = &localIsect; } return L; }
Spectrum_d PhotonLTEIntegrator::_FinalGather(const Intersection &i_intersection, const Vector3D_d &i_incident, const BSDF *ip_bsdf, const Sample *ip_sample, ThreadSpecifics i_ts) const { ASSERT(ip_bsdf); ASSERT(mp_irradiance_map); ASSERT(i_incident.IsNormalized()); MemoryPool *p_pool = i_ts.mp_pool; RandomGenerator<double> *p_rng = i_ts.mp_random_generator; Vector3D_d shading_normal = ip_bsdf->GetShadingNormal(); const double cos_gather_angle = 0.9848; // 10 degrees const double cone_pdf = SamplingRoutines::UniformConePDF(cos_gather_angle); BxDFType non_specular = BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_DIFFUSE | BSDF_GLOSSY); size_t gather_samples = m_params.m_gather_samples_num; if (gather_samples == 0) return Spectrum_d(); // Maybe it is worth to put this block out of the _FinalGather() method to improve the performance. const size_t samples_num_sqrt = 5; Point2D_d bsdf_scattering_samples[samples_num_sqrt*samples_num_sqrt]; SamplesSequence2D bsdf_scattering_sequence(bsdf_scattering_samples, bsdf_scattering_samples + samples_num_sqrt*samples_num_sqrt); SamplingRoutines::StratifiedSampling2D(bsdf_scattering_sequence.m_begin, samples_num_sqrt, samples_num_sqrt, false); SamplesSequence1D bsdf_1D_samples, direction_1D_samples; SamplesSequence2D bsdf_2D_samples, direction_2D_samples; if (ip_sample) { bsdf_1D_samples = ip_sample->GetSamplesSequence1D(m_bsdf_1D_samples_id); bsdf_2D_samples = ip_sample->GetSamplesSequence2D(m_bsdf_2D_samples_id); direction_1D_samples = ip_sample->GetSamplesSequence1D(m_direction_1D_samples_id); direction_2D_samples = ip_sample->GetSamplesSequence2D(m_direction_2D_samples_id); } else { size_t gather_samples_sqrt = (size_t) ceil(sqrt((double)gather_samples)); gather_samples = gather_samples_sqrt*gather_samples_sqrt; // BSDF samples. double *bsdf_samples_1D = (double *)p_pool->Alloc( gather_samples * sizeof(double) ); Point2D_d *bsdf_samples_2D = (Point2D_d *)p_pool->Alloc( gather_samples * sizeof(Point2D_d) ); bsdf_1D_samples = SamplesSequence1D(bsdf_samples_1D, bsdf_samples_1D+gather_samples); bsdf_2D_samples = SamplesSequence2D(bsdf_samples_2D, bsdf_samples_2D+gather_samples); SamplingRoutines::StratifiedSampling1D(bsdf_1D_samples.m_begin, gather_samples, true, p_rng); SamplingRoutines::StratifiedSampling2D(bsdf_2D_samples.m_begin, gather_samples_sqrt, gather_samples_sqrt, true, p_rng); // Direction samples. double *direction_samples_1D = (double *)p_pool->Alloc( gather_samples * sizeof(double) ); Point2D_d *direction_samples_2D = (Point2D_d *)p_pool->Alloc( gather_samples * sizeof(Point2D_d) ); direction_1D_samples = SamplesSequence1D(direction_samples_1D, direction_samples_1D+gather_samples); direction_2D_samples = SamplesSequence2D(direction_samples_2D, direction_samples_2D+gather_samples); SamplingRoutines::StratifiedSampling1D(direction_1D_samples.m_begin, gather_samples, true, p_rng); SamplingRoutines::StratifiedSampling2D(direction_2D_samples.m_begin, gather_samples_sqrt, gather_samples_sqrt, true, p_rng); } // Allocate array for the nearest photons. size_t photons_found = 0; NearestPhoton *p_nearest_photons = (NearestPhoton*)p_pool->Alloc(32 * sizeof(NearestPhoton)); // Search for nearest indirect photons. if (mp_photon_maps->GetIndirectMap() && mp_photon_maps->GetIndirectMap()->GetNumberOfPoints()>0) { double indirect_photon_area = m_scene_total_area / mp_photon_maps->GetIndirectMap()->GetNumberOfPoints(); double max_lookup_dist = sqrt(indirect_photon_area*32*INV_PI); PhotonFilter filter(i_intersection.m_dg.m_point, ip_bsdf->GetGeometricNormal(), MAX_NORMAL_DEVIATION_COS); photons_found = mp_photon_maps->GetIndirectMap()->GetNearestPoints(i_intersection.m_dg.m_point, 32, p_nearest_photons, filter, max_lookup_dist); } double inv_photons_found = (photons_found>0) ? 1.0/photons_found : 0.0; // Copy photon directions to a local array. Vector3D_d *photon_directions = (Vector3D_d*)p_pool->Alloc(photons_found * sizeof(Vector3D_d)); for (size_t i=0;i<photons_found;++i) photon_directions[i] = p_nearest_photons[i].mp_point->m_incident_direction.ToVector3D<double>(); size_t gather_rays = 0; Vector3D_d *p_gather_directions = (Vector3D_d*)p_pool->Alloc( 2 * gather_samples * sizeof(Vector3D_d) ); SpectrumCoef_d *p_gather_weights = (SpectrumCoef_d*)p_pool->Alloc( 2 * gather_samples * sizeof(SpectrumCoef_d) ); // Sample BSDF. SamplesSequence1D::Iterator iterator_1D = bsdf_1D_samples.m_begin; SamplesSequence2D::Iterator iterator_2D = bsdf_2D_samples.m_begin; for (size_t i=0;i<gather_samples;++i) { double component_sample = *iterator_1D; Point2D_d bxdf_sample = *iterator_2D; double bsdf_pdf = 0.0; BxDFType sampled_type; Vector3D_d exitant; SpectrumCoef_d reflectance = ip_bsdf->Sample(i_incident, exitant, bxdf_sample, component_sample, bsdf_pdf, sampled_type, BxDFType(BSDF_ALL & ~BSDF_SPECULAR)); if (bsdf_pdf>0.0 && reflectance.IsBlack()==false) { // Compute PDF for photon-sampling for the sampled direction. // We construct a cone around each photon and sample directions in the cone uniformly. double photon_pdf = 0.0; for (size_t j=0;j<photons_found;++j) if (photon_directions[j]*exitant > cos_gather_angle) photon_pdf += cone_pdf; photon_pdf *= inv_photons_found; double weight = SamplingRoutines::PowerHeuristic(gather_samples, bsdf_pdf, gather_samples, photon_pdf); p_gather_directions[gather_rays] = exitant; p_gather_weights[gather_rays] = reflectance * (weight * fabs(exitant*shading_normal) / bsdf_pdf); ++gather_rays; } ++iterator_1D; ++iterator_2D; } // Sample nearby photons. iterator_1D = direction_1D_samples.m_begin; iterator_2D = direction_2D_samples.m_begin; for (size_t i=0;i<gather_samples && photons_found>0;++i) { size_t sampled_index = (size_t)( (*iterator_1D) * photons_found); // Sample gather ray direction. Vector3D_d e2, e3; MathRoutines::CoordinateSystem(photon_directions[sampled_index], e2, e3); Vector3D_d direction_local = SamplingRoutines::UniformConeSampling(*iterator_2D, cos_gather_angle); Vector3D_d exitant = direction_local[0]*e2 + direction_local[1]*e3 + direction_local[2]*photon_directions[sampled_index]; SpectrumCoef_d reflectance = ip_bsdf->Evaluate(i_incident, exitant); if (reflectance.IsBlack()==false) { double bsdf_pdf = ip_bsdf->PDF(i_incident, exitant); // Compute PDF for photon-sampling of direction exitant. // We construct a cone around each photon and sample directions in the cone uniformly. double photon_pdf = 0.0; for (size_t j=0;j<photons_found;++j) if (photon_directions[j]*exitant > cos_gather_angle) photon_pdf += cone_pdf; photon_pdf *= inv_photons_found; double weight = SamplingRoutines::PowerHeuristic(gather_samples, photon_pdf, gather_samples, bsdf_pdf); p_gather_directions[gather_rays] = exitant; p_gather_weights[gather_rays] = reflectance * (weight * fabs(exitant*shading_normal) / photon_pdf); ++gather_rays; } ++iterator_1D; ++iterator_2D; } // Trace final gather rays and compute the radiance. Spectrum_d radiance; for(size_t i=0;i<gather_rays;++i) { Ray bounce_ray(i_intersection.m_dg.m_point, p_gather_directions[i]); bounce_ray.m_min_t = CoreUtils::GetNextMinT(i_intersection, p_gather_directions[i]); Intersection gather_isect; if (mp_scene->Intersect(RayDifferential(bounce_ray), gather_isect, &bounce_ray.m_max_t)) { // Compute exitant radiance at the final gather intersection. Vector3D_d gather_direction = bounce_ray.m_direction*(-1.0); const BSDF *p_gather_BSDF = gather_isect.mp_primitive->GetBSDF(gather_isect.m_dg, gather_isect.m_triangle_index, *p_pool); Vector3D_d gather_geometric_normal = p_gather_BSDF->GetGeometricNormal(); IrradiancePhotonFilter filter(gather_isect.m_dg.m_point, gather_geometric_normal, MAX_NORMAL_DEVIATION_COS); const IrradiancePhoton *p_irradiance_photon = mp_irradiance_map->GetNearestPoint(gather_isect.m_dg.m_point, filter, m_max_irradiance_lookup_dist); if (p_irradiance_photon == NULL) continue; Spectrum_d tmp; if (gather_direction*gather_geometric_normal > 0.0) { tmp += p_gather_BSDF->TotalScattering(gather_direction, bsdf_scattering_sequence, BxDFType(BSDF_ALL_REFLECTION)) *Convert<double>(p_irradiance_photon->m_external_irradiance); tmp += p_gather_BSDF->TotalScattering(gather_direction, bsdf_scattering_sequence, BxDFType(BSDF_ALL_TRANSMISSION))*Convert<double>(p_irradiance_photon->m_internal_irradiance); } else { tmp += p_gather_BSDF->TotalScattering(gather_direction, bsdf_scattering_sequence, BxDFType(BSDF_ALL_REFLECTION)) *Convert<double>(p_irradiance_photon->m_internal_irradiance); tmp += p_gather_BSDF->TotalScattering(gather_direction, bsdf_scattering_sequence, BxDFType(BSDF_ALL_TRANSMISSION))*Convert<double>(p_irradiance_photon->m_external_irradiance); } tmp *= INV_PI; radiance += tmp * _MediaTransmittance(bounce_ray, i_ts) * p_gather_weights[i]; } } return radiance / (double)gather_samples; }
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; }
void VolumePatIntegrator::EyeRandomWalk(const Scene *scene, const Ray &eyeRay, VolumeVertexList& vertexList, RNG &rng) const { // Do a random walk for the eye ray in the volume Spectrum cummulative(1.f); // Find the intersection between the eye ray and the volume VolumeRegion *vr = scene->volumeRegion; float t0, t1; if (!vr || !vr->IntersectP(eyeRay, &t0, &t1) || (t1-t0) == 0.f || t0 < 0.f) { return; } // Find the intersection point between the sampled light ray and the volume RayDifferential ray(eyeRay); Point p = ray(t0), pPrev; uint64_t bounces = 0; while(vr->WorldBound().Inside(p)) { Vector wi = -ray.d; const Spectrum sigma_a = vr->Sigma_a(p, wi, eyeRay.time); const Spectrum sigma_s = vr->Sigma_s(p, wi, eyeRay.time); const Spectrum STER = vr->STER(p, wi, eyeRay.time); // Construct and add the _eyeVertex_ to the _vertexList_ VolumeVertex eyeVertex(p, wi, sigma_a, sigma_s, cummulative, 1.0); vertexList.push_back(eyeVertex); // Sample the direction of the next event float directionPdf = 1.f; Vector wo; if(STER.y() > rng.RandomFloat()) { // Change the ray direction due to a scattering event at _p_ if(!vr->SampleDirection(p, wi, wo, &directionPdf, rng)) { break; // Direction error } // Account for the losses due to the scattering event at _p_ cummulative *= sigma_s * vr->p(p, wi, wo, ray.time); } else { // Account only for the trnsmittance between the previous and the // next events becuse there is no direction change. wo = ray.d; } // Sample the distance of the next event ray = RayDifferential(p, wo, 0, INFINITY); float tDist; float distancePdf = 1.f; Point Psample; if(!vr->SampleDistance(ray, &tDist, Psample, &distancePdf, rng)) { break; // The sampled point is outside the volume } // Account for the sampling Pdfs from sampling a direction and/or distance const float pdf = distancePdf * directionPdf; cummulative *= 1 / pdf; // Update the events and account for the transmittance between the events pPrev = p; p = Psample; const Ray tauRay(pPrev, p - pPrev, 0.f, 1.f, ray.time, ray.depth); const Spectrum stepTau = vr->tau(tauRay, .5f * stepSize, rng.RandomFloat()); const Spectrum TrPP = Exp(-stepTau); cummulative *= TrPP; // Possibly terminate ray marching if _cummulative_ is small if (cummulative.y() < 1e-3) { const float continueProb = .5f; if (rng.RandomFloat() > continueProb) { cummulative = 0.f; break; } cummulative /= continueProb; } // Terminate if bounces are more than requested bounces++; if (bounces > maxDepth) { break; } } }
Spectrum VisibilityTester::Transmittance(const Scene *scene, const Renderer *renderer, const Sample *sample, RNG &rng, MemoryArena &arena) const { return renderer->Transmittance(scene, RayDifferential(r), sample, rng, arena); }
void LightShootingTask::Run() { // tady by mel byt kod z photon mappingu MemoryArena arena; uint32_t totalPaths = 0; RNG rng(seed); PermutedHalton halton(6, rng); while (true) { // Follow photon paths for a block of samples const uint32_t blockSize = 4096; for (uint32_t i = 0; i < blockSize; ++i) { float u[6]; halton.Sample(++totalPaths, u); // Choose light to shoot photon from float lightPdf; int lightNum = lightDistribution->SampleDiscrete(u[0], &lightPdf); const Light *light = scene->lights[lightNum]; // Generate _photonRay_ from light source and initialize _alpha_ RayDifferential photonRay; float pdf; LightSample ls(u[1], u[2], u[3]); Normal Nl; Spectrum Le = light->Sample_L(scene, ls, u[4], u[5],time, &photonRay, &Nl, &pdf); if (pdf == 0.f || Le.IsBlack()) continue; Spectrum alpha = (AbsDot(Nl, photonRay.d) * Le) / (pdf * lightPdf); if (!alpha.IsBlack()) { // Follow photon path through scene and record intersections PBRT_PHOTON_MAP_STARTED_RAY_PATH(&photonRay, &alpha); bool specularPath = true; Intersection photonIsect; int nIntersections = 0; while (scene->Intersect(photonRay, &photonIsect)) { ++nIntersections; //MC tady by mel byt i kod pro volumetriku // Handle photon/surface intersection // alpha *= renderer->Transmittance(scene, photonRay, NULL, rng, arena); BSDF *photonBSDF = photonIsect.GetBSDF(photonRay, arena); Vector wo = -photonRay.d; //MC tady se ukladaly photony takze tady bych mel ukladat samples do filmu kamery // // Deposit photon at surface //Photon photon(photonIsect.dg.p, alpha, wo); //tuhle metodu chci pouzit //filmAddSample() if (nIntersections >= maxDepth) break; // Sample new photon ray direction Vector wi; float pdf; BxDFType flags; Spectrum fr = photonBSDF->Sample_f(wo, &wi, BSDFSample(rng), &pdf, BSDF_ALL, &flags); if (fr.IsBlack() || pdf == 0.f) break; Spectrum anew = alpha * fr * AbsDot(wi, photonBSDF->dgShading.nn) / pdf; // Possibly terminate photon path with Russian roulette float continueProb = min(1.f, anew.y() / alpha.y()); if (rng.RandomFloat() > continueProb) break; alpha = anew / continueProb; specularPath &= ((flags & BSDF_SPECULAR) != 0); photonRay = RayDifferential(photonIsect.dg.p, wi, photonRay, photonIsect.rayEpsilon); } PBRT_PHOTON_MAP_FINISHED_RAY_PATH(&photonRay, &alpha); } arena.FreeAll(); } //termination criteria ??? if (totalPaths==maxPathCount) { break; } } }