UseRadianceProbes::UseRadianceProbes(const string &filename) { lightSampleOffsets = NULL; bsdfSampleOffsets = NULL; // Read precomputed radiance probe values from file FILE *f = fopen(filename.c_str(), "r"); if (f) { if (fscanf(f, "%d %d %d", &lmax, &includeDirectInProbes, &includeIndirectInProbes) != 3 || fscanf(f, "%d %d %d", &nProbes[0], &nProbes[1], &nProbes[2]) != 3 || fscanf(f, "%f %f %f %f %f %f", &bbox.pMin.x, &bbox.pMin.y, &bbox.pMin.z, &bbox.pMax.x, &bbox.pMax.y, &bbox.pMax.z) != 6) { Error("Error reading data from radiance probe file \"%s\"", filename.c_str()); exit(1); } c_in = new Spectrum[SHTerms(lmax) * nProbes[0] * nProbes[1] * nProbes[2]]; int offset = 0; for (int i = 0; i < nProbes[0] * nProbes[1] * nProbes[2]; ++i) { for (int j = 0; j < SHTerms(lmax); ++j) if (!c_in[offset++].Read(f)) { Error("Error reading data from radiance probe file \"%s\"", filename.c_str()); exit(1); } } fclose(f); } else Error("Unable to read saved radiance volume values from file \"%s\"", filename.c_str()); }
void Light::SHProject(const PbrtPoint &p, float pEpsilon, int lmax, const Scene *scene, bool computeLightVisibility, float time, RNG &rng, Spectrum *coeffs) const { for (int i = 0; i < SHTerms(lmax); ++i) coeffs[i] = 0.f; uint32_t ns = RoundUpPow2(nSamples); uint32_t scramble1D = rng.RandomUInt(); uint32_t scramble2D[2] = { rng.RandomUInt(), rng.RandomUInt() }; float *Ylm = ALLOCA(float, SHTerms(lmax)); for (uint32_t i = 0; i < ns; ++i) { // Compute incident radiance sample from _light_, update SH _coeffs_ float u[2], pdf; Sample02(i, scramble2D, u); LightSample lightSample(u[0], u[1], VanDerCorput(i, scramble1D)); Vector wi; VisibilityTester vis; Spectrum Li = Sample_L(p, pEpsilon, lightSample, time, &wi, &pdf, &vis); if (!Li.IsBlack() && pdf > 0.f && (!computeLightVisibility || vis.Unoccluded(scene))) { // Add light sample contribution to MC estimate of SH coefficients SHEvaluate(wi, lmax, Ylm); for (int j = 0; j < SHTerms(lmax); ++j) coeffs[j] += Li * Ylm[j] / (pdf * ns); } } }
Spectrum DiffusePRTIntegrator::Li(const Scene *scene, const Renderer *, const RayDifferential &ray, const Intersection &isect, const Sample *sample, MemoryArena &arena) const { Spectrum L = 0.f; Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; // Compute reflected radiance using diffuse PRT // Project diffuse transfer function at point to SH Spectrum *c_transfer = arena.Alloc<Spectrum>(SHTerms(lmax)); SHComputeDiffuseTransfer(p, Faceforward(n, wo), isect.rayEpsilon, scene, *sample->rng, nSamples, lmax, c_transfer); // Compute integral of product of incident radiance and transfer function Spectrum LT = 0.f; for (int i = 0; i < SHTerms(lmax); ++i) LT += c_in[i] * c_transfer[i]; // Compute reflectance at point for diffuse transfer const int sqrtRhoSamples = 6; float rhoRSamples[2*sqrtRhoSamples*sqrtRhoSamples]; StratifiedSample2D(rhoRSamples, sqrtRhoSamples, sqrtRhoSamples, *sample->rng); Spectrum Kd = bsdf->rho(wo, sqrtRhoSamples*sqrtRhoSamples, rhoRSamples, BSDF_ALL_REFLECTION) * INV_PI; return L + Kd * LT.Clamp(); }
void GlossyPRTIntegrator::Preprocess(const Scene *scene, const Camera *camera, const Renderer *renderer) { // Project direct lighting into SH for _GlossyPRTIntegrator_ BBox bbox = scene->WorldBound(); Point p = .5f * bbox.pMin + .5f * bbox.pMax; RNG rng; MemoryArena arena; c_in = new Spectrum[SHTerms(lmax)]; SHProjectIncidentDirectRadiance(p, 0.f, camera->shutterOpen, arena, scene, false, lmax, rng, c_in); // Compute glossy BSDF matrix for PRT B = new Spectrum[SHTerms(lmax)*SHTerms(lmax)]; SHComputeBSDFMatrix(Kd, Ks, roughness, rng, 1024, lmax, B); }
void PointLight::SHProject(const pbrt::Point &p, float pEpsilon, int lmax, const Scene *scene, bool computeLightVisibility, float time, RNG &rng, Spectrum *coeffs) const { for (int i = 0; i < SHTerms(lmax); ++i) coeffs[i] = 0.f; if (computeLightVisibility && scene->IntersectP(Ray(p, Normalize(lightPos - p), pEpsilon, Distance(lightPos, p), time))) return; // Project point light source to SH float *Ylm = ALLOCA(float, SHTerms(lmax)); Vector wi = Normalize(lightPos - p); SHEvaluate(wi, lmax, Ylm); Spectrum Li = Intensity / DistanceSquared(lightPos, p); for (int i = 0; i < SHTerms(lmax); ++i) coeffs[i] = Li * Ylm[i]; }
Spectrum UseRadianceProbes::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Intersection &isect, const Sample *sample, RNG &rng, MemoryArena &arena, int wavelength) const { Spectrum L(0.); Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena, wavelength); const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; // Compute reflection for radiance probes integrator if (!includeDirectInProbes) L += UniformSampleAllLights(scene, renderer, arena, p, n, wo, isect.rayEpsilon, ray.time, bsdf, sample, rng, lightSampleOffsets, bsdfSampleOffsets); // Compute reflected lighting using radiance probes // Compute probe coordinates and offsets for lookup point Vector offset = bbox.Offset(p); float voxx = (offset.x * nProbes[0]) - 0.5f; float voxy = (offset.y * nProbes[1]) - 0.5f; float voxz = (offset.z * nProbes[2]) - 0.5f; int vx = Floor2Int(voxx), vy = Floor2Int(voxy), vz = Floor2Int(voxz); float dx = voxx - vx, dy = voxy - vy, dz = voxz - vz; // Get radiance probe coefficients around lookup point const Spectrum *b000 = c_inXYZ(lmax, vx, vy, vz); const Spectrum *b100 = c_inXYZ(lmax, vx+1, vy, vz); const Spectrum *b010 = c_inXYZ(lmax, vx, vy+1, vz); const Spectrum *b110 = c_inXYZ(lmax, vx+1, vy+1, vz); const Spectrum *b001 = c_inXYZ(lmax, vx, vy, vz+1); const Spectrum *b101 = c_inXYZ(lmax, vx+1, vy, vz+1); const Spectrum *b011 = c_inXYZ(lmax, vx, vy+1, vz+1); const Spectrum *b111 = c_inXYZ(lmax, vx+1, vy+1, vz+1); // Compute incident radiance from radiance probe coefficients Spectrum *c_inp = arena.Alloc<Spectrum>(SHTerms(lmax)); for (int i = 0; i < SHTerms(lmax); ++i) { // Do trilinear interpolation to compute SH coefficients at point Spectrum c00 = Lerp(dx, b000[i], b100[i]); Spectrum c10 = Lerp(dx, b010[i], b110[i]); Spectrum c01 = Lerp(dx, b001[i], b101[i]); Spectrum c11 = Lerp(dx, b011[i], b111[i]); Spectrum c0 = Lerp(dy, c00, c10); Spectrum c1 = Lerp(dy, c01, c11); c_inp[i] = Lerp(dz, c0, c1); } // Convolve incident radiance to compute irradiance function Spectrum *c_E = arena.Alloc<Spectrum>(SHTerms(lmax)); SHConvolveCosTheta(lmax, c_inp, c_E); // Evaluate irradiance function and accumulate reflection Spectrum rho = bsdf->rho(wo, rng, BSDF_ALL_REFLECTION); float *Ylm = ALLOCA(float, SHTerms(lmax)); SHEvaluate(Vector(Faceforward(n, wo)), lmax, Ylm); Spectrum E = 0.f; for (int i = 0; i < SHTerms(lmax); ++i) E += c_E[i] * Ylm[i]; L += rho * INV_PI * E.Clamp(); return L; }
// DiffusePRTIntegrator Method Definitions DiffusePRTIntegrator::DiffusePRTIntegrator(int lm, int ns) { lmax = lm; nSamples = RoundUpPow2(ns); c_in = new Spectrum[SHTerms(lmax)]; }
void CreateRadProbeTask::Run() { // Compute region in which to compute incident radiance probes int sx = pointNum % nProbes[0]; int sy = (pointNum / nProbes[0]) % nProbes[1]; int sz = pointNum / (nProbes[0] * nProbes[1]); Assert(sx >= 0 && sx < nProbes[0]); Assert(sy >= 0 && sy < nProbes[1]); Assert(sz >= 0 && sz < nProbes[2]); float tx0 = float(sx) / nProbes[0], tx1 = float(sx+1) / nProbes[0]; float ty0 = float(sy) / nProbes[1], ty1 = float(sy+1) / nProbes[1]; float tz0 = float(sz) / nProbes[2], tz1 = float(sz+1) / nProbes[2]; BBox b(bbox.Lerp(tx0, ty0, tz0), bbox.Lerp(tx1, ty1, tz1)); // Initialize common variables for _CreateRadProbeTask::Run()_ RNG rng(pointNum); Spectrum *c_probe = new Spectrum[SHTerms(lmax)]; MemoryArena arena; uint32_t nFound = 0, lastVisibleOffset = 0; for (int i = 0; i < 256; ++i) { if (nFound == 32) break; // Try to compute radiance probe contribution at _i_th sample point // Compute _i_th candidate point _p_ in cell's bounding box float dx = RadicalInverse(i+1, 2); float dy = RadicalInverse(i+1, 3); float dz = RadicalInverse(i+1, 5); Point p = b.Lerp(dx, dy, dz); // Skip point _p_ if not indirectly visible from camera if (scene->IntersectP(Ray(surfacePoints[lastVisibleOffset], p - surfacePoints[lastVisibleOffset], 1e-4f, 1.f, time))) { uint32_t j; // See if point is visible to any element of _surfacePoints_ for (j = 0; j < surfacePoints.size(); ++j) if (!scene->IntersectP(Ray(surfacePoints[j], p - surfacePoints[j], 1e-4f, 1.f, time))) { lastVisibleOffset = j; break; } if (j == surfacePoints.size()) continue; } ++nFound; // Compute SH coefficients of incident radiance at point _p_ if (includeDirectInProbes) { for (int i = 0; i < SHTerms(lmax); ++i) c_probe[i] = 0.f; SHProjectIncidentDirectRadiance(p, 0.f, time, arena, scene, true, lmax, rng, c_probe); for (int i = 0; i < SHTerms(lmax); ++i) c_in[i] += c_probe[i]; } if (includeIndirectInProbes) { for (int i = 0; i < SHTerms(lmax); ++i) c_probe[i] = 0.f; SHProjectIncidentIndirectRadiance(p, 0.f, time, renderer, origSample, scene, lmax, rng, nIndirSamples, c_probe); for (int i = 0; i < SHTerms(lmax); ++i) c_in[i] += c_probe[i]; } arena.FreeAll(); } // Compute final average value for probe and cleanup if (nFound > 0) for (int i = 0; i < SHTerms(lmax); ++i) c_in[i] /= nFound; delete[] c_probe; prog.Update(); }
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; }
Spectrum GlossyPRTIntegrator::Li(const Scene *scene, const Renderer *, const RayDifferential &ray, const Intersection &isect, const Sample *sample, RNG &rng, MemoryArena &arena) const { Spectrum L = 0.f; Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena); const Point &p = bsdf->dgShading.p; // Compute reflected radiance with glossy PRT at point // Compute SH radiance transfer matrix at point and SH coefficients Spectrum *c_t = arena.Alloc<Spectrum>(SHTerms(lmax)); Spectrum *T = arena.Alloc<Spectrum>(SHTerms(lmax)*SHTerms(lmax)); SHComputeTransferMatrix(p, isect.rayEpsilon, scene, rng, nSamples, lmax, T); SHMatrixVectorMultiply(T, c_in, c_t, lmax); // Rotate incident SH lighting to local coordinate frame Vector r1 = bsdf->LocalToWorld(Vector(1,0,0)); Vector r2 = bsdf->LocalToWorld(Vector(0,1,0)); Normal nl = Normal(bsdf->LocalToWorld(Vector(0,0,1))); Matrix4x4 rot(r1.x, r2.x, nl.x, 0, r1.y, r2.y, nl.y, 0, r1.z, r2.z, nl.z, 0, 0, 0, 0, 1); Spectrum *c_l = arena.Alloc<Spectrum>(SHTerms(lmax)); SHRotate(c_t, c_l, rot, lmax, arena); #if 0 // Sample BSDF and integrate against direct SH coefficients float *Ylm = ALLOCA(float, SHTerms(lmax)); int ns = 1024; for (int i = 0; i < ns; ++i) { Vector wi; float pdf; Spectrum f = bsdf->Sample_f(wo, &wi, BSDFSample(rng), &pdf); if (pdf > 0.f && !f.IsBlack() && !scene->IntersectP(Ray(p, wi))) { f *= fabsf(Dot(wi, n)) / (pdf * ns); SHEvaluate(bsdf->WorldToLocal(wi), lmax, Ylm); Spectrum Li = 0.f; for (int j = 0; j < SHTerms(lmax); ++j) Li += Ylm[j] * c_l[j] * f; L += Li.Clamp(); } } #else // Compute final coefficients _c\_o_ using BSDF matrix Spectrum *c_o = arena.Alloc<Spectrum>(SHTerms(lmax)); SHMatrixVectorMultiply(B, c_l, c_o, lmax); // Evaluate outgoing radiance function for $\wo$ and add to _L_ Vector woLocal = bsdf->WorldToLocal(wo); float *Ylm = ALLOCA(float, SHTerms(lmax)); SHEvaluate(woLocal, lmax, Ylm); Spectrum Li = 0.f; for (int i = 0; i < SHTerms(lmax); ++i) Li += Ylm[i] * c_o[i]; L += Li.Clamp(); #endif return L; }
// DiffusePRTIntegrator Method Definitions DiffusePRTIntegrator::DiffusePRTIntegrator(int lm, int ns) : lmax(lm), nSamples(RoundUpPow2(ns)) { c_in = new Spectrum[SHTerms(lmax)]; }