void MLTTask::Run() { PBRT_MLT_STARTED_MLT_TASK(this); // Declare basic _MLTTask_ variables and prepare for sampling PBRT_MLT_STARTED_TASK_INIT(); uint32_t nPixels = (x1-x0) * (y1-y0); uint32_t nPixelSamples = renderer->nPixelSamples; uint32_t largeStepRate = nPixelSamples / renderer->largeStepsPerPixel; Assert(largeStepRate > 1); uint64_t nTaskSamples = uint64_t(nPixels) * uint64_t(largeStepRate); uint32_t consecutiveRejects = 0; uint32_t progressCounter = progressUpdateFrequency; // Declare variables for storing and computing MLT samples MemoryArena arena; RNG rng(taskNum); vector<PathVertex> cameraPath(renderer->maxDepth, PathVertex()); vector<PathVertex> lightPath(renderer->maxDepth, PathVertex()); vector<MLTSample> samples(2, MLTSample(renderer->maxDepth)); Spectrum L[2]; float I[2]; uint32_t current = 0, proposed = 1; // Compute _L[current]_ for initial sample samples[current] = initialSample; L[current] = renderer->PathL(initialSample, scene, arena, camera, lightDistribution, &cameraPath[0], &lightPath[0], rng); I[current] = ::I(L[current]); arena.FreeAll(); // Compute randomly permuted table of pixel indices for large steps uint32_t pixelNumOffset = 0; vector<int> largeStepPixelNum; largeStepPixelNum.reserve(nPixels); for (uint32_t i = 0; i < nPixels; ++i) largeStepPixelNum.push_back(i); Shuffle(&largeStepPixelNum[0], nPixels, 1, rng); PBRT_MLT_FINISHED_TASK_INIT(); for (uint64_t s = 0; s < nTaskSamples; ++s) { // Compute proposed mutation to current sample PBRT_MLT_STARTED_MUTATION(); samples[proposed] = samples[current]; bool largeStep = ((s % largeStepRate) == 0); if (largeStep) { int x = x0 + largeStepPixelNum[pixelNumOffset] % (x1 - x0); int y = y0 + largeStepPixelNum[pixelNumOffset] / (x1 - x0); LargeStep(rng, &samples[proposed], renderer->maxDepth, x + dx, y + dy, t0, t1, renderer->bidirectional); ++pixelNumOffset; } else SmallStep(rng, &samples[proposed], renderer->maxDepth, x0, x1, y0, y1, t0, t1, renderer->bidirectional); PBRT_MLT_FINISHED_MUTATION(); // Compute contribution of proposed sample L[proposed] = renderer->PathL(samples[proposed], scene, arena, camera, lightDistribution, &cameraPath[0], &lightPath[0], rng); I[proposed] = ::I(L[proposed]); arena.FreeAll(); // Compute acceptance probability for proposed sample float a = min(1.f, I[proposed] / I[current]); // Splat current and proposed samples to _Film_ PBRT_MLT_STARTED_SAMPLE_SPLAT(); if (I[current] > 0.f) { if (!isinf(1.f / I[current])) { Spectrum contrib = (b / nPixelSamples) * L[current] / I[current]; camera->film->Splat(samples[current].cameraSample, (1.f - a) * contrib); } } if (I[proposed] > 0.f) { if (!isinf(1.f / I[proposed])) { Spectrum contrib = (b / nPixelSamples) * L[proposed] / I[proposed]; camera->film->Splat(samples[proposed].cameraSample, a * contrib); } } PBRT_MLT_FINISHED_SAMPLE_SPLAT(); // Randomly accept proposed path mutation (or not) if (consecutiveRejects >= renderer->maxConsecutiveRejects || rng.RandomFloat() < a) { PBRT_MLT_ACCEPTED_MUTATION(a, &samples[current], &samples[proposed]); current ^= 1; proposed ^= 1; consecutiveRejects = 0; } else { PBRT_MLT_REJECTED_MUTATION(a, &samples[current], &samples[proposed]); ++consecutiveRejects; } if (--progressCounter == 0) { progress.Update(); progressCounter = progressUpdateFrequency; } } Assert(pixelNumOffset == nPixels); // Update display for recently computed Metropolis samples PBRT_MLT_STARTED_DISPLAY_UPDATE(); int ntf = AtomicAdd(&renderer->nTasksFinished, 1); int64_t totalSamples = int64_t(nPixels) * int64_t(nPixelSamples); float splatScale = float(double(totalSamples) / double(ntf * nTaskSamples)); camera->film->UpdateDisplay(x0, y0, x1, y1, splatScale); if ((taskNum % 8) == 0) { MutexLock lock(*filmMutex); camera->film->WriteImage(splatScale); } PBRT_MLT_FINISHED_DISPLAY_UPDATE(); PBRT_MLT_FINISHED_MLT_TASK(this); }
void MLTTask::Run() { PBRT_MLT_STARTED_MLT_TASK(this); // Declare basic _MLTTask_ variables and prepare for sampling RNG rng(taskNum); MemoryArena arena; vector<MLTSample> mltSamples(2, MLTSample(maxDepth)); Spectrum sampleLs[2]; uint32_t currentSample = 0, proposedSample = 1; mltSamples[currentSample] = initialSample; sampleLs[currentSample] = L(scene, renderer, camera, arena, rng, maxDepth, ignoreDirect, mltSamples[currentSample]); int consecutiveRejects = 0; for (int sampleNum = 0; sampleNum < nSamples; ++sampleNum) { // Compute proposed mutation to current sample bool largeStep = rng.RandomFloat() < largeStepProbability; mltSamples[proposedSample] = mltSamples[currentSample]; if (largeStep) LargeStep(rng, &mltSamples[proposedSample], maxDepth, x0, x1, y0, y1, t0, t1); else SmallStep(rng, &mltSamples[proposedSample], maxDepth, x0, x1, y0, y1, t0, t1); // Compute contribution of proposed sample and acceptance probability sampleLs[proposedSample] = L(scene, renderer, camera, arena, rng, maxDepth, ignoreDirect, mltSamples[proposedSample]); float currentI = I(sampleLs[currentSample], mltSamples[currentSample]); float proposedI = I(sampleLs[proposedSample], mltSamples[proposedSample]); float a = min(1.f, proposedI / currentI); float currentWeight = (1.f - a) / (currentI / b + largeStepProbability) * float(nPixels) / float(totalSamples); float proposedWeight = (a + (largeStep ? 1.f : 0.f)) / (proposedI / b + largeStepProbability) * float(nPixels) / float(totalSamples); // Splat current and proposed samples to _Film_ if (currentWeight > 0.f && currentI > 0.f) camera->film->Splat(mltSamples[currentSample].cameraSample, sampleLs[currentSample] * currentWeight); if (proposedWeight > 0.f && proposedI > 0.f) camera->film->Splat(mltSamples[proposedSample].cameraSample, sampleLs[proposedSample] * proposedWeight); // Randomly accept proposed path mutation (or not) if (consecutiveRejects >= maxConsecutiveRejects || rng.RandomFloat() < a) { PBRT_MLT_ACCEPTED_MUTATION(a, &mltSamples[currentSample], &mltSamples[proposedSample]); currentSample ^= 1; proposedSample ^= 1; consecutiveRejects = 0; } else { PBRT_MLT_REJECTED_MUTATION(a, &mltSamples[currentSample], &mltSamples[proposedSample]); ++consecutiveRejects; } arena.FreeAll(); } // Update display for recently computed Metropolis samples float nf = AtomicAdd(nSamplesFinished, nSamples); float splatScale = float(totalSamples)/nf; camera->film->UpdateDisplay(x0, y0, x1, y1, splatScale); if ((taskNum % 32) == 0) { MutexLock lock(*filmMutex); camera->film->WriteImage(splatScale); } progress.Update(); PBRT_MLT_FINISHED_MLT_TASK(this); }
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 MetropolisRenderer::Render(const Scene *scene) { int x0, x1, y0, y1; camera->film->GetPixelExtent(&x0, &x1, &y0, &y1); int nPixels = (x1-x0) * (y1-y0); float t0 = camera->shutterOpen; float t1 = camera->shutterClose; if (doDirectSeparately) { // Compute direct lighting before Metropolis light transport LDSampler sampler(x0, x1, y0, y1, directPixelSamples, 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, &sampler, directProgress, sample, 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(); } // Take initial set of samples to compute $b$ RNG rng(0); MemoryArena arena; vector<float> bootstrapSamples; float sumContrib = 0.f; bootstrapSamples.reserve(nBootstrap); MLTSample sample(maxDepth); for (int i = 0; i < nBootstrap; ++i) { // Compute contribution for random sample for MLT bootstrapping LargeStep(rng, &sample, maxDepth, x0, x1, y0, y1, t0, t1); float contrib = I(L(scene, this, camera, arena, rng, maxDepth, doDirectSeparately, sample), sample); sumContrib += contrib; bootstrapSamples.push_back(contrib); arena.FreeAll(); } float b = sumContrib / nBootstrap; // Select initial sample from bootstrap samples rng.Seed(0); float contribOffset = rng.RandomFloat() * sumContrib; sumContrib = 0.f; MLTSample initialSample(maxDepth); for (int i = 0; i < nBootstrap; ++i) { LargeStep(rng, &initialSample, maxDepth, x0, x1, y0, y1, t0, t1); sumContrib += bootstrapSamples[i]; if (contribOffset < sumContrib) break; } // Launch tasks to generate Metropolis samples if (scene->lights.size() > 0) { int nTasks = int(nSamples / 50000); nTasks = max(nTasks, 32 * NumSystemCores()); nTasks = min(nTasks, 32768); nSamples = (nSamples / nTasks) * nTasks; ProgressReporter progress(nTasks, "Metropolis"); vector<Task *> tasks; Mutex *filmMutex = Mutex::Create(); for (int i = 0; i < nTasks; ++i) tasks.push_back(new MLTTask(progress, i, int(nSamples/nTasks), nSamples, nPixels, x0, x1, y0, y1, t0, t1, b, largeStepProbability, initialSample, doDirectSeparately, maxConsecutiveRejects, maxDepth, scene, camera, this, &nSamplesFinished, filmMutex)); EnqueueTasks(tasks); WaitForAllTasks(); for (uint32_t i = 0; i < tasks.size(); ++i) delete tasks[i]; progress.Done(); } camera->film->WriteImage(); }