void DipoleSubsurfaceIntegrator::Preprocess(const Scene *scene, const Camera *camera, const Renderer *renderer) { if (scene->lights.size() == 0) return; vector<SurfacePoint> pts; // Get _SurfacePoint_s for translucent objects in scene if (filename != "") { // Initialize _SurfacePoint_s from file vector<float> fpts; if (ReadFloatFile(filename.c_str(), &fpts)) { if ((fpts.size() % 8) != 0) Error("Excess values (%d) in points file \"%s\"", int(fpts.size() % 8), filename.c_str()); for (u_int i = 0; i < fpts.size(); i += 8) pts.push_back(SurfacePoint(Point(fpts[i], fpts[i+1], fpts[i+2]), Normal(fpts[i+3], fpts[i+4], fpts[i+5]), fpts[i+6], fpts[i+7])); } } if (pts.size() == 0) { Point pCamera = camera->CameraToWorld(camera->shutterOpen, Point(0, 0, 0)); FindPoissonPointDistribution(pCamera, camera->shutterOpen, minSampleDist, scene, &pts); } // Compute irradiance values at sample points RNG rng; MemoryArena arena; PBRT_SUBSURFACE_STARTED_COMPUTING_IRRADIANCE_VALUES(); ProgressReporter progress(pts.size(), "Computing Irradiances"); for (uint32_t i = 0; i < pts.size(); ++i) { SurfacePoint &sp = pts[i]; Spectrum E(0.f); for (uint32_t j = 0; j < scene->lights.size(); ++j) { // Add irradiance from light at point const Light *light = scene->lights[j]; Spectrum Elight = 0.f; int nSamples = RoundUpPow2(light->nSamples); uint32_t scramble[2] = { rng.RandomUInt(), rng.RandomUInt() }; uint32_t compScramble = rng.RandomUInt(); for (int s = 0; s < nSamples; ++s) { float lpos[2]; Sample02(s, scramble, lpos); float lcomp = VanDerCorput(s, compScramble); LightSample ls(lpos[0], lpos[1], lcomp); Vector wi; float lightPdf; VisibilityTester visibility; Spectrum Li = light->Sample_L(sp.p, sp.rayEpsilon, ls, camera->shutterOpen, &wi, &lightPdf, &visibility); if (Dot(wi, sp.n) <= 0.) continue; if (Li.IsBlack() || lightPdf == 0.f) continue; Li *= visibility.Transmittance(scene, renderer, NULL, rng, arena); if (visibility.Unoccluded(scene)) Elight += Li * AbsDot(wi, sp.n) / lightPdf; } E += Elight / nSamples; } irradiancePoints.push_back(IrradiancePoint(sp, E)); PBRT_SUBSURFACE_COMPUTED_IRRADIANCE_AT_POINT(&sp, &E); arena.FreeAll(); progress.Update(); } progress.Done(); PBRT_SUBSURFACE_FINISHED_COMPUTING_IRRADIANCE_VALUES(); // Create octree of clustered irradiance samples octree = octreeArena.Alloc<SubsurfaceOctreeNode>(); for (uint32_t i = 0; i < irradiancePoints.size(); ++i) octreeBounds = Union(octreeBounds, irradiancePoints[i].p); for (uint32_t i = 0; i < irradiancePoints.size(); ++i) octree->Insert(octreeBounds, &irradiancePoints[i], octreeArena); octree->InitHierarchy(); }
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 *= AbsDot(Nl, ray.d) / (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 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 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; char filename[32]; snprintf(filename, sizeof(filename), "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); ParallelFor([&](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); 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); } }
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, camera->film, 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 PhotonShootingTask::Run() { // Declare local variables for _PhotonShootingTask_ MemoryArena arena; RNG rng(31 * taskNum); vector<Photon> localDirectPhotons, localIndirectPhotons, localCausticPhotons; vector<RadiancePhoton> localRadiancePhotons; u_int 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 u_int blockSize = 4096; for (u_int 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], &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; u_int nIntersections = 0; while (scene->Intersect(photonRay, &photonIsect)) { ++nIntersections; // Handle photon/surface intersection alpha *= renderer->Transmittance(scene, photonRay, NULL, arena, &rng); 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 (nIntersections == 1) { PBRT_PHOTON_MAP_DEPOSITED_DIRECT_PHOTON(&photonIsect.dg, &alpha, &wo); depositedPhoton = true; localDirectPhotons.push_back(photon); } else { // Deposit either caustic or indirect photon if (specularPath && !causticDone) { PBRT_PHOTON_MAP_DEPOSITED_CAUSTIC_PHOTON(&photonIsect.dg, &alpha, &wo); depositedPhoton = true; localCausticPhotons.push_back(photon); } else if (!specularPath && !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) { // Store data for radiance photon Normal n = photonIsect.dg.nn; n = Faceforward(n, -photonRay.d); localRadiancePhotons.push_back(RadiancePhoton(photonIsect.dg.p, n)); // Generate random samples for computing reflectance and transmittance const int sqrtRhoSamples = 4; float rhoRSamples1[2*sqrtRhoSamples*sqrtRhoSamples]; float rhoRSamples2[2*sqrtRhoSamples*sqrtRhoSamples]; StratifiedSample2D(rhoRSamples1, sqrtRhoSamples, sqrtRhoSamples, rng); StratifiedSample2D(rhoRSamples2, sqrtRhoSamples, sqrtRhoSamples, rng); float rhoTSamples1[2*sqrtRhoSamples*sqrtRhoSamples]; float rhoTSamples2[2*sqrtRhoSamples*sqrtRhoSamples]; StratifiedSample2D(rhoTSamples1, sqrtRhoSamples, sqrtRhoSamples, rng); StratifiedSample2D(rhoTSamples2, sqrtRhoSamples, sqrtRhoSamples, rng); Spectrum rho_r = photonBSDF->rho(sqrtRhoSamples * sqrtRhoSamples, rhoRSamples1, rhoRSamples2, BSDF_ALL_REFLECTION); localRpReflectances.push_back(rho_r); Spectrum rho_t = photonBSDF->rho(sqrtRhoSamples * sqrtRhoSamples, rhoTSamples1, rhoTSamples2, BSDF_ALL_TRANSMISSION); localRpTransmittances.push_back(rho_t); } } if ((int)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"); abortTasks = true; return; } progress.Update(localIndirectPhotons.size() + localCausticPhotons.size()); nshot += blockSize; // Merge direct photons into shared array nDirectPaths += blockSize; for (u_int i = 0; i < localDirectPhotons.size(); ++i) directPhotons.push_back(localDirectPhotons[i]); localDirectPhotons.erase(localDirectPhotons.begin(), localDirectPhotons.end()); // Merge indirect photons into shared array if (!indirectDone) { integrator->nIndirectPaths += blockSize; for (u_int i = 0; i < localIndirectPhotons.size(); ++i) indirectPhotons.push_back(localIndirectPhotons[i]); localIndirectPhotons.erase(localIndirectPhotons.begin(), localIndirectPhotons.end()); if (indirectPhotons.size() >= integrator->nIndirectPhotonsWanted) indirectDone = true; } // Merge caustic photons into shared array if (!causticDone) { integrator->nCausticPaths += blockSize; for (u_int i = 0; i < localCausticPhotons.size(); ++i) causticPhotons.push_back(localCausticPhotons[i]); localCausticPhotons.erase(localCausticPhotons.begin(), localCausticPhotons.end()); if (causticPhotons.size() >= integrator->nCausticPhotonsWanted) causticDone = true; } // Merge radiance photons and reflectances into shared array for (u_int i = 0; i < localRadiancePhotons.size(); ++i) radiancePhotons.push_back(localRadiancePhotons[i]); localRadiancePhotons.erase(localRadiancePhotons.begin(), localRadiancePhotons.end()); for (u_int i = 0; i < localRpReflectances.size(); ++i) rpReflectances.push_back(localRpReflectances[i]); localRpReflectances.erase(localRpReflectances.begin(), localRpReflectances.end()); for (u_int 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 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; Intersection isect; bool hitOnSphere = false; if (!scene->Intersect(ray, &isect)) { if (!sphere.Intersect(ray, &isect)) break; hitOnSphere = true; } DifferentialGeometry &hitGeometry = isect.dg; hitGeometry.nn = Faceforward(hitGeometry.nn, -ray.d); // Store candidate sample point at ray intersection if appropriate if (!hitOnSphere && ray.depth >= 3 && isect.GetBSSRDF(RayDifferential(ray), arena) != NULL) { float area = M_PI * (minSampleDist / 2.f) * (minSampleDist / 2.f); candidates.push_back(SurfacePoint(hitGeometry.p, hitGeometry.nn, area, isect.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, isect.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 BDPTIntegrator::Render(const Scene &scene) { // Compute _lightDistr_ for sampling lights proportional to power std::unique_ptr<Distribution1D> lightDistr(ComputeLightSamplingCDF(scene)); // Partition the image into buckets Film *film = camera->film; const Bounds2i sampleBounds = film->GetSampleBounds(); const Vector2i sampleExtent = sampleBounds.Diagonal(); const int bucketSize = 16; const int nXBuckets = (sampleExtent.x + bucketSize - 1) / bucketSize; const int nYBuckets = (sampleExtent.y + bucketSize - 1) / bucketSize; ProgressReporter reporter(nXBuckets * nYBuckets, "Rendering"); // Allocate buffers for debug visualization const int bufferCount = (1 + maxdepth) * (6 + maxdepth) / 2; std::vector<std::unique_ptr<Film>> films(bufferCount); if (visualize_strategies || visualize_weights) { 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; char filename[32]; snprintf(filename, sizeof(filename), "bdpt_d%02i_s%02i_t%02i.exr", depth, s, t); films[BufferIndex(s, t)] = std::unique_ptr<Film>(new Film( film->fullResolution, Bounds2f(Point2f(0, 0), Point2f(1, 1)), CreateBoxFilter(ParamSet()), film->diagonal * 1000, // XXX what does this parameter // mean? Why the multiplication? filename, 1.f, 2.2f)); } } } // Render and write the output image to disk { StatTimer timer(&renderingTime); ParallelFor([&](const Point2i bucket) { // Render a single bucket using BDPT MemoryArena arena; int seed = bucket.y * nXBuckets + bucket.x; std::unique_ptr<Sampler> bucketSampler = sampler->Clone(seed); int x0 = sampleBounds.pMin.x + bucket.x * bucketSize; int x1 = std::min(x0 + bucketSize, sampleBounds.pMax.x); int y0 = sampleBounds.pMin.y + bucket.y * bucketSize; int y1 = std::min(y0 + bucketSize, sampleBounds.pMax.y); Bounds2i bucketBounds(Point2i(x0, y0), Point2i(x1, y1)); std::unique_ptr<FilmTile> filmTile = camera->film->GetFilmTile(bucketBounds); for (Point2i pixel : bucketBounds) { bucketSampler->StartPixel(pixel); do { // Generate a single sample using BDPT Point2f rasterPos((Float)pixel.x, (Float)pixel.y); rasterPos += bucketSampler->Get2D(); // Trace the light and camera subpaths Vertex *cameraSubpath = (Vertex *)arena.Alloc<Vertex>(maxdepth + 2); Vertex *lightSubpath = (Vertex *)arena.Alloc<Vertex>(maxdepth + 1); int nCamera = GenerateCameraSubpath( scene, *bucketSampler, arena, maxdepth + 2, *camera, rasterPos, cameraSubpath); int nLight = GenerateLightSubpath( scene, *bucketSampler, arena, maxdepth + 1, cameraSubpath[0].GetTime(), *lightDistr, lightSubpath); // Execute all connection strategies Spectrum pixelWeight(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 Point2f finalRasterPos = rasterPos; Float misWeight = 0.f; Spectrum weight = ConnectBDPT( scene, lightSubpath, cameraSubpath, s, t, *lightDistr, *camera, *bucketSampler, &finalRasterPos, &misWeight); if (visualize_strategies || visualize_weights) { Spectrum value(1.0f); if (visualize_strategies) value *= weight; if (visualize_weights) value *= misWeight; films[BufferIndex(s, t)]->Splat(finalRasterPos, value); } if (t != 1) pixelWeight += weight * misWeight; else film->Splat(finalRasterPos, weight * misWeight); } } filmTile->AddSample(rasterPos, pixelWeight, 1.0f); arena.Reset(); } while (bucketSampler->StartNextSample()); } film->MergeFilmTile(std::move(filmTile)); reporter.Update(); }, Point2i(nXBuckets, nYBuckets)); reporter.Done(); } film->WriteImage(1.0f / sampler->samplesPerPixel); // Write buffers for debug visualization if (visualize_strategies || visualize_weights) { const Float invSampleCount = 1.0f / sampler->samplesPerPixel; for (size_t i = 0; i < films.size(); ++i) { if (films[i]) films[i]->WriteImage(invSampleCount); } } }
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; } } }