PowerLightDistribution::PowerLightDistribution(const Scene &scene) : distrib(ComputeLightPowerDistribution(scene)) {}
void MLTIntegrator::Render(const Scene &scene) { ProfilePhase p(Prof::IntegratorRender); std::unique_ptr<Distribution1D> lightDistr = ComputeLightPowerDistribution(scene); // Generate bootstrap samples and compute normalization constant $b$ int nBootstrapSamples = nBootstrap * (maxDepth + 1); std::vector<Float> bootstrapWeights(nBootstrapSamples, 0); if (scene.lights.size() > 0) { ProgressReporter progress(nBootstrap / 256, "Generating bootstrap paths"); std::vector<MemoryArena> bootstrapThreadArenas(MaxThreadIndex()); int chunkSize = Clamp(nBootstrap / 128, 1, 8192); ParallelFor([&](int i) { // Generate _i_th bootstrap sample MemoryArena &arena = bootstrapThreadArenas[threadIndex]; for (int depth = 0; depth <= maxDepth; ++depth) { int rngIndex = i * (maxDepth + 1) + depth; MLTSampler sampler(mutationsPerPixel, rngIndex, sigma, largeStepProbability, nSampleStreams); Point2f pRaster; bootstrapWeights[rngIndex] = L(scene, arena, lightDistr, sampler, depth, &pRaster).y(); arena.Reset(); } if ((i + 1 % 256) == 0) progress.Update(); }, nBootstrap, chunkSize); progress.Done(); } Distribution1D bootstrap(&bootstrapWeights[0], nBootstrapSamples); Float b = bootstrap.funcInt * (maxDepth + 1); // Run _nChains_ Markov chains in parallel Film &film = *camera->film; int64_t nTotalMutations = (int64_t)mutationsPerPixel * (int64_t)film.GetSampleBounds().Area(); if (scene.lights.size() > 0) { StatTimer timer(&renderingTime); const int progressFrequency = 32768; ProgressReporter progress(nTotalMutations / progressFrequency, "Rendering"); ParallelFor([&](int i) { int64_t nChainMutations = std::min((i + 1) * nTotalMutations / nChains, nTotalMutations) - i * nTotalMutations / nChains; // Follow {i}th Markov chain for _nChainMutations_ MemoryArena arena; // Select initial state from the set of bootstrap samples RNG rng(i); int bootstrapIndex = bootstrap.SampleDiscrete(rng.UniformFloat()); int depth = bootstrapIndex % (maxDepth + 1); // Initialize local variables for selected state MLTSampler sampler(mutationsPerPixel, bootstrapIndex, sigma, largeStepProbability, nSampleStreams); Point2f pCurrent; Spectrum LCurrent = L(scene, arena, lightDistr, sampler, depth, &pCurrent); // Run the Markov chain for _nChainMutations_ steps for (int64_t j = 0; j < nChainMutations; ++j) { sampler.StartIteration(); Point2f pProposed; Spectrum LProposed = L(scene, arena, lightDistr, sampler, depth, &pProposed); // Compute acceptance probability for proposed sample Float accept = std::min((Float)1, LProposed.y() / LCurrent.y()); // Splat both current and proposed samples to _film_ if (accept > 0) film.AddSplat(pProposed, LProposed * accept / LProposed.y()); film.AddSplat(pCurrent, LCurrent * (1 - accept) / LCurrent.y()); // Accept or reject the proposal if (rng.UniformFloat() < accept) { pCurrent = pProposed; LCurrent = LProposed; sampler.Accept(); ++acceptedMutations; } else sampler.Reject(); ++totalMutations; if ((i * nTotalMutations / nChains + j) % progressFrequency == 0) progress.Update(); arena.Reset(); } }, nChains); progress.Done(); } // Store final image computed with MLT camera->film->WriteImage(b / mutationsPerPixel); }
void BDPTIntegrator::Render(const Scene &scene) { ProfilePhase p(Prof::IntegratorRender); // Compute _lightDistr_ for sampling lights proportional to power std::unique_ptr<Distribution1D> lightDistr = ComputeLightPowerDistribution(scene); // Partition the image into tiles Film *film = camera->film; const Bounds2i sampleBounds = film->GetSampleBounds(); const Vector2i sampleExtent = sampleBounds.Diagonal(); const int tileSize = 16; const int nXTiles = (sampleExtent.x + tileSize - 1) / tileSize; const int nYTiles = (sampleExtent.y + tileSize - 1) / tileSize; ProgressReporter reporter(nXTiles * nYTiles, "Rendering"); // Allocate buffers for debug visualization const int bufferCount = (1 + maxDepth) * (6 + maxDepth) / 2; std::vector<std::unique_ptr<Film>> weightFilms(bufferCount); if (visualizeStrategies || visualizeWeights) { for (int depth = 0; depth <= maxDepth; ++depth) { for (int s = 0; s <= depth + 2; ++s) { int t = depth + 2 - s; if (t == 0 || (s == 1 && t == 1)) continue; std::string filename = StringPrintf("bdpt_d%02i_s%02i_t%02i.exr", depth, s, t); weightFilms[BufferIndex(s, t)] = std::unique_ptr<Film>(new Film( film->fullResolution, Bounds2f(Point2f(0, 0), Point2f(1, 1)), std::unique_ptr<Filter>(CreateBoxFilter(ParamSet())), film->diagonal * 1000, filename, 1.f)); } } } // Render and write the output image to disk if (scene.lights.size() > 0) { StatTimer timer(&renderingTime); ParallelFor2D([&](const Point2i tile) { // Render a single tile using BDPT MemoryArena arena; int seed = tile.y * nXTiles + tile.x; std::unique_ptr<Sampler> tileSampler = sampler->Clone(seed); int x0 = sampleBounds.pMin.x + tile.x * tileSize; int x1 = std::min(x0 + tileSize, sampleBounds.pMax.x); int y0 = sampleBounds.pMin.y + tile.y * tileSize; int y1 = std::min(y0 + tileSize, sampleBounds.pMax.y); Bounds2i tileBounds(Point2i(x0, y0), Point2i(x1, y1)); std::unique_ptr<FilmTile> filmTile = camera->film->GetFilmTile(tileBounds); for (Point2i pPixel : tileBounds) { tileSampler->StartPixel(pPixel); if (!InsideExclusive(pPixel, pixelBounds)) continue; do { // Generate a single sample using BDPT Point2f pFilm = (Point2f)pPixel + tileSampler->Get2D(); // Trace the camera and light subpaths Vertex *cameraVertices = arena.Alloc<Vertex>(maxDepth + 2); Vertex *lightVertices = arena.Alloc<Vertex>(maxDepth + 1); int nCamera = GenerateCameraSubpath( scene, *tileSampler, arena, maxDepth + 2, *camera, pFilm, cameraVertices); int nLight = GenerateLightSubpath( scene, *tileSampler, arena, maxDepth + 1, cameraVertices[0].time(), *lightDistr, lightVertices); // Execute all BDPT connection strategies Spectrum L(0.f); for (int t = 1; t <= nCamera; ++t) { for (int s = 0; s <= nLight; ++s) { int depth = t + s - 2; if ((s == 1 && t == 1) || depth < 0 || depth > maxDepth) continue; // Execute the $(s, t)$ connection strategy and // update _L_ Point2f pFilmNew = pFilm; Float misWeight = 0.f; Spectrum Lpath = ConnectBDPT( scene, lightVertices, cameraVertices, s, t, *lightDistr, *camera, *tileSampler, &pFilmNew, &misWeight); if (visualizeStrategies || visualizeWeights) { Spectrum value; if (visualizeStrategies) value = misWeight == 0 ? 0 : Lpath / misWeight; if (visualizeWeights) value = Lpath; weightFilms[BufferIndex(s, t)]->AddSplat( pFilmNew, value); } if (t != 1) L += Lpath; else film->AddSplat(pFilmNew, Lpath); } } filmTile->AddSample(pFilm, L); arena.Reset(); } while (tileSampler->StartNextSample()); } film->MergeFilmTile(std::move(filmTile)); reporter.Update(); }, Point2i(nXTiles, nYTiles)); reporter.Done(); } film->WriteImage(1.0f / sampler->samplesPerPixel); // Write buffers for debug visualization if (visualizeStrategies || visualizeWeights) { const Float invSampleCount = 1.0f / sampler->samplesPerPixel; for (size_t i = 0; i < weightFilms.size(); ++i) if (weightFilms[i]) weightFilms[i]->WriteImage(invSampleCount); } }