bool Sphere::Sample(const Interaction &ref, const Point2f &sample, Interaction *it) const { // Compute coordinate system for sphere sampling Point3f pCenter = (*ObjectToWorld)(Point3f(0, 0, 0)); Point3f pOrigin = OffsetRayOrigin(ref.p, ref.pError, ref.n, pCenter - ref.p); Vector3f wc = Normalize(pCenter - ref.p); Vector3f wcX, wcY; CoordinateSystem(wc, &wcX, &wcY); // Sample uniformly on sphere if $\pt{}$ is inside it if (DistanceSquared(pOrigin, pCenter) <= 1.0001f * radius * radius) return Sample(sample, it); // Sample sphere uniformly inside subtended cone Float sinThetaMax2 = radius * radius / DistanceSquared(ref.p, pCenter); Float cosThetaMax = std::sqrt(std::max((Float)0., (Float)1. - sinThetaMax2)); SurfaceInteraction isectSphere; Float tHit; Ray r = ref.SpawnRay(UniformSampleCone(sample, cosThetaMax, wcX, wcY, wc)); if (!Intersect(r, &tHit, &isectSphere)) return false; *it = isectSphere; if (ReverseOrientation) it->n *= -1.f; return true; }
Spectrum DiffuseAreaLight::Sample_Le(const Point2f &u1, const Point2f &u2, Float time, Ray *ray, Normal3f *nLight, Float *pdfPos, Float *pdfDir) const { ProfilePhase _(Prof::LightSample); // Sample a point on the area light's _Shape_, _pShape_ Interaction pShape = shape->Sample(u1, pdfPos); pShape.mediumInterface = mediumInterface; *nLight = pShape.n; // Sample a cosine-weighted outgoing direction _w_ for area light Vector3f w; if (twoSided) { Point2f u = u2; // Choose a side to sample and then remap u[0] to [0,1] before // applying cosine-weighted hemisphere sampling for the chosen side. if (u[0] < .5) { u[0] = std::min(u[0] * 2, OneMinusEpsilon); w = CosineSampleHemisphere(u); } else { u[0] = std::min((u[0] - .5f) * 2, OneMinusEpsilon); w = CosineSampleHemisphere(u); w.z *= -1; } *pdfDir = 0.5f * CosineHemispherePdf(std::abs(w.z)); } else { w = CosineSampleHemisphere(u2); *pdfDir = CosineHemispherePdf(w.z); } Vector3f v1, v2, n(pShape.n); CoordinateSystem(n, &v1, &v2); w = w.x * v1 + w.y * v2 + w.z * n; *ray = pShape.SpawnRay(w); return L(pShape, w); }
Float Shape::Pdf(const Interaction &ref, const Vector3f &wi) const { // Intersect sample ray with area light geometry Ray ray = ref.SpawnRay(wi); Float tHit; SurfaceInteraction isectLight; // Ignore any alpha textures used for trimming the shape when performing // this intersection. Hack for the "San Miguel" scene, where this is used // to make an invisible area light. if (!Intersect(ray, &tHit, &isectLight, false)) return 0; // Convert light sample weight to solid angle measure Float pdf = DistanceSquared(ref.p, isectLight.p) / (AbsDot(isectLight.n, -wi) * Area()); if (std::isinf(pdf)) pdf = 0.f; return pdf; }
Spectrum DiffuseAreaLight::Sample_Le(const Point2f &u1, const Point2f &u2, Float time, Ray *ray, Normal3f *nLight, Float *pdfPos, Float *pdfDir) const { Interaction pShape = shape->Sample(u1); pShape.mediumInterface = mediumInterface; Vector3f w = CosineSampleHemisphere(u2); *pdfDir = CosineHemispherePdf(w.z); // Transform cosine-weighted direction to normal's coordinate system Vector3f v1, v2, n(pShape.n); CoordinateSystem(n, &v1, &v2); w = w.x * v1 + w.y * v2 + w.z * n; *ray = pShape.SpawnRay(w); *nLight = pShape.n; *pdfPos = shape->Pdf(pShape); return L(pShape, w); }
Spectrum DiffuseAreaLight::Sample_Le(const Point2f &u1, const Point2f &u2, Float time, Ray *ray, Normal3f *nLight, Float *pdfPos, Float *pdfDir) const { // Sample a point on the area light's _Shape_, _pShape_ Interaction pShape = shape->Sample(u1); pShape.mediumInterface = mediumInterface; *pdfPos = shape->Pdf(pShape); *nLight = pShape.n; // Sample a cosine-weighted outgoing direction _w_ for area light Vector3f w = CosineSampleHemisphere(u2); *pdfDir = CosineHemispherePdf(w.z); Vector3f v1, v2, n(pShape.n); CoordinateSystem(n, &v1, &v2); w = w.x * v1 + w.y * v2 + w.z * n; *ray = pShape.SpawnRay(w); return L(pShape, w); }
Spectrum EstimateDirect(const Interaction &it, const Point2f &uScattering, const Light &light, const Point2f &uLight, const Scene &scene, Sampler &sampler, MemoryArena &arena, bool handleMedia, bool specular) { BxDFType bsdfFlags = specular ? BSDF_ALL : BxDFType(BSDF_ALL & ~BSDF_SPECULAR); Spectrum Ld(0.f); // Sample light source with multiple importance sampling Vector3f wi; Float lightPdf = 0, scatteringPdf = 0; VisibilityTester visibility; Spectrum Li = light.Sample_Li(it, uLight, &wi, &lightPdf, &visibility); if (lightPdf > 0 && !Li.IsBlack()) { // Compute BSDF or phase function's value for light sample Spectrum f; if (it.IsSurfaceInteraction()) { // Evaluate BSDF for light sampling strategy const SurfaceInteraction &isect = (const SurfaceInteraction &)it; f = isect.bsdf->f(isect.wo, wi, bsdfFlags) * AbsDot(wi, isect.shading.n); scatteringPdf = isect.bsdf->Pdf(isect.wo, wi, bsdfFlags); } else { // Evaluate phase function for light sampling strategy const MediumInteraction &mi = (const MediumInteraction &)it; Float p = mi.phase->p(mi.wo, wi); f = Spectrum(p); scatteringPdf = p; } if (!f.IsBlack()) { // Compute effect of visibility for light source sample if (handleMedia) Li *= visibility.Tr(scene, sampler); else if (!visibility.Unoccluded(scene)) Li = Spectrum(0.f); // Add light's contribution to reflected radiance if (!Li.IsBlack()) { if (IsDeltaLight(light.flags)) Ld += f * Li / lightPdf; else { Float weight = PowerHeuristic(1, lightPdf, 1, scatteringPdf); Ld += f * Li * weight / lightPdf; } } } } // Sample BSDF with multiple importance sampling if (!IsDeltaLight(light.flags)) { Spectrum f; bool sampledSpecular = false; if (it.IsSurfaceInteraction()) { // Sample scattered direction for surface interactions BxDFType sampledType; const SurfaceInteraction &isect = (const SurfaceInteraction &)it; f = isect.bsdf->Sample_f(isect.wo, &wi, uScattering, &scatteringPdf, bsdfFlags, &sampledType); f *= AbsDot(wi, isect.shading.n); sampledSpecular = sampledType & BSDF_SPECULAR; } else { // Sample scattered direction for medium interactions const MediumInteraction &mi = (const MediumInteraction &)it; Float p = mi.phase->Sample_p(mi.wo, &wi, uScattering); f = Spectrum(p); scatteringPdf = p; } if (!f.IsBlack() && scatteringPdf > 0) { // Account for light contributions along sampled direction _wi_ Float weight = 1; if (!sampledSpecular) { lightPdf = light.Pdf_Li(it, wi); if (lightPdf == 0) return Ld; weight = PowerHeuristic(1, scatteringPdf, 1, lightPdf); } // Find intersection and compute transmittance SurfaceInteraction lightIsect; Ray ray = it.SpawnRay(wi); Spectrum Tr(1.f); bool foundSurfaceInteraction = handleMedia ? scene.IntersectTr(ray, sampler, &lightIsect, &Tr) : scene.Intersect(ray, &lightIsect); // Add light contribution from material sampling Spectrum Li(0.f); if (foundSurfaceInteraction) { if (lightIsect.primitive->GetAreaLight() == &light) Li = lightIsect.Le(-wi); } else Li = light.Le(ray); if (!Li.IsBlack()) Ld += f * Li * Tr * weight / scatteringPdf; } } return Ld; }