void LightTracerRenderer::Render(const Scene *scene) { // Compute light power CDF for photon shooting Distribution1D *lightDistribution = ComputeLightSamplingCDF(scene); vector<Task *> lightShootingTasks; int nTasks = NumSystemCores(); //iteratively increase fidelity of the image for (int iter=0; iter<niter; iter++) { //multi tasking one for each processor for (int i = 0; i < nTasks; ++i) lightShootingTasks.push_back(new LightShootingTask(scene,camera,lightDistribution, camera ? camera->shutterOpen : 0.f,maxPathCount,seed*(i+1))); EnqueueTasks(lightShootingTasks); WaitForAllTasks(); for (uint32_t i = 0; i < lightShootingTasks.size(); ++i) delete lightShootingTasks[i]; //write image camera->film->WriteImage(); } }
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, const Camera *camera, const Renderer *renderer) { if (scene->lights.size() == 0) return; // Declare shared variables for photon shooting Mutex *mutex = Mutex::Create(); int nDirectPaths = 0; vector<Photon> causticPhotons, directPhotons, indirectPhotons; vector<RadiancePhoton> radiancePhotons; bool abortTasks = false; causticPhotons.reserve(nCausticPhotonsWanted); indirectPhotons.reserve(nIndirectPhotonsWanted); uint32_t nshot = 0; vector<Spectrum> rpReflectances, rpTransmittances; // Compute light power CDF for photon shooting Distribution1D *lightDistribution = ComputeLightSamplingCDF(scene); // Run parallel tasks for photon shooting ProgressReporter progress(nCausticPhotonsWanted+nIndirectPhotonsWanted, "Shooting photons"); vector<Task *> photonShootingTasks; int nTasks = NumSystemCores(); for (int i = 0; i < nTasks; ++i) photonShootingTasks.push_back(new PhotonShootingTask( i, camera ? camera->shutterOpen : 0.f, *mutex, this, progress, abortTasks, nDirectPaths, directPhotons, indirectPhotons, causticPhotons, radiancePhotons, rpReflectances, rpTransmittances, nshot, lightDistribution, scene, renderer)); EnqueueTasks(photonShootingTasks); WaitForAllTasks(); for (uint32_t i = 0; i < photonShootingTasks.size(); ++i) delete photonShootingTasks[i]; Mutex::Destroy(mutex); progress.Done(); // Build kd-trees for indirect and caustic photons KdTree<Photon> *directMap = NULL; if (directPhotons.size() > 0) directMap = new KdTree<Photon>(directPhotons); if (causticPhotons.size() > 0) causticMap = new KdTree<Photon>(causticPhotons); if (indirectPhotons.size() > 0) indirectMap = new KdTree<Photon>(indirectPhotons); // Precompute radiance at a subset of the photons if (finalGather && radiancePhotons.size()) { // Launch tasks to compute photon radiances vector<Task *> radianceTasks; uint32_t numTasks = 64; ProgressReporter progRadiance(numTasks, "Computing photon radiances"); for (uint32_t i = 0; i < numTasks; ++i) radianceTasks.push_back(new ComputeRadianceTask(progRadiance, i, numTasks, radiancePhotons, rpReflectances, rpTransmittances, nLookup, maxDistSquared, nDirectPaths, directMap, nIndirectPaths, indirectMap, nCausticPaths, causticMap)); EnqueueTasks(radianceTasks); WaitForAllTasks(); for (uint32_t i = 0; i < radianceTasks.size(); ++i) delete radianceTasks[i]; progRadiance.Done(); radianceMap = new KdTree<RadiancePhoton>(radiancePhotons); } delete directMap; }
void MetropolisRenderer::Render(const Scene *scene) { PBRT_MLT_STARTED_RENDERING(); if (scene->lights.size() > 0) { int x0, x1, y0, y1; camera->film->GetPixelExtent(&x0, &x1, &y0, &y1); float t0 = camera->shutterOpen, t1 = camera->shutterClose; Distribution1D *lightDistribution = ComputeLightSamplingCDF(scene); if (directLighting != NULL) { PBRT_MLT_STARTED_DIRECTLIGHTING(); // Compute direct lighting before Metropolis light transport if (nDirectPixelSamples > 0) { LDSampler sampler(x0, x1, y0, y1, nDirectPixelSamples, t0, t1); Sample *sample = new Sample(&sampler, directLighting, NULL, scene); vector<Task *> directTasks; int nDirectTasks = max(32 * NumSystemCores(), (camera->film->xResolution * camera->film->yResolution) / (16*16)); nDirectTasks = RoundUpPow2(nDirectTasks); ProgressReporter directProgress(nDirectTasks, "Direct Lighting"); for (int i = 0; i < nDirectTasks; ++i) directTasks.push_back(new SamplerRendererTask(scene, this, camera, directProgress, &sampler, sample, false, i, nDirectTasks)); std::reverse(directTasks.begin(), directTasks.end()); EnqueueTasks(directTasks); WaitForAllTasks(); for (uint32_t i = 0; i < directTasks.size(); ++i) delete directTasks[i]; delete sample; directProgress.Done(); } camera->film->WriteImage(); PBRT_MLT_FINISHED_DIRECTLIGHTING(); } // Take initial set of samples to compute $b$ PBRT_MLT_STARTED_BOOTSTRAPPING(nBootstrap); RNG rng(0); MemoryArena arena; vector<float> bootstrapI; vector<PathVertex> cameraPath(maxDepth, PathVertex()); vector<PathVertex> lightPath(maxDepth, PathVertex()); float sumI = 0.f; bootstrapI.reserve(nBootstrap); MLTSample sample(maxDepth); for (uint32_t i = 0; i < nBootstrap; ++i) { // Generate random sample and path radiance for MLT bootstrapping float x = Lerp(rng.RandomFloat(), x0, x1); float y = Lerp(rng.RandomFloat(), y0, y1); LargeStep(rng, &sample, maxDepth, x, y, t0, t1, bidirectional); Spectrum L = PathL(sample, scene, arena, camera, lightDistribution, &cameraPath[0], &lightPath[0], rng); // Compute contribution for random sample for MLT bootstrapping float I = ::I(L); sumI += I; bootstrapI.push_back(I); arena.FreeAll(); } float b = sumI / nBootstrap; PBRT_MLT_FINISHED_BOOTSTRAPPING(b); Info("MLT computed b = %f", b); // Select initial sample from bootstrap samples float contribOffset = rng.RandomFloat() * sumI; rng.Seed(0); sumI = 0.f; MLTSample initialSample(maxDepth); for (uint32_t i = 0; i < nBootstrap; ++i) { float x = Lerp(rng.RandomFloat(), x0, x1); float y = Lerp(rng.RandomFloat(), y0, y1); LargeStep(rng, &initialSample, maxDepth, x, y, t0, t1, bidirectional); sumI += bootstrapI[i]; if (sumI > contribOffset) break; } // Launch tasks to generate Metropolis samples uint32_t nTasks = largeStepsPerPixel; uint32_t largeStepRate = nPixelSamples / largeStepsPerPixel; Info("MLT running %d tasks, large step rate %d", nTasks, largeStepRate); ProgressReporter progress(nTasks * largeStepRate, "Metropolis"); vector<Task *> tasks; Mutex *filmMutex = Mutex::Create(); Assert(IsPowerOf2(nTasks)); uint32_t scramble[2] = { rng.RandomUInt(), rng.RandomUInt() }; uint32_t pfreq = (x1-x0) * (y1-y0); for (uint32_t i = 0; i < nTasks; ++i) { float d[2]; Sample02(i, scramble, d); tasks.push_back(new MLTTask(progress, pfreq, i, d[0], d[1], x0, x1, y0, y1, t0, t1, b, initialSample, scene, camera, this, filmMutex, lightDistribution)); } EnqueueTasks(tasks); WaitForAllTasks(); for (uint32_t i = 0; i < tasks.size(); ++i) delete tasks[i]; progress.Done(); Mutex::Destroy(filmMutex); delete lightDistribution; } camera->film->WriteImage(); PBRT_MLT_FINISHED_RENDERING(); }
void MLTIntegrator::Render(const Scene &scene) { lightDistr = std::unique_ptr<Distribution1D>(ComputeLightSamplingCDF(scene)); Film &film = *camera->film; // Generate bootstrap samples and compute $b$ int bootstrapSamples = nBootstrap * (maxDepth + 1); std::unique_ptr<Float[]> bootstrapWeights(new Float[bootstrapSamples]); { ProgressReporter progress(nBootstrap, "Generating bootstrap paths"); ParallelFor([&](int k) { // Generate a single bootstrap sample MemoryArena arena; for (int depth = 0; depth <= maxDepth; ++depth) { uint32_t uIndex = k * (maxDepth + 1) + depth; MLTSampler sampler(mutationsPerPixel, uIndex, sigma, largeStepProb); Point2f samplePos; bootstrapWeights[uIndex] = L(scene, arena, sampler, depth, &samplePos).y(); } progress.Update(); }, nBootstrap); progress.Done(); } Distribution1D bootstrap(bootstrapWeights.get(), bootstrapSamples); Float b = bootstrap.funcInt * (maxDepth + 1); // Run _nChains_ Markov Chains in parallel int64_t nTotalMutations = mutationsPerPixel * (int64_t)film.GetSampleBounds().Area(); { StatTimer timer(&renderingTime); ProgressReporter progress(nTotalMutations / 100, "Rendering"); ParallelFor([&](int k) { int64_t nChainMutations = std::min((k + 1) * nTotalMutations / nChains, nTotalMutations) - k * nTotalMutations / nChains; MemoryArena arena; std::unique_ptr<FilmTile> filmTile = film.GetFilmTile(Bounds2i( film.croppedPixelBounds.pMin, film.croppedPixelBounds.pMin)); // Select initial state from the set of bootstrap samples RNG rng(PCG32_DEFAULT_STATE, k); int bootstrapIndex = bootstrap.SampleDiscrete(rng.UniformFloat()); int depth = bootstrapIndex % (maxDepth + 1); // Initialize local variables for selected state MLTSampler sampler(mutationsPerPixel, bootstrapIndex, sigma, largeStepProb); Point2f currentPos, proposalPos; Spectrum currentL, proposalL; currentL = L(scene, arena, sampler, depth, ¤tPos); // Run the Markov Chain for _nChainMutations_ steps for (int64_t i = 0; i != nChainMutations; ++i) { sampler.Begin(); proposalL = L(scene, arena, sampler, depth, &proposalPos); // Compute the acceptance rate Float accept = std::min((Float)1, proposalL.y() / currentL.y()); // Splat both current and proposed samples to _FilmTile_ if (accept > 0) filmTile->AddSplat(proposalPos, proposalL * accept / proposalL.y()); filmTile->AddSplat(currentPos, currentL * (1 - accept) / currentL.y()); // Accept or reject the proposal if (rng.UniformFloat() < accept) { currentPos = proposalPos; currentL = proposalL; sampler.Accept(); ++acceptedMutations; } else { sampler.Reject(); } ++totalMutations; if (i % 100 == 0) progress.Update(); } film.MergeFilmTile(std::move(filmTile)); }, nChains); progress.Done(); } film.WriteImage(b / mutationsPerPixel); }