bool TraceBase::handleSurface(SurfaceScatterEvent &event, IntersectionTemporary &data, IntersectionInfo &info, const Medium *&medium, int bounce, bool adjoint, bool enableLightSampling, Ray &ray, Vec3f &throughput, Vec3f &emission, bool &wasSpecular, Medium::MediumState &state, Vec3f *transmittance) { const Bsdf &bsdf = *info.bsdf; // For forward events, the transport direction does not matter (since wi = -wo) Vec3f transparency = bsdf.eval(event.makeForwardEvent(), false); float transparencyScalar = transparency.avg(); Vec3f wo; if (event.sampler->nextBoolean(transparencyScalar) ){ wo = ray.dir(); event.pdf = transparencyScalar; event.weight = transparency/transparencyScalar; event.sampledLobe = BsdfLobes::ForwardLobe; throughput *= event.weight; } else { if (!adjoint) { if (enableLightSampling && bounce < _settings.maxBounces - 1) emission += estimateDirect(event, medium, bounce + 1, ray, transmittance)*throughput; if (info.primitive->isEmissive() && bounce >= _settings.minBounces) { if (!enableLightSampling || wasSpecular || !info.primitive->isSamplable()) emission += info.primitive->evalDirect(data, info)*throughput; } } event.requestedLobe = BsdfLobes::AllLobes; if (!bsdf.sample(event, adjoint)) return false; wo = event.frame.toGlobal(event.wo); if (!isConsistent(event, wo)) return false; throughput *= event.weight; wasSpecular = event.sampledLobe.hasSpecular(); if (!wasSpecular) ray.setPrimaryRay(false); } bool geometricBackside = (wo.dot(info.Ng) < 0.0f); medium = info.primitive->selectMedium(medium, geometricBackside); state.reset(); ray = ray.scatter(ray.hitpoint(), wo, info.epsilon); return true; }
Vec3f PhotonTracer::traceSample(Vec2u pixel, const KdTree<Photon> &surfaceTree, const KdTree<VolumePhoton> *mediumTree, PathSampleGenerator &sampler, float gatherRadius) { PositionSample point; if (!_scene->cam().samplePosition(sampler, point)) return Vec3f(0.0f); DirectionSample direction; if (!_scene->cam().sampleDirection(sampler, point, pixel, direction)) return Vec3f(0.0f); sampler.advancePath(); Vec3f throughput = point.weight*direction.weight; Ray ray(point.p, direction.d); ray.setPrimaryRay(true); IntersectionTemporary data; IntersectionInfo info; const Medium *medium = _scene->cam().medium().get(); Vec3f result(0.0f); int bounce = 0; bool didHit = _scene->intersect(ray, data, info); while ((medium || didHit) && bounce < _settings.maxBounces - 1) { if (medium) { if (mediumTree) { Vec3f beamEstimate(0.0f); mediumTree->beamQuery(ray.pos(), ray.dir(), ray.farT(), [&](const VolumePhoton &p, float t, float distSq) { Ray mediumQuery(ray); mediumQuery.setFarT(t); beamEstimate += (3.0f*INV_PI*sqr(1.0f - distSq/p.radiusSq))/p.radiusSq *medium->phaseFunction(p.pos)->eval(ray.dir(), -p.dir) *medium->transmittance(mediumQuery)*p.power; }); result += throughput*beamEstimate; } throughput *= medium->transmittance(ray); } if (!didHit) break; const Bsdf &bsdf = *info.bsdf; SurfaceScatterEvent event = makeLocalScatterEvent(data, info, ray, &sampler); Vec3f transparency = bsdf.eval(event.makeForwardEvent(), false); float transparencyScalar = transparency.avg(); Vec3f wo; if (sampler.nextBoolean(DiscreteTransparencySample, transparencyScalar)) { wo = ray.dir(); throughput *= transparency/transparencyScalar; } else { event.requestedLobe = BsdfLobes::SpecularLobe; if (!bsdf.sample(event, false)) break; wo = event.frame.toGlobal(event.wo); throughput *= event.weight; } bool geometricBackside = (wo.dot(info.Ng) < 0.0f); medium = info.primitive->selectMedium(medium, geometricBackside); ray = ray.scatter(ray.hitpoint(), wo, info.epsilon); if (std::isnan(ray.dir().sum() + ray.pos().sum())) break; if (std::isnan(throughput.sum())) break; sampler.advancePath(); bounce++; if (bounce < _settings.maxBounces) didHit = _scene->intersect(ray, data, info); } if (!didHit) { if (!medium && _scene->intersectInfinites(ray, data, info)) result += throughput*info.primitive->evalDirect(data, info); return result; } if (info.primitive->isEmissive()) result += throughput*info.primitive->evalDirect(data, info); int count = surfaceTree.nearestNeighbours(ray.hitpoint(), _photonQuery.get(), _distanceQuery.get(), _settings.gatherCount, gatherRadius); if (count == 0) return result; const Bsdf &bsdf = *info.bsdf; SurfaceScatterEvent event = makeLocalScatterEvent(data, info, ray, &sampler); Vec3f surfaceEstimate(0.0f); for (int i = 0; i < count; ++i) { event.wo = event.frame.toLocal(-_photonQuery[i]->dir); // Asymmetry due to shading normals already compensated for when storing the photon, // so we don't use the adjoint BSDF here surfaceEstimate += _photonQuery[i]->power*bsdf.eval(event, false)/std::abs(event.wo.z()); } float radiusSq = count == int(_settings.gatherCount) ? _distanceQuery[0] : gatherRadius*gatherRadius; result += throughput*surfaceEstimate*(INV_PI/radiusSq); return result; }
inline Vec3f TraceBase::generalizedShadowRayImpl(PathSampleGenerator &sampler, Ray &ray, const Medium *medium, const Primitive *endCap, int bounce, bool startsOnSurface, bool endsOnSurface, float &pdfForward, float &pdfBackward) const { IntersectionTemporary data; IntersectionInfo info; float initialFarT = ray.farT(); Vec3f throughput(1.0f); do { bool didHit = _scene->intersect(ray, data, info) && info.primitive != endCap; if (didHit) { if (!info.bsdf->lobes().hasForward()) return Vec3f(0.0f); SurfaceScatterEvent event = makeLocalScatterEvent(data, info, ray, nullptr); // For forward events, the transport direction does not matter (since wi = -wo) Vec3f transparency = info.bsdf->eval(event.makeForwardEvent(), false); if (transparency == 0.0f) return Vec3f(0.0f); if (ComputePdfs) { float transparencyScalar = transparency.avg(); pdfForward *= transparencyScalar; pdfBackward *= transparencyScalar; } throughput *= transparency; bounce++; if (bounce >= _settings.maxBounces) return Vec3f(0.0f); } if (medium) { if (ComputePdfs) { float forward, backward; throughput *= medium->transmittanceAndPdfs(sampler, ray, startsOnSurface, didHit || endsOnSurface, forward, backward); pdfForward *= forward; pdfBackward *= backward; } else { throughput *= medium->transmittance(sampler, ray, startsOnSurface, endsOnSurface); } } if (info.primitive == nullptr || info.primitive == endCap) return bounce >= _settings.minBounces ? throughput : Vec3f(0.0f); medium = info.primitive->selectMedium(medium, !info.primitive->hitBackside(data)); startsOnSurface = true; ray.setPos(ray.hitpoint()); initialFarT -= ray.farT(); ray.setNearT(info.epsilon); ray.setFarT(initialFarT); } while(true); return Vec3f(0.0f); }