static Color3 finalGathering(KdTree<Photon> *map , Scene& scene , 
							 Intersection& inter , RNG& rng , const Vector3& wo ,
                             int gatherSamples , int knn , Real maxSqrDis)
{
    Color3 res = Color3(0.0 , 0.0 , 0.0);
    for (int i = 0; i < gatherSamples; i++)
    {
		Real pdf;
        Vector3 wi = sampleCosHemisphere(rng.randVector3() , &pdf);
        Ray ray = Ray(inter.p + wi * EPS , wi);

        Intersection _inter;
        
        Geometry *_g = scene.intersect(ray , _inter);

        if (_g == NULL)
            continue;
        
        Color3 tmp = estimate(map , 0 , knn , scene , _inter , -wi , maxSqrDis);

		BSDF bsdf(wi , _inter , scene);
		Real cosine , bsdfPdf;
        Color3 brdf = bsdf.f(scene , wo , cosine , &bsdfPdf);
		if (brdf.isBlack())
			continue;
		pdf *= bsdfPdf;
        res = res + (tmp | brdf) * (cosine / pdf);
    }
    res = res / gatherSamples;
    return res;
}
static Color3 directIllumination(Scene& scene , Intersection& inter ,
                           RNG& rng , const Vector3& visionDir)
{
    Color3 res = Color3(0.0 , 0.0 , 0.0);
    Vector3 lightDir;
    Real cosine;
    Color3 brdf;
    Ray ray;
    
	BSDF bsdf(visionDir , inter , scene);

	int N = 1;
    for (int i = 0; i < N; i++)
    {
        int k = rand() % scene.lights.size();
		AbstractLight *l = scene.lights[k];
		
		Vector3 dirToLight;
		Real dist , directPdf;

		Color3 illu = l->illuminance(scene.sceneSphere , inter.p , rng.randVector3() ,
			dirToLight , dist , directPdf);

		illu = illu / (directPdf / scene.lights.size());

        if (scene.occluded(inter.p + dirToLight * EPS , dirToLight ,
			inter.p + dirToLight * (inter.t - EPS)))
            continue;
        
		Real bsdfPdf;
        brdf = bsdf.f(scene , dirToLight , cosine , &bsdfPdf);
		if (brdf.isBlack())
			continue;
        res = res + (illu | brdf) * (cosine / bsdfPdf);
    }
    res = res / N;
    return res;
}