void LightTracer::traceSample(PathSampleGenerator &sampler) { float lightPdf; const Primitive *light = chooseLightAdjoint(sampler, lightPdf); const Medium *medium = light->extMedium().get(); PositionSample point; if (!light->samplePosition(sampler, point)) return; DirectionSample direction; if (!light->sampleDirection(sampler, point, direction)) return; sampler.advancePath(); Vec3f throughput(point.weight/lightPdf); LensSample splat; if (_settings.minBounces == 0 && !light->isInfinite() && _scene->cam().sampleDirect(point.p, sampler, splat)) { Ray shadowRay(point.p, splat.d); shadowRay.setFarT(splat.dist); Vec3f transmission = generalizedShadowRay(shadowRay, medium, nullptr, 0); if (transmission != 0.0f) { Vec3f value = throughput*transmission*splat.weight *light->evalDirectionalEmission(point, DirectionSample(splat.d)); _splatBuffer->splatFiltered(splat.pixel, value); } } sampler.advancePath(); Ray ray(point.p, direction.d); throughput *= direction.weight; VolumeScatterEvent volumeEvent; SurfaceScatterEvent surfaceEvent; IntersectionTemporary data; IntersectionInfo info; Medium::MediumState state; state.reset(); Vec3f emission(0.0f); int bounce = 0; bool wasSpecular = true; bool didHit = _scene->intersect(ray, data, info); while ((didHit || medium) && bounce < _settings.maxBounces - 1) { bool hitSurface = true; if (medium) { volumeEvent = VolumeScatterEvent(&sampler, throughput, ray.pos(), ray.dir(), ray.farT()); if (!medium->sampleDistance(volumeEvent, state)) break; throughput *= volumeEvent.weight; volumeEvent.weight = Vec3f(1.0f); hitSurface = volumeEvent.t == volumeEvent.maxT; if (hitSurface && !didHit) break; } if (hitSurface) { surfaceEvent = makeLocalScatterEvent(data, info, ray, &sampler); Vec3f weight; Vec2f pixel; if (surfaceLensSample(_scene->cam(), surfaceEvent, medium, bounce + 1, ray, weight, pixel)) _splatBuffer->splatFiltered(pixel, weight*throughput); if (!handleSurface(surfaceEvent, data, info, medium, bounce, true, false, ray, throughput, emission, wasSpecular, state)) break; } else { volumeEvent.p += volumeEvent.t*volumeEvent.wi; Vec3f weight; Vec2f pixel; if (volumeLensSample(_scene->cam(), volumeEvent, medium, bounce + 1, ray, weight, pixel)) _splatBuffer->splatFiltered(pixel, weight*throughput); if (!handleVolume(volumeEvent, medium, bounce, true, false, ray, throughput, emission, wasSpecular, state)) break; } if (throughput.max() == 0.0f) break; 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); } }
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; }
void PhotonTracer::tracePhoton(SurfacePhotonRange &surfaceRange, VolumePhotonRange &volumeRange, PathSampleGenerator &sampler) { float lightPdf; const Primitive *light = chooseLightAdjoint(sampler, lightPdf); PositionSample point; if (!light->samplePosition(sampler, point)) return; DirectionSample direction; if (!light->sampleDirection(sampler, point, direction)) return; sampler.advancePath(); Ray ray(point.p, direction.d); Vec3f throughput(point.weight*direction.weight/lightPdf); SurfaceScatterEvent event; IntersectionTemporary data; IntersectionInfo info; Medium::MediumState state; state.reset(); Vec3f emission(0.0f); const Medium *medium = nullptr; // TODO: Media int bounce = 0; bool wasSpecular = true; bool hitSurface = true; bool didHit = _scene->intersect(ray, data, info); while ((didHit || medium) && bounce < _settings.maxBounces - 1) { if (medium) { MediumSample mediumSample; if (!medium->sampleDistance(sampler, ray, state, mediumSample)) break; throughput *= mediumSample.weight; hitSurface = mediumSample.exited; if (!hitSurface) { if (!volumeRange.full()) { VolumePhoton &p = volumeRange.addPhoton(); p.pos = mediumSample.p; p.dir = ray.dir(); p.power = throughput; } PhaseSample phaseSample; if (!mediumSample.phase->sample(sampler, ray.dir(), phaseSample)) break; ray = ray.scatter(mediumSample.p, phaseSample.w, 0.0f); ray.setPrimaryRay(false); throughput *= phaseSample.weight; } } if (hitSurface) { if (!info.bsdf->lobes().isPureSpecular() && !surfaceRange.full()) { Photon &p = surfaceRange.addPhoton(); p.pos = info.p; p.dir = ray.dir(); p.power = throughput*std::abs(info.Ns.dot(ray.dir())/info.Ng.dot(ray.dir())); } } if (volumeRange.full() && surfaceRange.full()) break; if (hitSurface) { event = makeLocalScatterEvent(data, info, ray, &sampler); if (!handleSurface(event, data, info, medium, bounce, true, false, ray, throughput, emission, wasSpecular, state)) break; } if (throughput.max() == 0.0f) break; 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); } }
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); }