void LightTracer::splatFilmT1(const ScenePtr& scene, const Sample& sample, const RNG& rng, std::vector<PathVertex>& pathVertices, ImageTile* tile) const { const vector<Light*>& lights = scene->getLights(); if (lights.size() == 0) { return; } // get the camera point const CameraPtr camera = scene->getCamera(); Vector3 nCamera; float pdfCamera; Vector3 pCamera = camera->samplePosition(sample, &nCamera, &pdfCamera); PathVertex cVertex(Color(1.0f / pdfCamera), pCamera, nCamera, camera.get()); // get the light point float pickLightPdf; float pickSample = sample.u1D[mPickLightSampleIndexes[0].offset][0]; int lightIndex = mPowerDistribution->sampleDiscrete( pickSample, &pickLightPdf); const Light* light = lights[lightIndex]; LightSample ls(sample, mLightSampleIndexes[0], 0); Vector3 nLight; float pdfLightArea; Vector3 pLight = light->samplePosition(scene, ls, &nLight, &pdfLightArea); pathVertices[0] = PathVertex( Color(1.0f / (pdfLightArea * pickLightPdf)), pLight, nLight, light); float pdfLightDirection; BSDFSample bs(sample, mBSDFSampleIndexes[0], 0); Vector3 dir = light->sampleDirection( nLight, bs.uDirection[0], bs.uDirection[1], &pdfLightDirection); Color throughput = pathVertices[0].throughput * absdot(nLight, dir) / pdfLightDirection; Ray ray(pLight, dir, 1e-3f); // start building a path from light by bouncing around the scene int lightVertex = 1; while (lightVertex < mMaxPathLength) { float epsilon; Intersection isect; if (!scene->intersect(ray, &epsilon, &isect)) { break; } const Fragment& frag = isect.fragment; pathVertices[lightVertex] = PathVertex(throughput, isect); BSDFSample bs(sample, mBSDFSampleIndexes[lightVertex], 0); lightVertex += 1; Vector3 wo = -normalize(ray.d); Vector3 wi; float pdfW; Color f = isect.getMaterial()->sampleBSDF(frag, wo, bs, &wi, &pdfW, BSDFAll, NULL, BSDFImportance); if (f == Color::Black || pdfW == 0.0f) { break; } throughput *= f * absdot(wi, frag.getNormal()) / pdfW; ray = Ray(frag.getPosition(), wi, epsilon); } // evaluate path contribution Cs,1 for (int s = 1; s <= lightVertex; ++s) { const PathVertex& pv = pathVertices[s - 1]; const Vector3& pvPos = pv.fragment.getPosition(); Vector3 filmPixel = camera->worldToScreen( pv.fragment.getPosition(), pCamera); if (filmPixel == Camera::sInvalidPixel) { continue; } // occlude test Vector3 pv2Cam(pCamera - pvPos); float occludeDistance = length(pv2Cam); float epsilon = 1e-3f * occludeDistance; Ray occludeRay(pvPos, normalize(pv2Cam), epsilon, occludeDistance - epsilon); if (scene->intersect(occludeRay)) { continue; } Color fsL; Vector3 wo = normalize(pCamera - pvPos); if (s > 1) { Vector3 wi = normalize(pathVertices[s - 2].fragment.getPosition() - pvPos); Color f = pv.material->bsdf(pv.fragment, wo, wi, BSDFAll, BSDFImportance); fsL = f * light->evalL(pLight, nLight, pathVertices[1].fragment.getPosition()); } else { fsL = pv.light->evalL(pLight, nLight, pCamera); } float fsE = camera->evalWe(pCamera, pvPos); float G = absdot(pv.fragment.getNormal(), wo) * absdot(nCamera, wo) / squaredLength(pvPos - pCamera); Color pathContribution = fsL * fsE * G * pv.throughput * cVertex.throughput; tile->addSample(filmPixel.x, filmPixel.y, pathContribution); } }