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);
    }
}