void SamplerRenderer::Render(const Scene *scene) { PBRT_FINISHED_PARSING(); // Allow integrators to do pre-processing for the scene PBRT_STARTED_PREPROCESSING(); surfaceIntegrator->Preprocess(scene, camera, this); volumeIntegrator->Preprocess(scene, camera, this); PBRT_FINISHED_PREPROCESSING(); PBRT_STARTED_RENDERING(); // Allocate and initialize _sample_ Sample *sample = new Sample(sampler, surfaceIntegrator, volumeIntegrator, scene); // Create and launch _SamplerRendererTask_s for rendering image // Compute number of _SamplerRendererTask_s to create for rendering int nPixels = camera->film->xResolution * camera->film->yResolution; int nTasks = max(32 * NumSystemCores(), nPixels / (16*16)); nTasks = RoundUpPow2(nTasks); ProgressReporter reporter(nTasks, "Rendering"); vector<Task *> renderTasks; for (int i = 0; i < nTasks; ++i) renderTasks.push_back(new SamplerRendererTask(scene, this, camera, sampler, reporter, sample, nTasks-1-i, nTasks)); EnqueueTasks(renderTasks); WaitForAllTasks(); for (uint32_t i = 0; i < renderTasks.size(); ++i) delete renderTasks[i]; reporter.Done(); PBRT_FINISHED_RENDERING(); // Clean up after rendering and store final image delete sample; camera->film->WriteImage(); }
void IrradianceCacheIntegrator::Preprocess(const Scene *scene, const Camera *camera, const Renderer *renderer) { BBox wb = scene->WorldBound(); Vector delta = .01f * (wb.pMax - wb.pMin); wb.pMin -= delta; wb.pMax += delta; octree = new Octree<IrradianceSample *>(wb); // Prime irradiance cache minWeight *= 1.5f; int xstart, xend, ystart, yend; camera->film->GetSampleExtent(&xstart, &xend, &ystart, ¥d); HaltonSampler sampler(xstart, xend, ystart, yend, 1, camera->shutterOpen, camera->shutterClose); Sample *sample = new Sample(&sampler, this, NULL, scene); const int nTasks = 64; ProgressReporter progress(nTasks, "Priming irradiance cache"); vector<Task *> tasks; for (int i = 0; i < nTasks; ++i) tasks.push_back(new IrradiancePrimeTask(scene, renderer, camera, &sampler, sample, this, progress, i, nTasks)); EnqueueTasks(tasks); WaitForAllTasks(); for (uint32_t i = 0; i < tasks.size(); ++i) delete tasks[i]; progress.Done(); delete sample; minWeight /= 1.5f; }
void SurfacePointsRenderer::Render(const Scene &scene) { // Declare shared variables for Poisson point generation BBox octBounds = scene.WorldBound(); octBounds.Expand(.001f * powf(octBounds.Volume(), 1.f/3.f)); Octree<SurfacePoint> pointOctree(octBounds); // Create scene bounding sphere to catch rays that leave the scene Point sceneCenter; float sceneRadius; scene.WorldBound().BoundingSphere(&sceneCenter, &sceneRadius); Transform ObjectToWorld(Translate(sceneCenter - Point(0,0,0))); Transform WorldToObject(Inverse(ObjectToWorld)); Reference<Shape> sph = new Sphere(&ObjectToWorld, &WorldToObject, true, sceneRadius, -sceneRadius, sceneRadius, 360.f); //Reference<Material> nullMaterial = Reference<Material>(NULL); Material nullMaterial; GeometricPrimitive sphere(sph, nullMaterial, NULL); int maxFails = 2000, repeatedFails = 0, maxRepeatedFails = 0; if (PbrtOptions.quickRender) maxFails = max(10, maxFails / 10); int totalPathsTraced = 0, totalRaysTraced = 0, numPointsAdded = 0; ProgressReporter prog(maxFails, "Depositing samples"); // Launch tasks to trace rays to find Poisson points PBRT_SUBSURFACE_STARTED_RAYS_FOR_POINTS(); vector<Task *> tasks; RWMutex *mutex = RWMutex::Create(); int nTasks = NumSystemCores(); for (int i = 0; i < nTasks; ++i) tasks.push_back(new SurfacePointTask(scene, pCamera, time, i, minDist, maxFails, *mutex, repeatedFails, maxRepeatedFails, totalPathsTraced, totalRaysTraced, numPointsAdded, sphere, pointOctree, points, prog)); EnqueueTasks(tasks); WaitForAllTasks(); for (uint32_t i = 0; i < tasks.size(); ++i) delete tasks[i]; RWMutex::Destroy(mutex); prog.Done(); PBRT_SUBSURFACE_FINISHED_RAYS_FOR_POINTS(totalRaysTraced, numPointsAdded); if (filename != "") { // Write surface points to file FILE *f = fopen(filename.c_str(), "w"); if (!f) { Error("Unable to open output file \"%s\" (%s)", filename.c_str(), strerror(errno)); return; } fprintf(f, "# points generated by SurfacePointsRenderer\n"); fprintf(f, "# position (x,y,z), normal (x,y,z), area, rayEpsilon\n"); for (u_int i = 0; i < points.size(); ++i) { const SurfacePoint &sp = points[i]; fprintf(f, "%g %g %g %g %g %g %g %g\n", sp.p.x, sp.p.y, sp.p.z, sp.n.x, sp.n.y, sp.n.z, sp.area, sp.rayEpsilon); } fclose(f); } }
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 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 CreateRadianceProbes::Render(const Scene *scene) { // Compute scene bounds and initialize probe integrators if (bbox.pMin.x > bbox.pMax.x) bbox = scene->WorldBound(); surfaceIntegrator->Preprocess(scene, camera, this); volumeIntegrator->Preprocess(scene, camera, this); Sample *origSample = new Sample(NULL, surfaceIntegrator, volumeIntegrator, scene); // Compute sampling rate in each dimension Vector delta = bbox.pMax - bbox.pMin; int nProbes[3]; for (int i = 0; i < 3; ++i) nProbes[i] = max(1, Ceil2Int(delta[i] / probeSpacing)); // Allocate SH coefficient vector pointers for sample points int count = nProbes[0] * nProbes[1] * nProbes[2]; Spectrum **c_in = new Spectrum *[count]; for (int i = 0; i < count; ++i) c_in[i] = new Spectrum[SHTerms(lmax)]; // Compute random points on surfaces of scene // Create scene bounding sphere to catch rays that leave the scene Point sceneCenter; float sceneRadius; scene->WorldBound().BoundingSphere(&sceneCenter, &sceneRadius); Transform ObjectToWorld(Translate(sceneCenter - Point(0,0,0))); Transform WorldToObject(Inverse(ObjectToWorld)); Reference<Shape> sph = new Sphere(&ObjectToWorld, &WorldToObject, true, sceneRadius, -sceneRadius, sceneRadius, 360.f); Reference<Material> nullMaterial = Reference<Material>(NULL); GeometricPrimitive sphere(sph, nullMaterial, NULL); vector<Point> surfacePoints; uint32_t nPoints = 32768, maxDepth = 32; surfacePoints.reserve(nPoints + maxDepth); Point pCamera = camera->CameraToWorld(camera->shutterOpen, Point(0, 0, 0)); surfacePoints.push_back(pCamera); RNG rng; while (surfacePoints.size() < nPoints) { // Generate random path from camera and deposit surface points Point pray = pCamera; Vector dir = UniformSampleSphere(rng.RandomFloat(), rng.RandomFloat()); float rayEpsilon = 0.f; for (uint32_t i = 0; i < maxDepth; ++i) { Ray ray(pray, dir, rayEpsilon, INFINITY, time); Intersection isect; if (!scene->Intersect(ray, &isect) && !sphere.Intersect(ray, &isect)) break; surfacePoints.push_back(ray(ray.maxt)); DifferentialGeometry &hitGeometry = isect.dg; pray = isect.dg.p; rayEpsilon = isect.rayEpsilon; hitGeometry.nn = Faceforward(hitGeometry.nn, -ray.d); dir = UniformSampleSphere(rng.RandomFloat(), rng.RandomFloat()); dir = Faceforward(dir, hitGeometry.nn); } } // Launch tasks to compute radiance probes at sample points vector<Task *> tasks; ProgressReporter prog(count, "Radiance Probes"); for (int i = 0; i < count; ++i) tasks.push_back(new CreateRadProbeTask(i, nProbes, time, bbox, lmax, includeDirectInProbes, includeIndirectInProbes, nIndirSamples, prog, origSample, surfacePoints, scene, this, c_in[i])); EnqueueTasks(tasks); WaitForAllTasks(); for (uint32_t i = 0; i < tasks.size(); ++i) delete tasks[i]; prog.Done(); // Write radiance probe coefficients to file FILE *f = fopen(filename.c_str(), "w"); if (f) { if (fprintf(f, "%d %d %d\n", lmax, includeDirectInProbes?1:0, includeIndirectInProbes?1:0) < 0 || fprintf(f, "%d %d %d\n", nProbes[0], nProbes[1], nProbes[2]) < 0 || fprintf(f, "%f %f %f %f %f %f\n", bbox.pMin.x, bbox.pMin.y, bbox.pMin.z, bbox.pMax.x, bbox.pMax.y, bbox.pMax.z) < 0) { Error("Error writing radiance file \"%s\" (%s)", filename.c_str(), strerror(errno)); exit(1); } for (int i = 0; i < nProbes[0] * nProbes[1] * nProbes[2]; ++i) { for (int j = 0; j < SHTerms(lmax); ++j) { fprintf(f, " "); if (c_in[i][j].Write(f) == false) { Error("Error writing radiance file \"%s\" (%s)", filename.c_str(), strerror(errno)); exit(1); } fprintf(f, "\n"); } fprintf(f, "\n"); } fclose(f); } for (int i = 0; i < nProbes[0] * nProbes[1] * nProbes[2]; ++i) delete[] c_in[i]; delete[] c_in; delete origSample; }
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(); }
int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "usage: mbrast [grid_file.out]\n"); return 1; } // Read grids from grid dump file std::vector<ShadedGrid> grids; if (!ReadGrids(argv[1], &grids)) return 1; InitSamples(); // Extract the name of the scene in case a full pathname was provided // on the command line char *sceneName = rindex(argv[1], '/'); if (sceneName != NULL) ++sceneName; else sceneName = argv[1]; // Remove trailing ".grids" from scene name char *end = rindex(sceneName, '.'); assert(end); *end = '\0'; printf("Rendering scene \"%s\"\n", sceneName); // Change this call to switch to creating an instance of your 3D // rasterizer once you start implementing it. #if MBRAST_MODE == NAIVE_RAST_2D || MBRAST_MODE == SMART_RAST_2D Rasterizer *rast = Create2DRasterizer(); #elif MBRAST_MODE == BASIC_RAST_3D || MBRAST_MODE == INTER_RAST_3D Rasterizer *rast = Create3DRasterizer(); #elif MBRAST_MODE == BASIC_RAST_5D Rasterizer *rast = Create5DRasterizer(); #endif // Initialize task system for multithreading if the rasterizer // indicates that it's thread safe. bool useTasks = rast->IsThreadSafe(); int nThreads = NumSystemCores(); assert(nThreads < MAX_CORES); if (useTasks) { printf("Using %d threads!\n", nThreads); TasksInit(); } // Render the scene multiple times, with each of the pixelSamples // sample counts. The sample counts must all be powers of two. // The bucketSize array, which must be the same size as // pixelSamples[], gives the length of the side of the bucket we'll // decompose the image into for the corresponding sample count. int pixelSamples[] = { 1, 4, 32 }; int bucketSize[] = { 64, 32, 8 }; assert(sizeof(pixelSamples) == sizeof(bucketSize)); int samplesSize = sizeof(pixelSamples) / sizeof(pixelSamples[0]); int failures = 0; for (int samplesIndex = 0; samplesIndex < samplesSize; ++samplesIndex) { assert(pixelSamples[samplesIndex] <= MAX_SAMPLES_PER_PIXEL); int nPixelSamples = pixelSamples[samplesIndex]; int bucketExtent = bucketSize[samplesIndex]; printf("Rendering with %d samples per pixel\n", nPixelSamples); // 720p resolution. (This can't be easily changed, as it's baked // into the xy coordinates of the micropolygons. So changing this // here would require a corresponding adjustment to those // coordinate values when the grids are read in....) int xRes = 1280, yRes = 720; // Allow the rasterizer to preprocess the grids ResetAndStartTimer(); rast->PreprocessGrids(grids, bucketExtent, xRes, yRes); double preprocessCycles = GetElapsedMCycles() / 1024.; printf(" Spent %.3f billion cycles on grid preprocessing.\n", preprocessCycles); // Allocate final output image as well as one Bucket structure for // each rasterization task thread we're running; this way the // rasterizer can update the framebuffer in its Bucket directly // without needing to coordinate with any other rasterizer threads. uint8_t *image = new uint8_t[xRes * yRes * 3]; Bucket *buckets[MAX_CORES]; int nBuckets = useTasks ? nThreads : 1; for (int i = 0; i < nBuckets; ++i) buckets[i] = new Bucket(bucketExtent, nPixelSamples); // Render with intervals set from 1 to the number of pixel samples // in multiples of two. for (int logIntervals = 0; (1 << logIntervals) <= nPixelSamples; ++logIntervals) { int nIntervals = (1 << logIntervals); printf(" Rendering with %d intervals: ", nIntervals); fflush(stdout); if (useTasks) { // Create a RastTask instance for each of the buckets of // the image; do this outside of the timer so as not to // penalize for the unnecessary dynamic allocation overhead // in the implementation here. std::vector<Task *> rastTasks; for (int y = 0; y < yRes; y += bucketExtent) { for (int x = 0; x < xRes; x += bucketExtent) { int x1 = std::min(x + bucketExtent, xRes); int y1 = std::min(y + bucketExtent, yRes); RastTask *rt = new RastTask(rast, &buckets[0], x, y, x1, y1, nIntervals, grids, image, xRes, yRes); rastTasks.push_back(rt); } } ResetAndStartTimer(); EnqueueTasks(rastTasks); WaitForAllTasks(); } else { // If not using tasks, just loop over all of the buckets // and rasterize. ResetAndStartTimer(); for (int y = 0; y < yRes; y += bucketExtent) { for (int x = 0; x < xRes; x += bucketExtent) { buckets[0]->Start(x, y, std::min(x + bucketExtent, xRes), std::min(y + bucketExtent, yRes)); rast->Rasterize(grids, nIntervals, buckets[0]); buckets[0]->Resolve(image, xRes, yRes); } } } // Report total time for rasterization double rastGCycles = GetElapsedMCycles() / 1024.; printf(" %.3f billion cycles\n", rastGCycles); // Write the image char outName[256]; sprintf(outName, "%s_int%d_samp%d.ppm", sceneName, nIntervals, nPixelSamples); WritePPM(image, xRes, yRes, outName); printf(" Wrote output image \"%s\"\n", outName); // Compare to the "golden" image for correctness char goldenName[256]; sprintf(goldenName, "golden/%s_%d.ppm", sceneName, nPixelSamples); if (!CompareToGolden(image, xRes, yRes, goldenName, outName)) ++failures; } printf("\n"); for (int i = 0; i < nBuckets; ++i) delete buckets[i]; delete[] image; } if (useTasks) TasksCleanup(); delete rast; return failures; }
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(); }