// UberMaterial Method Definitions void UberMaterial::ComputeScatteringFunctions(SurfaceInteraction *si, MemoryArena &arena, TransportMode mode, bool allowMultipleLobes) const { // Perform bump mapping with _bumpMap_, if present if (bumpMap) Bump(bumpMap, si); Float e = eta->Evaluate(*si); Spectrum op = opacity->Evaluate(*si).Clamp(); if (op != Spectrum(1.f)) { si->bsdf = ARENA_ALLOC(arena, BSDF)(*si, 1.f); BxDF *tr = ARENA_ALLOC(arena, SpecularTransmission)(-op + Spectrum(1.f), 1.f, 1.f, mode); si->bsdf->Add(tr); } else si->bsdf = ARENA_ALLOC(arena, BSDF)(*si, e); Spectrum kd = op * Kd->Evaluate(*si).Clamp(); if (!kd.IsBlack()) { BxDF *diff = ARENA_ALLOC(arena, LambertianReflection)(kd); si->bsdf->Add(diff); } Spectrum ks = op * Ks->Evaluate(*si).Clamp(); if (!ks.IsBlack()) { Fresnel *fresnel = ARENA_ALLOC(arena, FresnelDielectric)(1.f, e); Float roughu, roughv; if (roughnessu) roughu = roughnessu->Evaluate(*si); else roughu = roughness->Evaluate(*si); if (roughnessv) roughv = roughnessv->Evaluate(*si); else roughv = roughu; if (remapRoughness) { roughu = TrowbridgeReitzDistribution::RoughnessToAlpha(roughu); roughv = TrowbridgeReitzDistribution::RoughnessToAlpha(roughv); } MicrofacetDistribution *distrib = ARENA_ALLOC(arena, TrowbridgeReitzDistribution)(roughu, roughv); BxDF *spec = ARENA_ALLOC(arena, MicrofacetReflection)(ks, distrib, fresnel); si->bsdf->Add(spec); } Spectrum kr = op * Kr->Evaluate(*si).Clamp(); if (!kr.IsBlack()) { Fresnel *fresnel = ARENA_ALLOC(arena, FresnelDielectric)(1.f, e); si->bsdf->Add(ARENA_ALLOC(arena, SpecularReflection)(kr, fresnel)); } Spectrum kt = op * Kt->Evaluate(*si).Clamp(); if (!kt.IsBlack()) si->bsdf->Add( ARENA_ALLOC(arena, SpecularTransmission)(kt, 1.f, e, mode)); }
void FourierMaterial::ComputeScatteringFunctions( SurfaceInteraction *si, MemoryArena &arena, TransportMode mode, bool allowMultipleLobes) const { // Perform bump mapping with _bumpMap_, if present if (bumpMap) Bump(bumpMap, si); si->bsdf = ARENA_ALLOC(arena, BSDF)(*si); // Checking for zero channels works as a proxy for checking whether the // table was successfully read from the file. if (bsdfTable.nChannels > 0) si->bsdf->Add(ARENA_ALLOC(arena, FourierBSDF)(bsdfTable, mode)); }
Spectrum SeparableBSSRDF::Sample_S(const Scene &scene, Float u1, const Point2f &u2, MemoryArena &arena, SurfaceInteraction *si, Float *pdf) const { ProfilePhase pp(Prof::BSSRDFEvaluation); Spectrum Sp = Sample_Sp(scene, u1, u2, arena, si, pdf); if (!Sp.IsBlack()) { // Initialize material model at sampled surface interaction si->bsdf = ARENA_ALLOC(arena, BSDF)(*si); si->bsdf->Add(ARENA_ALLOC(arena, SeparableBSSRDFAdapter)(this)); si->wo = Vector3f(si->shading.n); } return Sp; }
// KdSubsurfaceMaterial Method Definitions void KdSubsurfaceMaterial::ComputeScatteringFunctions( SurfaceInteraction *si, MemoryArena &arena, TransportMode mode, bool allowMultipleLobes) const { // Perform bump mapping with _bumpMap_, if present if (bumpMap) Bump(bumpMap, si); Spectrum R = Kr->Evaluate(*si).Clamp(); Spectrum T = Kt->Evaluate(*si).Clamp(); Float urough = uRoughness->Evaluate(*si); Float vrough = vRoughness->Evaluate(*si); // Initialize _bsdf_ for smooth or rough dielectric si->bsdf = ARENA_ALLOC(arena, BSDF)(*si, eta); if (R.IsBlack() && T.IsBlack()) return; bool isSpecular = urough == 0 && vrough == 0; if (isSpecular && allowMultipleLobes) { si->bsdf->Add( ARENA_ALLOC(arena, FresnelSpecular)(R, T, 1.f, eta, mode)); } else { if (remapRoughness) { urough = TrowbridgeReitzDistribution::RoughnessToAlpha(urough); vrough = TrowbridgeReitzDistribution::RoughnessToAlpha(vrough); } MicrofacetDistribution *distrib = isSpecular ? nullptr : ARENA_ALLOC(arena, TrowbridgeReitzDistribution)( urough, vrough); if (!R.IsBlack()) { Fresnel *fresnel = ARENA_ALLOC(arena, FresnelDielectric)(1.f, eta); if (isSpecular) si->bsdf->Add( ARENA_ALLOC(arena, SpecularReflection)(R, fresnel)); else si->bsdf->Add(ARENA_ALLOC(arena, MicrofacetReflection)( R, distrib, fresnel)); } if (!T.IsBlack()) { if (isSpecular) si->bsdf->Add(ARENA_ALLOC(arena, SpecularTransmission)( T, 1.f, eta, mode)); else si->bsdf->Add(ARENA_ALLOC(arena, MicrofacetTransmission)( T, distrib, 1.f, eta, mode)); } } Spectrum mfree = scale * mfp->Evaluate(*si).Clamp(); Spectrum kd = Kd->Evaluate(*si).Clamp(); Spectrum sig_a, sig_s; SubsurfaceFromDiffuse(table, kd, Spectrum(1.f) / mfree, &sig_a, &sig_s); si->bssrdf = ARENA_ALLOC(arena, TabulatedBSSRDF)(*si, this, mode, eta, sig_a, sig_s, table); }
Spectrum GridDensityMedium::Sample(const Ray &rWorld, Sampler &sampler, MemoryArena &arena, MediumInteraction *mi) const { Ray ray = WorldToMedium( Ray(rWorld.o, Normalize(rWorld.d), rWorld.tMax * rWorld.d.Length())); // Compute $[\tmin, \tmax]$ interval of _ray_'s overlap with medium bounds const Bounds3f b(Point3f(0, 0, 0), Point3f(1, 1, 1)); Float tMin, tMax; if (!b.IntersectP(ray, &tMin, &tMax)) return Spectrum(1.f); // Run delta-tracking iterations to sample a medium interaction Float t = tMin; while (true) { t -= std::log(1 - sampler.Get1D()) * invMaxDensity; if (t >= tMax) break; if (Density(ray(t)) * invMaxDensity * sigma_t > sampler.Get1D()) { // Populate _mi_ with medium interaction information and return PhaseFunction *phase = ARENA_ALLOC(arena, HenyeyGreenstein)(g); *mi = MediumInteraction(rWorld(t), -rWorld.d, rWorld.time, this, phase); return sigma_s / sigma_t; } } return Spectrum(1.f); }
Spectrum GridDensityMedium::Sample(const Ray &_ray, Sampler &sampler, MemoryArena &arena, MediumInteraction *mi) const { // Transform the ray into local coordinates and determine overlap interval // [_tMin, tMax_] const Bounds3f dataBounds(Point3f(0.f, 0.f, 0.f), Point3f(1.f, 1.f, 1.f)); Ray ray = WorldToMedium( Ray(_ray.o, Normalize(_ray.d), _ray.tMax * _ray.d.Length())); Float tMin, tMax; if (!dataBounds.IntersectP(ray, &tMin, &tMax)) return Spectrum(1.f); tMin = std::max(tMin, (Float)0.f); tMax = std::min(tMax, ray.tMax); if (tMin >= tMax) return Spectrum(1.f); // Run Delta-Tracking iterations to sample a medium interaction Float t = tMin; while (true) { t -= std::log(1 - sampler.Get1D()) * invMaxDensity; if (t >= tMax) break; Float density = Density(ray(t)); if (density * invMaxDensity * sigma_t > sampler.Get1D()) { // Populate _mi_ with medium interaction information and return PhaseFunction *phase = ARENA_ALLOC(arena, HenyeyGreenstein)(g); *mi = MediumInteraction(_ray(t), -_ray.d, _ray.time, this, phase); return sigma_s / sigma_t; } } return Spectrum(1.0f); }
/* * initializes a keyword-code table */ void (clx_init)(void) { static struct { const char *name; int code; } map[] = { #define xx(a, b, c, d, e, f, g, h) #define kk(a, b, c, d, e, f, g, h) g, LEX_##a, #define yy(a, b, c, d, e, f, g, h) #include "xtoken.h" }; int i; unsigned h; struct tab *p; for (i = 0; i < NELEM(map); i++) { map[i].name = hash_string(map[i].name); h = hashkey(map[i].name, NELEM(tab)); p = ARENA_ALLOC(strg_perm, sizeof(*p)); p->key = map[i].name; p->code = map[i].code; p->link = tab[h]; tab[h] = p; } proc_prep(); }
void createMicrofacet30and0(BSDF* bsdf, bool beckmann) { Spectrum Ks(0.5); MicrofacetDistribution *distrib1, *distrib2; if (beckmann) { Float alphax = BeckmannDistribution::RoughnessToAlpha(0.8); Float alphay = BeckmannDistribution::RoughnessToAlpha(0.8); distrib1 = ARENA_ALLOC(arena, BeckmannDistribution)(alphax, alphay); alphax = BeckmannDistribution::RoughnessToAlpha(0.01); alphay = BeckmannDistribution::RoughnessToAlpha(0.01); distrib2 = ARENA_ALLOC(arena, BeckmannDistribution)(alphax, alphay); } else { Float alphax = TrowbridgeReitzDistribution::RoughnessToAlpha(0.8); Float alphay = TrowbridgeReitzDistribution::RoughnessToAlpha(0.8); distrib1 = ARENA_ALLOC(arena, TrowbridgeReitzDistribution)(alphax, alphay); alphax = TrowbridgeReitzDistribution::RoughnessToAlpha(0.01); alphay = TrowbridgeReitzDistribution::RoughnessToAlpha(0.01); distrib2 = ARENA_ALLOC(arena, TrowbridgeReitzDistribution)(alphax, alphay); } Fresnel* fresnel = ARENA_ALLOC(arena, FresnelNoOp)(); BxDF* bxdf1 = ARENA_ALLOC(arena, MicrofacetReflection)(Ks, distrib1, fresnel); bsdf->Add(bxdf1); BxDF* bxdf2 = ARENA_ALLOC(arena, MicrofacetReflection)(Ks, distrib2, fresnel); bsdf->Add(bxdf2); }
void createMicrofacet(BSDF* bsdf, MemoryArena& arena, bool beckmann, bool samplevisible, float roughx, float roughy) { Spectrum Ks(1); MicrofacetDistribution* distrib; if (beckmann) { Float alphax = BeckmannDistribution::RoughnessToAlpha(roughx); Float alphay = BeckmannDistribution::RoughnessToAlpha(roughy); distrib = ARENA_ALLOC(arena, BeckmannDistribution)(alphax, alphay, samplevisible); } else { Float alphax = TrowbridgeReitzDistribution::RoughnessToAlpha(roughx); Float alphay = TrowbridgeReitzDistribution::RoughnessToAlpha(roughy); distrib = ARENA_ALLOC(arena, TrowbridgeReitzDistribution)( alphax, alphay, samplevisible); } Fresnel* fresnel = ARENA_ALLOC(arena, FresnelNoOp)(); BxDF* bxdf = ARENA_ALLOC(arena, MicrofacetReflection)(Ks, distrib, fresnel); bsdf->Add(bxdf); }
void createFresnelBlend(BSDF* bsdf, MemoryArena& arena, bool beckmann, bool samplevisible, float roughx, float roughy) { Spectrum d(0.5); Spectrum s(0.5); MicrofacetDistribution* distrib; if (beckmann) { Float alphax = BeckmannDistribution::RoughnessToAlpha(roughx); Float alphay = BeckmannDistribution::RoughnessToAlpha(roughy); distrib = ARENA_ALLOC(arena, BeckmannDistribution)(alphax, alphay, samplevisible); } else { Float alphax = TrowbridgeReitzDistribution::RoughnessToAlpha(roughx); Float alphay = TrowbridgeReitzDistribution::RoughnessToAlpha(roughy); distrib = ARENA_ALLOC(arena, TrowbridgeReitzDistribution)( alphax, alphay, samplevisible); } BxDF* bxdf = ARENA_ALLOC(arena, FresnelBlend)(d, s, distrib); bsdf->Add(bxdf); }
// KdSubsurfaceMaterial Method Definitions void KdSubsurfaceMaterial::ComputeScatteringFunctions( SurfaceInteraction *si, MemoryArena &arena, TransportMode mode, bool allowMultipleLobes) const { // Perform bump mapping with _bumpMap_, if present if (bumpMap) Bump(bumpMap, si); si->bsdf = ARENA_ALLOC(arena, BSDF)(*si, eta); Spectrum R = Kr->Evaluate(*si).Clamp(); Spectrum T = Kt->Evaluate(*si).Clamp(); if (allowMultipleLobes && (!R.IsBlack() || !T.IsBlack())) si->bsdf->Add( ARENA_ALLOC(arena, FresnelSpecular)(1., 1., 1.f, eta, mode)); else { if (!R.IsBlack()) si->bsdf->Add(ARENA_ALLOC(arena, SpecularReflection)( R, ARENA_ALLOC(arena, FresnelDielectric)(1.f, eta))); if (!T.IsBlack()) si->bsdf->Add( ARENA_ALLOC(arena, SpecularTransmission)(T, 1.f, eta, mode)); } Spectrum sig_t = scale * sigma_t->Evaluate(*si).Clamp(); Spectrum kd = Kd->Evaluate(*si).Clamp(); Spectrum sig_a, sig_s; SubsurfaceFromDiffuse(table, kd, sig_t, &sig_a, &sig_s); si->bssrdf = ARENA_ALLOC(arena, TabulatedBSSRDF)(*si, this, mode, eta, sig_a, sig_s, table); }
void MetalMaterial::ComputeScatteringFunctions(SurfaceInteraction *si, MemoryArena &arena, TransportMode mode, bool allowMultipleLobes) const { // Perform bump mapping with _bumpMap_, if present if (bumpMap) Bump(bumpMap, si); si->bsdf = ARENA_ALLOC(arena, BSDF)(*si); Float uRough = uRoughness ? uRoughness->Evaluate(*si) : roughness->Evaluate(*si); Float vRough = vRoughness ? vRoughness->Evaluate(*si) : roughness->Evaluate(*si); if (remapRoughness) { uRough = TrowbridgeReitzDistribution::RoughnessToAlpha(uRough); vRough = TrowbridgeReitzDistribution::RoughnessToAlpha(vRough); } Fresnel *frMf = ARENA_ALLOC(arena, FresnelConductor)(1., eta->Evaluate(*si), k->Evaluate(*si)); MicrofacetDistribution *distrib = ARENA_ALLOC(arena, TrowbridgeReitzDistribution)(uRough, vRough); si->bsdf->Add(ARENA_ALLOC(arena, MicrofacetReflection)(1., distrib, frMf)); }
// GlassMaterial Method Definitions void GlassMaterial::ComputeScatteringFunctions(SurfaceInteraction *si, MemoryArena &arena, TransportMode mode, bool allowMultipleLobes) const { // Perform bump mapping with _bumpMap_, if present if (bumpMap) Bump(bumpMap, si); Float eta = index->Evaluate(*si); si->bsdf = ARENA_ALLOC(arena, BSDF)(*si, eta); Spectrum R = Kr->Evaluate(*si).Clamp(); Spectrum T = Kt->Evaluate(*si).Clamp(); if (R.IsBlack() && T.IsBlack()) return; Float urough = uRoughness->Evaluate(*si); Float vrough = vRoughness->Evaluate(*si); bool isSpecular = urough == 0 && vrough == 0; if (isSpecular && allowMultipleLobes) { si->bsdf->Add( ARENA_ALLOC(arena, FresnelSpecular)(R, T, 1.f, eta, mode)); } else { if (remapRoughness) { urough = TrowbridgeReitzDistribution::RoughnessToAlpha(urough); vrough = TrowbridgeReitzDistribution::RoughnessToAlpha(vrough); } MicrofacetDistribution *distrib = isSpecular ? nullptr : ARENA_ALLOC(arena, TrowbridgeReitzDistribution)( urough, vrough); if (!R.IsBlack()) { Fresnel *fresnel = ARENA_ALLOC(arena, FresnelDielectric)(1.f, eta); if (isSpecular) si->bsdf->Add( ARENA_ALLOC(arena, SpecularReflection)(R, fresnel)); else si->bsdf->Add(ARENA_ALLOC(arena, MicrofacetReflection)( R, distrib, fresnel)); } if (!T.IsBlack()) { if (isSpecular) si->bsdf->Add(ARENA_ALLOC(arena, SpecularTransmission)( T, 1.f, eta, mode)); else si->bsdf->Add(ARENA_ALLOC(arena, MicrofacetTransmission)( T, distrib, 1.f, eta, mode)); } } }
/* * recognizes a string literal including escape sequence */ static const char *recstr(lex_t *t) { char *p, *r; const char *ss, *s; assert(t); ss = s = LEX_SPELL(t); if (!strchr(s, '\\')) return s; p = r = ARENA_ALLOC(strg_perm, strlen(s)+1); /* file names go into strg_perm */ while (*s) { *p = (*s == '\\')? xnu(lex_bs(t, ss, &s, xiu(UCHAR_MAX), "file name")): *s++; p++; } return r; }
/* * remembers a macro guard */ void (mg_once)(void) { unsigned h; struct mgt *p; const lmap_t *pos = lmap_pfrom(lmap_from); assert(pos->type <= LMAP_INC); h = hashkey(pos->u.i.rf, NELEM(mgt)); for (p = mgt[h]; p; p = p->link) if (p->path == pos->u.i.rf) { if (p->name) /* #pragma once always wins */ p->name = mg_name; return; } p = ARENA_ALLOC(strg_perm, sizeof(*p)); p->path = pos->u.i.rf; p->name = mg_name; p->link = mgt[h]; mgt[h] = p; }
// TranslucentMaterial Method Definitions void TranslucentMaterial::ComputeScatteringFunctions( SurfaceInteraction *si, MemoryArena &arena, TransportMode mode, bool allowMultipleLobes) const { // Perform bump mapping with _bumpMap_, if present if (bumpMap) Bump(bumpMap, si); Float eta = 1.5f; si->bsdf = ARENA_ALLOC(arena, BSDF)(*si, eta); Spectrum r = reflect->Evaluate(*si).Clamp(); Spectrum t = transmit->Evaluate(*si).Clamp(); if (r.IsBlack() && t.IsBlack()) return; Spectrum kd = Kd->Evaluate(*si).Clamp(); if (!kd.IsBlack()) { if (!r.IsBlack()) si->bsdf->Add(ARENA_ALLOC(arena, LambertianReflection)(r * kd)); if (!t.IsBlack()) si->bsdf->Add(ARENA_ALLOC(arena, LambertianTransmission)(t * kd)); } Spectrum ks = Ks->Evaluate(*si).Clamp(); if (!ks.IsBlack() && (!r.IsBlack() || !t.IsBlack())) { Float rough = roughness->Evaluate(*si); if (remapRoughness) rough = TrowbridgeReitzDistribution::RoughnessToAlpha(rough); MicrofacetDistribution *distrib = ARENA_ALLOC(arena, TrowbridgeReitzDistribution)(rough, rough); if (!r.IsBlack()) { Fresnel *fresnel = ARENA_ALLOC(arena, FresnelDielectric)(1.f, eta); si->bsdf->Add(ARENA_ALLOC(arena, MicrofacetReflection)( r * ks, distrib, fresnel)); } if (!t.IsBlack()) si->bsdf->Add(ARENA_ALLOC(arena, MicrofacetTransmission)( t * ks, distrib, 1.f, eta, mode)); } }
Spectrum SeparableBSSRDF::Sample_Sp(const Scene &scene, Float u1, const Point2f &u2, MemoryArena &arena, SurfaceInteraction *pi, Float *pdf) const { ProfilePhase pp(Prof::BSSRDFEvaluation); // Choose projection axis for BSSRDF sampling Vector3f vx, vy, vz; if (u1 < .5f) { vx = ss; vy = ts; vz = Vector3f(ns); u1 *= 2; } else if (u1 < .75f) { // Prepare for sampling rays with respect to _ss_ vx = ts; vy = Vector3f(ns); vz = ss; u1 = (u1 - .5f) * 4; } else { // Prepare for sampling rays with respect to _ts_ vx = Vector3f(ns); vy = ss; vz = ts; u1 = (u1 - .75f) * 4; } // Choose spectral channel for BSSRDF sampling int ch = Clamp((int)(u1 * Spectrum::nSamples), 0, Spectrum::nSamples - 1); u1 = u1 * Spectrum::nSamples - ch; // Sample BSSRDF profile in polar coordinates Float r = Sample_Sr(ch, u2[0]); if (r < 0) return Spectrum(0.f); Float phi = 2 * Pi * u2[1]; // Compute BSSRDF profile bounds and intersection height Float rMax = Sample_Sr(ch, 0.999f); if (r > rMax) return Spectrum(0.f); Float l = 2 * std::sqrt(rMax * rMax - r * r); // Compute BSSRDF sampling ray segment Interaction base; base.p = po.p + r * (vx * std::cos(phi) + vy * std::sin(phi)) - l * vz * 0.5f; base.time = po.time; Point3f pTarget = base.p + l * vz; // Intersect BSSRDF sampling ray against the scene geometry // Declare _IntersectionChain_ and linked list struct IntersectionChain { SurfaceInteraction si; IntersectionChain *next = nullptr; }; IntersectionChain *chain = ARENA_ALLOC(arena, IntersectionChain)(); // Accumulate chain of intersections along ray IntersectionChain *ptr = chain; int nFound = 0; while (scene.Intersect(base.SpawnRayTo(pTarget), &ptr->si)) { base = ptr->si; // Append admissible intersection to _IntersectionChain_ if (ptr->si.primitive->GetMaterial() == this->material) { IntersectionChain *next = ARENA_ALLOC(arena, IntersectionChain)(); ptr->next = next; ptr = next; nFound++; } } // Randomly choose one of several intersections during BSSRDF sampling if (nFound == 0) return Spectrum(0.0f); int selected = Clamp((int)(u1 * nFound), 0, nFound - 1); while (selected-- > 0) chain = chain->next; *pi = chain->si; // Compute sample PDF and return the spatial BSSRDF term $\Sp$ *pdf = this->Pdf_Sp(*pi) / nFound; return this->Sp(*pi); }
void createOrenNayar20(BSDF* bsdf) { Spectrum Kd(1); float sigma = 20.0; BxDF* bxdf = ARENA_ALLOC(arena, OrenNayar)(Kd, sigma); bsdf->Add(bxdf); }
void TestBSDF(void (*createBSDF)(BSDF*, MemoryArena&), const char* description) { MemoryArena arena; Options opt; pbrtInit(opt); const int thetaRes = CHI2_THETA_RES; const int phiRes = CHI2_PHI_RES; const int sampleCount = CHI2_SAMPLECOUNT; Float* frequencies = new Float[thetaRes * phiRes]; Float* expFrequencies = new Float[thetaRes * phiRes]; RNG rng; int index = 0; std::cout.precision(3); // Create BSDF, which requires creating a Shape, casting a Ray that // hits the shape to get a SurfaceInteraction object. BSDF* bsdf = nullptr; Transform t = RotateX(-90); Transform tInv = Inverse(t); { bool reverseOrientation = false; ParamSet p; std::shared_ptr<Shape> disk( new Disk(&t, &tInv, reverseOrientation, 0., 1., 0, 360.)); Point3f origin(0.1, 1, 0); // offset slightly so we don't hit center of disk Vector3f direction(0, -1, 0); Float tHit; Ray r(origin, direction); SurfaceInteraction isect; disk->Intersect(r, &tHit, &isect); bsdf = ARENA_ALLOC(arena, BSDF)(isect); createBSDF(bsdf, arena); } for (int k = 0; k < CHI2_RUNS; ++k) { /* Randomly pick an outgoing direction on the hemisphere */ Point2f sample {rng.UniformFloat(), rng.UniformFloat()}; Vector3f woL = CosineSampleHemisphere(sample); Vector3f wo = bsdf->LocalToWorld(woL); FrequencyTable(bsdf, wo, rng, sampleCount, thetaRes, phiRes, frequencies); IntegrateFrequencyTable(bsdf, wo, sampleCount, thetaRes, phiRes, expFrequencies); std::string filename = StringPrintf("/tmp/chi2test_%s_%03i.m", description, ++index); DumpTables(frequencies, expFrequencies, thetaRes, phiRes, filename.c_str()); auto result = Chi2Test(frequencies, expFrequencies, thetaRes, phiRes, sampleCount, CHI2_MINFREQ, CHI2_SLEVEL, CHI2_RUNS); EXPECT_TRUE(result.first) << result.second << ", iteration " << k; } delete[] frequencies; delete[] expFrequencies; pbrtCleanup(); }
void createLambertian(BSDF* bsdf, MemoryArena& arena) { Spectrum Kd(1); bsdf->Add(ARENA_ALLOC(arena, LambertianReflection)(Kd)); }
int main(int argc, char* argv[]) { Options opt; pbrtInit(opt); // number of monte carlo estimates // const int estimates = 1; const int estimates = 10000000; // radiance of uniform environment map const double environmentRadiance = 1.0; fprintf(stderr, "outgoing radiance from a surface viewed\n" "straight on with uniform lighting\n\n" " uniform incoming radiance = %.3f\n" " monte carlo samples = %d\n\n\n", environmentRadiance, estimates); CreateBSDFFunc BSDFFuncArray[] = { createLambertian, createOrenNayar0, createOrenNayar20, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, true, true, 0.5, 0.5); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, false, true, 0.5, 0.5); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, true, true, 0.2, 0.1); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, false, true, 0.2, 0.1); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, true, true, 0.15, 0.25); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, false, true, 0.15, 0.25); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, true, true, 0.33, 0.033); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, false, true, 0.33, 0.033); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, true, false, 0.5, 0.5); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, false, false, 0.5, 0.5); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, true, false, 0.2, 0.1); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, false, false, 0.2, 0.1); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, true, false, 0.15, 0.25); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, false, false, 0.15, 0.25); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, true, false, 0.33, 0.033); }, [](BSDF* bsdf) -> void { createMicrofacet(bsdf, false, false, 0.33, 0.033); }, [](BSDF* bsdf) -> void { createFresnelBlend(bsdf, true, true, 0.15, 0.25); }, [](BSDF* bsdf) -> void { createFresnelBlend(bsdf, false, true, 0.15, 0.25); }, [](BSDF* bsdf) -> void { createFresnelBlend(bsdf, true, false, 0.15, 0.25); }, [](BSDF* bsdf) -> void { createFresnelBlend(bsdf, false, false, 0.15, 0.25); }, }; const char* BSDFFuncDescripArray[] = { "Lambertian", "Oren Nayar (sigma 0)", "Oren Nayar (sigma 20)", "Beckmann (roughness 0.5, sample visible mf area)", "Trowbridge-Reitz (roughness 0.5, sample visible mf area)", "Beckmann (roughness 0.2/0.1, sample visible mf area)", "Trowbridge-Reitz (roughness 0.2/0.1, sample visible mf area)", "Beckmann (roughness 0.15/0.25, sample visible mf area)", "Trowbridge-Reitz (roughness 0.15/0.25, sample visible mf area)", "Beckmann (roughness 0.33/0.033, sample visible mf area)", "Trowbridge-Reitz (roughness 0.33/0.033, sample visible mf area)", "Beckmann (roughness 0.5, traditional sample wh)", "Trowbridge-Reitz (roughness 0.5, traditional sample wh)", "Beckmann (roughness 0.2/0.1, traditional sample wh)", "Trowbridge-Reitz (roughness 0.2/0.1, traditional sample wh)", "Beckmann (roughness 0.15/0.25, traditional sample wh)", "Trowbridge-Reitz (roughness 0.15/0.25, traditional sample wh)", "Beckmann (roughness 0.33/0.033, traditional sample wh)", "Trowbridge-Reitz (roughness 0.33/0.033, traditional sample wh)", "Fresnel Blend Beckmann (roughness 0.15/0.25, sample visible mf area)", "Fresnel Blend Trowbridge-Reitz (roughness 0.15/0.25, sample visible mf area)", "Fresnel Blend Beckmann (roughness 0.15/0.25, traditional sample wh)", "Fresnel Blend Trowbridge-Reitz (roughness 0.15/0.25, traditional sample wh)", }; GenSampleFunc SampleFuncArray[] = { Gen_Sample_f, // CO Gen_CosHemisphere, // CO Gen_UniformHemisphere, }; const char* SampleFuncDescripArray[] = { "BSDF Importance Sampling", // CO "Cos Hemisphere", // CO "Uniform Hemisphere", }; int numModels = sizeof(BSDFFuncArray) / sizeof(BSDFFuncArray[0]); int numModelsDescrip = sizeof(BSDFFuncDescripArray) / sizeof(BSDFFuncDescripArray[0]); int numGenerators = sizeof(SampleFuncArray) / sizeof(SampleFuncArray[0]); int numGeneratorsDescrip = sizeof(SampleFuncDescripArray) / sizeof(SampleFuncDescripArray[0]); if (numModels != numModelsDescrip) { fprintf(stderr, "BSDFFuncArray and BSDFFuncDescripArray out of sync!\n"); exit(1); } if (numGenerators != numGeneratorsDescrip) { fprintf(stderr, "SampleFuncArray and SampleFuncDescripArray out of sync!\n"); exit(1); } // for each bsdf model for (int model = 0; model < numModels; model++) { BSDF* bsdf; // create BSDF which requires creating a Shape, casting a Ray // that hits the shape to get a SurfaceInteraction object. { Transform t = RotateX(-90); bool reverseOrientation = false; ParamSet p; std::shared_ptr<Shape> disk( new Disk(new Transform(t), new Transform(Inverse(t)), reverseOrientation, 0., 1., 0, 360.)); Point3f origin( 0.1, 1, 0); // offset slightly so we don't hit center of disk Vector3f direction(0, -1, 0); Float tHit; Ray r(origin, direction); SurfaceInteraction isect; disk->Intersect(r, &tHit, &isect); bsdf = ARENA_ALLOC(arena, BSDF)(isect); (BSDFFuncArray[model])(bsdf); } // facing directly at normal Vector3f woL = Normalize(Vector3f(0, 0, 1)); Vector3f wo = bsdf->LocalToWorld(woL); // was bsdf->dgShading.nn const Normal3f n = Normal3f(bsdf->LocalToWorld(Vector3f(0, 0, 1))); // for each method of generating samples over the hemisphere for (int gen = 0; gen < numGenerators; gen++) { double redSum = 0.0; const int numHistoBins = 10; double histogram[numHistoBins][numHistoBins]; for (int i = 0; i < numHistoBins; i++) { for (int j = 0; j < numHistoBins; j++) { histogram[i][j] = 0; } } int badSamples = 0; int outsideSamples = 0; int warningTarget = 1; for (int sample = 0; sample < estimates; sample++) { Vector3f wi; Float pdf; Spectrum f; // sample hemisphere around bsdf, wo is fixed (SampleFuncArray[gen])(bsdf, wo, &wi, &pdf, &f); double redF = spectrumRedValue(f); // add hemisphere sample to histogram Vector3f wiL = bsdf->WorldToLocal(wi); float x = Clamp(wiL.x, -1.f, 1.f); float y = Clamp(wiL.y, -1.f, 1.f); float wiPhi = (atan2(y, x) + Pi) / (2.0 * Pi); float wiCosTheta = wiL.z; bool validSample = (wiCosTheta > 1e-7); if (wiPhi < -0.0001 || wiPhi > 1.0001 || wiCosTheta > 1.0001) { // wiCosTheta can be less than 0 fprintf(stderr, "bad wi! %.3f %.3f %.3f, (%.3f %.3f)\n", wiL[0], wiL[1], wiL[2], wiPhi, wiCosTheta); } else if (validSample) { int histoPhi = (int)(wiPhi * numHistoBins); if (histoPhi == numHistoBins) --histoPhi; int histoCosTheta = (int)(wiCosTheta * numHistoBins); if (histoCosTheta == numHistoBins) --histoCosTheta; assert(histoPhi >= 0 && histoPhi < numHistoBins); assert(histoCosTheta >= 0 && histoCosTheta < numHistoBins); histogram[histoCosTheta][histoPhi] += 1.0 / pdf; } if (!validSample) { outsideSamples++; } else if (pdf == 0.f || std::isnan(pdf) || redF < 0 || std::isnan(redF)) { if (badSamples == warningTarget) { fprintf(stderr, "warning %d, bad sample %d! " "pdf: %.3f, redF: %.3f\n", warningTarget, sample, pdf, redF); warningTarget *= 10; } badSamples++; } else { // outgoing radiance estimate = // bsdf * incomingRadiance * cos(wi) / pdf redSum += redF * environmentRadiance * AbsDot(wi, n) / pdf; } } int goodSamples = estimates - badSamples; // print results fprintf(stderr, "*** BRDF: '%s', Samples: '%s'\n\n" "wi histogram showing the relative weight in each bin\n" " all entries should be close to 2pi = %.5f:\n" " (%d bad samples, %d outside samples)\n\n" " phi bins\n", BSDFFuncDescripArray[model], SampleFuncDescripArray[gen], Pi * 2.0, badSamples, outsideSamples); double totalSum = 0.0; for (int i = 0; i < numHistoBins; i++) { fprintf(stderr, " cos(theta) bin %02d:", i); for (int j = 0; j < numHistoBins; j++) { fprintf(stderr, " %5.2f", histogram[i][j] * numHistoBins * numHistoBins / goodSamples); totalSum += histogram[i][j]; } fprintf(stderr, "\n"); } fprintf(stderr, "\n final average : %.5f (error %.5f)\n\n" " radiance = %.5f\n\n", totalSum / goodSamples, totalSum / goodSamples - Pi * 2.0, redSum / goodSamples); } } pbrtCleanup(); return 0; }
Spectrum SeparableBSSRDF::Sample_Sp(const Scene &scene, Float sample1, const Point2f &sample2, MemoryArena &arena, SurfaceInteraction *pi, Float *pdf) const { ProfilePhase pp(Prof::BSSRDFEvaluation); // Choose projection axis for BSSRDF sampling Vector3f axis; if (sample1 < .25f) { axis = ss; sample1 *= 4; } else if (sample1 < .5f) { axis = ts; sample1 = (sample1 - .25f) * 4; } else { axis = Vector3f(ns); sample1 = (sample1 - .5f) * 2; } // Choose spectral channel for BSSRDF sampling int ch = Clamp((int)(sample1 * Spectrum::nSamples), 0, Spectrum::nSamples - 1); sample1 = sample1 * Spectrum::nSamples - ch; // Sample BSSRDF profile in polar coordinates Float r = Sample_Sr(ch, sample2.x); if (r < 0) return Spectrum(0.f); Float phi = 2 * Pi * sample2.y; // Compute BSSRDF profile bounds and intersection height Float rMax = Sample_Sr(ch, 0.999f); if (r > rMax) return Spectrum(0.f); Float l = 2 * std::sqrt(rMax * rMax - r * r); // Compute BSSRDF sampling ray segment Vector3f v0, v1; CoordinateSystem(axis, &v0, &v1); Interaction base; base.p = po.p + r * (v0 * std::cos(phi) + v1 * std::sin(phi)) - l * axis * 0.5f; base.time = po.time; Point3f target = base.p + l * axis; // Intersect BSSRDF sampling ray against the scene geometry struct IntersectionChain { SurfaceInteraction si; IntersectionChain *next; }; IntersectionChain *chain = ARENA_ALLOC(arena, IntersectionChain)(), *ptr = chain; int nFound = 0; while (true) { if (!scene.Intersect(base.SpawnRayTo(target), &ptr->si)) break; base = ptr->si; // Append admissible intersection to _IntersectionChain_ if (ptr->si.primitive->GetMaterial() == material) { IntersectionChain *next = ARENA_ALLOC(arena, IntersectionChain)(); ptr->next = next; ptr = next; nFound++; } } // Randomly choose one of several intersections during BSSRDF sampling if (nFound == 0) return Spectrum(0.0f); int selected = Clamp((int)(sample1 * nFound), 0, nFound - 1); while (selected-- > 0) chain = chain->next; *pi = chain->si; // Compute sample PDF and return the spatial BSSRDF term $\Sp$ *pdf = Pdf_Sp(*pi) / nFound; return Sp(*pi); }