void PathVertex::evalPdfs(const PathVertex *prev, const PathEdge *prevEdge, const PathVertex &next, const PathEdge &nextEdge, float *forward, float *backward) const { switch (_type) { case EmitterVertex: if (_sampler.emitter->isInfinite()) // Positional pdf is constant for a fixed direction, which is the case // for connections to a point on an infinite emitter *forward = nextEdge.pdfForward*_record.emitter.point.pdf*next.cosineFactor(nextEdge.d); else *forward = nextEdge.pdfForward*next.cosineFactor(nextEdge.d)/nextEdge.rSq* _sampler.emitter->directionalPdf(_record.emitter.point, DirectionSample(nextEdge.d)); break; case CameraVertex: *forward = nextEdge.pdfForward*next.cosineFactor(nextEdge.d)/nextEdge.rSq* _sampler.camera->directionPdf(_record.camera.point, DirectionSample(nextEdge.d)); break; case SurfaceVertex: { const SurfaceScatterEvent &event = _record.surface.event; Vec3f dPrev = event.frame.toLocal(-prevEdge->d); Vec3f dNext = event.frame.toLocal(nextEdge.d); *forward = nextEdge .pdfForward *_sampler.bsdf->pdf(event.makeWarpedQuery(dPrev, dNext)); *backward = prevEdge->pdfBackward*_sampler.bsdf->pdf(event.makeWarpedQuery(dNext, dPrev)); if (!next .isInfiniteEmitter()) *forward *= next .cosineFactor(nextEdge .d)/nextEdge .rSq; if (!prev->isInfiniteEmitter()) *backward *= prev->cosineFactor(prevEdge->d)/prevEdge->rSq; break; } case MediumVertex: { *forward = nextEdge .pdfForward *_sampler.phase->pdf(prevEdge->d, nextEdge.d); *backward = prevEdge->pdfBackward*_sampler.phase->pdf(-nextEdge.d, -prevEdge->d); if (!next .isInfiniteEmitter()) *forward *= next .cosineFactor(nextEdge .d)/nextEdge .rSq; if (!prev->isInfiniteEmitter()) *backward *= prev->cosineFactor(prevEdge->d)/prevEdge->rSq; break; }} }
bool PinholeCamera::sampleDirect(const Vec3f &p, PathSampleGenerator &sampler, LensSample &sample) const { sample.d = _pos - p; if (!evalDirection(sampler, PositionSample(), DirectionSample(-sample.d), sample.weight, sample.pixel)) return false; float rSq = sample.d.lengthSq(); sample.dist = std::sqrt(rSq); sample.d /= sample.dist; sample.weight /= rSq; return true; }
Vec3f PathVertex::eval(const Vec3f &d, bool adjoint) const { switch (_type) { case EmitterVertex: if (_sampler.emitter->isInfinite()) return _sampler.emitter->evalPositionalEmission(_record.emitter.point); else return _sampler.emitter->evalDirectionalEmission(_record.emitter.point, DirectionSample(d)); case CameraVertex: return Vec3f(0.0f); case SurfaceVertex: return _sampler.bsdf->eval(_record.surface.event.makeWarpedQuery( _record.surface.event.wi, _record.surface.event.frame.toLocal(d)), adjoint); case MediumVertex: return _sampler.phase->eval(_record.medium.wi, d); default: return Vec3f(0.0f); } }
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); } }