Ejemplo n.º 1
0
  Vec6f GatherTreeBuilder::_ComputeKdPoint( mdGatherPoint &gp )
  {
    CompositedBRDF brdfs;
    // isect.Ns is modified in the shade function although it has been already set
    Vector3f ns = gp.isect.Ns;
    if (gp.isect.material) gp.isect.material->shade(Ray(), Medium::Vacuum(), gp.isect, brdfs);
    gp.isect.Ns = ns;
    float n=0;
    Color kd(zero),ks(zero);
    brdfs.getPhong(kd,ks,n);

    Vector3f direction;
    if (ks!=zero && n!=0.0f)
    {
      float scale = 0.0f;
      if(n < 20.0f)
        scale = 1.0f;
      else if(n < 40.0f)
        scale = 2.0f;
      else if(n < 80.0f)
        scale = 3.0f;
      else
        scale = 4.0f;
      direction =  (- gp.wo + gp.isect.Ns* 2*dot(gp.wo, gp.isect.Ns) ) * scale * _normalScale;
    }
    else
      direction = gp.isect.Ns * _normalScale;

    return Vec6f(gp.isect.P, direction);
  }
Ejemplo n.º 2
0
  GatherNode* GatherTreeBuilder::_MakeLeaf( vector<GpKdItem>::iterator it, vector<mdGatherPoint> &points)
  {
    GatherNode *node = new GatherNode();
    mdGatherPoint &gp = points[it->idx];
    // the shading normal has been already computed, so restore it
    Vector3f ns = gp.isect.Ns;
    CompositedBRDF brdfs;
    if (gp.isect.material) gp.isect.material->shade(Ray(), Medium::Vacuum(), gp.isect, brdfs);
    gp.isect.Ns = ns;
    float n=0;
    Color kd(zero),ks(zero);
    brdfs.getPhong(kd,ks,n);

    node->gp = &gp;
    node->strength = 1.0f / _samples;
    node->hasGlossy = false;
    node->bbox = BBox3f(gp.isect.P, gp.isect.P);
    node->emission = gp.emission;
    node->kd = kd;
    node->ks = ks;
    node->n = n;

    if(zero != kd)
      node->normalCone = FastConef(gp.isect.Ns);
    if (zero != ks && n > 0.0f)
    {
      node->hasGlossy = true;
      node->mirrorCone =  FastConef(- gp.wo + gp.isect.Ns* 2*dot(gp.wo, gp.isect.Ns) );
    }
    return node;
  }
Ejemplo n.º 3
0
  void MaterialRenderer::renderThread()
  {
    size_t numRays = 0;

    /*! tile pick loop */
    while (true)
    {
      /*! pick a new tile */
      ssize_t tile = tileID++;
      if (tile >= numTiles) break;

      /*! compute tile location */
      Random rand(int(tile)*1024);
      size_t x0 = (tile%numTilesX)*TILE_SIZE_X;
      size_t y0 = (tile/numTilesX)*TILE_SIZE_Y;

      /*! loop over all pixels of the tile */
      for (size_t dy=0; dy<TILE_SIZE_Y; dy++)
      {
        for (size_t dx=0; dx<TILE_SIZE_X; dx++)
        {
          /*! ignore tile pixels outside framebuffer */
          size_t ix = x0+dx, iy = y0+dy;
          if (ix >= film->width || iy >= film->height) continue;

          /*! create primary ray */
          DifferentialGeometry hit;
          Ray ray; camera->ray(Vec2f(ix*rcpWidth,iy*rcpHeight), Vec2f(rand.getFloat(),rand.getFloat()), ray);
          numRays++;
          scene->accel->intersect(ray,hit);
          scene->postIntersect(ray,hit);

          /*! update framebuffer */
          if (!hit) film->set(ix,iy,zero);
          else {
            if (hit.material) {
              CompositedBRDF brdfs;
              hit.material->shade(ray, Medium::Vacuum(), hit, brdfs);
              Col3f col = brdfs.eval(ray.rdir,hit,hit.Ns,(BRDFType)(DIFFUSE));
              film->set(ix,iy,col);
            }
            else {
              film->set(ix,iy,one);
            }
          }
        }
      }
    }

    /*! we access the atomic ray counter only once per tile */
    atomicNumRays += numRays;
  }
Ejemplo n.º 4
0
  void BackendScene::generateVPL( size_t vplNum, const char* vplFile,size_t vpldepth )
  {
    this->vpls.clear();
    SET_CLAMPING_DISTANCE( 0.05 * sceneradius );

    //------------------------- READ FILE IF IT DOES EXIST--------------------------------------
    if( strcmp(vplFile,"")){
      FILE *ptr;
      ptr = fopen(vplFile,"rb");  
      if (ptr)
      {
        size_t siz;
        fread(&siz,sizeof(size_t),1,ptr);
        vpls.resize(siz);
        for (size_t i = 0 ; i<siz; ++i)
          vpls[i].read(ptr);

        fclose(ptr);
        std::cout << "Vpl file read. Total # of vpls:"<< vpls.size()<<std::endl;
        return;
      }
      else  std::cout << "Unable to read vpl file."<< std::endl;
    }

    //------------------------- OTHERWISE GENERATE VPLS--------------------------------------

    Random rnd(RND_SEED);

    // Place VPLs that substitute the original lights (direct VPLs)
    std::vector<Ref<Light> >::iterator it = allLights.begin();
    for ( ; it !=allLights.end(); ++it)
    {
      // Replace TriangleLight
      if( (*it).dynamicCast<TriangleLight>() )
      {
        Ref<TriangleLight> tr = (*it).dynamicCast<TriangleLight>();
        Color I = tr->L * length(cross(tr->e1,tr->e2)) * 0.5;
        int triangleSamples = 1024;
        // HACK to avoid too many VPLs for many quads...
        if (allLights.size() > 2000)
          triangleSamples = 100;
        for (int i=0 ; i < triangleSamples ; ++i)
        {
          Vector3f pos = uniformSampleTriangle(rnd.getFloat(),rnd.getFloat(),tr->v0,tr->v1,tr->v2);
          this->vpls.push_back(vplVPL(pos, -tr->Ng, I * rcp((float)triangleSamples) ) );
        }
      }
      // Replace SpotLight
      else if ((*it).dynamicCast<SpotLight>())
      {
        Ref<SpotLight> sl = (*it).dynamicCast<SpotLight>();
        if ( sl->cosAngleMax >= 0.05 || sl->cosAngleMin <= 0.95 )
          throw std::runtime_error("Not supporting spotlights with other than 90 degree falloff for VPL generation.");

        this->vpls.push_back(vplVPL(sl->P,sl->_D,sl->I));
      }
      // Replace environment map
      else if ((*it).dynamicCast<HDRILight>())
      {
        Ref<HDRILight> hd = (*it).dynamicCast<HDRILight>();
        int envmapSamples = 32768;
        float dirlightdistance = DIR_LIGHT_DISTANCE;
        for (int i=0 ; i < envmapSamples ; ++i)
        {
          Vector3f pos(uniformSampleSphere(rnd.getFloat(),rnd.getFloat()));
          Vector3f dir = -pos;
          pos *= dirlightdistance; pos += scenecenter;
          // CHECK below line later it is not completely precise
          Color I = hd->Le(-dir) * four_pi * rcp((float)envmapSamples) * dirlightdistance * dirlightdistance;               
          this->vpls.push_back(vplVPL(pos, -dir , I ));
        }
      }
      else if ((*it).dynamicCast<DirectionalLight>())
      {
        Ref<DirectionalLight> sl = (*it).dynamicCast<DirectionalLight>();
        float distance = 100000;
        this->vpls.push_back(vplVPL(sl->_wo*distance,sl->_wo,sl->E*distance*distance));
      }
      else
        throw std::runtime_error("Not supported light type for VPL generation.");
    }

    // Place VPLs  (indirect VPLs)
    size_t direct = this->vpls.size();
    size_t orig = this->allLights.size();
    if (vpldepth <=1) return;
    while (this->vpls.size() < (vplNum+direct))
    {
      // Sample orig lights for photons
      Ref<Light> vp = this->allLights[rnd.getInt(orig)];
      Vector3f pos; 
      Sample3f dir;
      Vec2f vr(rnd.getFloat(),rnd.getFloat());
      Vec2f vs(rnd.getFloat(),rnd.getFloat());

      Color I = vp->samplePhoton(dir,pos,vr,vs,sceneradius,scenecenter);
      if (dir.pdf==0.0f) continue;
      I *= (float) orig  *  rcp(dir.pdf);

      // Generate a path for the photon and add VPLs 
      int maxdepth = int(vpldepth) - 1;   // -1 because 1 for pt means only direct light
      while ( maxdepth-- && (I != zero ) )
      {
        Ray ray (pos,dir,32*std::numeric_limits<float>::epsilon(), inf);
        DifferentialGeometry dg;
        rtcIntersect(this->scene,(RTCRay&)ray);
        this->postIntersect(ray,dg);

        if (!ray)//nothing is hit
          break;
        /*! face forward normals */
        if (dot(dg.Ng, ray.dir) > 0)
        {
          dg.Ng = -dg.Ng; dg.Ns = -dg.Ns;
        }
        CompositedBRDF brdfs;
        if (dg.material) dg.material->shade(ray, Medium::Vacuum(), dg, brdfs);
        BRDFType diffuseBRDFTypes = (BRDFType)(DIFFUSE); 
        Vec2f s = Vec2f(rnd.getFloat(),rnd.getFloat());
        Color c = brdfs.sample(-ray.dir,dg, dir,diffuseBRDFTypes,s,rnd.getFloat(),diffuseBRDFTypes);
        if (c==zero || dir.pdf==0.0f) break;

        // the dot prod should not be zero since then c would be zero
        float dott = dot(dir,dg.Ns);
        if (dott==0.0f) break;
        Color contrib = I * c * rcp(dott) * rcp((float)vplNum) ;
        //if (std::isnan(contrib.r) || std::isnan(contrib.g) || std::isnan(contrib.b))
        //  break;

        this->vpls.push_back(vplVPL(dg.P,-dg.Ns,contrib));

        Color contribScale =  c * rcp(dir.pdf);
        float rrProb = min(1.0f, reduce_avg(contribScale));
        if(rrProb == 0.0f)break;
        if (rnd.getFloat() > rrProb)
          break;

        I *= contribScale * rcp(rrProb);
        pos = dg.P;
      }
    }

    //------------------------------PERTURB VPLS WITH THE SAME POSITION-----------------------------------------------------------
    auto vplComp = [] (const vplVPL& vp1,const vplVPL& vp2) { return vp1.P < vp2.P; };
    std::sort(this->vpls.begin(),this->vpls.end(),vplComp);
    std::vector<vplVPL>::iterator itt = this->vpls.begin();
    while(itt != ( vpls.end() - 1 ) && itt != vpls.end())
    {
      vplVPL& v1 = *itt, & v2 = *(itt+1);
      if(v1.P == v2.P)
      {
        v2.I += v1.I;
        itt = this->vpls.erase(itt);
      }
      else
        ++itt;
    }

    //------------------------------WRITE OUT VPLS INTO A FILE-----------------------------------------------------------

    // writing out the VPLs
    FILE *ptr;
    ptr = fopen(vplFile,"wb");  
    if (ptr)
    {
      size_t siz = this->vpls.size();
      fwrite(&siz,sizeof(size_t),1,ptr);
      if (vpls.size())
        for (size_t i = 0 ; i<siz; ++i)
          vpls[i].write(ptr);

      fclose(ptr);
      std::cout << "Vpl file written. Total # of vpls:"<< vpls.size()<<std::endl;
    }
    else std::cout << "Unable to write vpl file. Total # of vpls: "<<vpls.size() << std::endl;

    return;
  }
Ejemplo n.º 5
0
  Color vplIntegrator::Li(LightPath& lightPath, const Ref<BackendScene>& scene, IntegratorState& state)
  {
    /*! Traverse ray. */
    DifferentialGeometry dg;
    //scene->intersector->intersect(lightPath.lastRay);
    rtcIntersect(scene->scene,(RTCRay&)lightPath.lastRay);
    scene->postIntersect(lightPath.lastRay,dg);
    state.numRays++;
    
    Color L = zero;
    const Vector3f wo = -lightPath.lastRay.dir;
    BRDFType directLightingBRDFTypes = (BRDFType)(ALL); 

    /*! Environment shading when nothing hit. */
    if (!lightPath.lastRay)
    {
      if (backplate && lightPath.unbend) {
        const int x = clamp(int(state.pixel.x * backplate->width ), 0, int(backplate->width )-1);
        const int y = clamp(int(state.pixel.y * backplate->height), 0, int(backplate->height)-1);
        L = backplate->get(x, y);
      }
      else {
        if (!lightPath.ignoreVisibleLights)
          for (size_t i=0; i<scene->envLights.size(); i++)
            L += scene->envLights[i]->Le(wo);
      }
      return L;
    }

    /*! face forward normals */
    bool backfacing = false;
    if (dot(dg.Ng, lightPath.lastRay.dir) > 0) {
      backfacing = true; dg.Ng = -dg.Ng; dg.Ns = -dg.Ns;
    }

    /*! Shade surface. */
    CompositedBRDF brdfs;
    if (dg.material) dg.material->shade(lightPath.lastRay, lightPath.lastMedium, dg, brdfs);

    /*! Add light emitted by hit area light source. */
    if (!lightPath.ignoreVisibleLights && dg.light && !backfacing)
      L += dg.light->Le(dg,wo);

    /*! Check if any BRDF component uses direct lighting. */
    bool useDirectLighting = false;
    for (size_t i=0; i<brdfs.size(); i++)
      useDirectLighting |= (brdfs[i]->type & directLightingBRDFTypes) != NONE;

    /*! Direct lighting. Shoot shadow rays to all light sources. */
    if (useDirectLighting)
    {
      for (size_t i=0; i<scene->vpls.size(); i++)
      {
        LightSample ls;
        ls.L = scene->vpls[i].sample(dg, ls.wi, ls.tMax);

        /*! Ignore zero radiance or illumination from the back. */
        //if (ls.L == Color(zero) || ls.wi.pdf == 0.0f || dot(dg.Ns,Vector3f(ls.wi)) <= 0.0f) continue; 
        if (ls.L == Color(zero) || ls.wi.pdf == 0.0f) continue;

        /*! Evaluate BRDF */
        Color brdf = brdfs.eval(wo, dg, ls.wi, ALL);
        if (brdf == Color(zero)) continue;

        /*! Test for shadows. */
        Ray shadowRay(dg.P, ls.wi, dg.error*epsilon, ls.tMax-dg.error*epsilon, lightPath.lastRay.time,dg.shadowMask);
        TIMESTART(stats.atomic.timeRT,rt0);
        rtcOccluded(scene->scene,(RTCRay&)shadowRay);
        TIMESTOP(stats.atomic.timeRT,rt0);
        state.numRays++;
        if (shadowRay) continue;

        /*! Evaluate BRDF. */
        L += ls.L * brdf * rcp(ls.wi.pdf);
      }
    }
    
    return L;
  }
Ejemplo n.º 6
0
  Col3f PathTraceIntegrator::Li(const LightPath& lightPathOrig, const Ref<BackendScene>& scene, Sampler* sampler, size_t& numRays)
  {
	  bool done = false;
	  Col3f coeff = Col3f(1,1,1);
	  Col3f Lsum = zero;
	  Col3f L = zero;
	  LightPath lightPath = lightPathOrig;
    bool doneDiffuse = false;

    /*! while cycle instead of the recusrion call 
    * throughput is accumulated and the resulting light addition is 
    * multipliled by this throughput (coef) at each itteration */
	  while (!done)
	  {

    BRDFType directLightingBRDFTypes = (BRDFType)(DIFFUSE);
    BRDFType giBRDFTypes = (BRDFType)(ALL);

    /*! Terminate path if too long or contribution too low. */
    L = zero;

    /*! Terminate the path if maxDepth is reached */
    if (lightPath.depth >= maxDepth) // || reduce_max(coeff) < minContribution)
      return Lsum;

    /*! Traverse ray. */
    DifferentialGeometry dg;
    scene->accel->intersect(lightPath.lastRay,dg);
    scene->postIntersect(lightPath.lastRay,dg);
    const Vec3f wo = -lightPath.lastRay.dir;
    numRays++;

    /*! Environment shading when nothing hit. */
    if (!dg)
    {
      if (backplate && lightPath.unbend) {
        Vec2f raster = sampler->getPrimary();
        int width = sampler->getImageSize().x;
        int height = sampler->getImageSize().y;
        int x = (int)((raster.x / width) * backplate->width);
        x = clamp(x, 0, int(backplate->width)-1);
        int y = (int)((raster.y / height) * backplate->height);
        y = clamp(y, 0, int(backplate->height)-1);
        L = backplate->get(x, y);
      }
      else {
        if (!lightPath.ignoreVisibleLights)
          for (size_t i=0; i<scene->envLights.size(); i++)
            L += scene->envLights[i]->Le(wo);
      }
      return Lsum + L*coeff;
    }

    /*! Shade surface. */
    CompositedBRDF brdfs;
    if (dg.material) dg.material->shade(lightPath.lastRay, lightPath.lastMedium, dg, brdfs);

    /*! face forward normals */
    bool backfacing = false;
#if defined(__EMBREE_CONSISTENT_NORMALS__) && __EMBREE_CONSISTENT_NORMALS__ > 1
    return Col3f(abs(dg.Ns.x),abs(dg.Ns.y),abs(dg.Ns.z));
#else
    if (dot(dg.Ng, lightPath.lastRay.dir) > 0) {
      backfacing = true; dg.Ng = -dg.Ng; dg.Ns = -dg.Ns;
    }
#endif

    /*! Sample BRDF - get the sample direction for
    * both the indirect illumination as well as for the MIS BRDF sampling */
    Col3f c; Sample3f wi;BRDFType type;
    Vec2f s  = sampler->getVec2f(firstScatterSampleID     + lightPath.depth);
    float ss = sampler->getFloat(firstScatterTypeSampleID + lightPath.depth);
    c = brdfs.sample(wo, dg, wi, type, s, ss, giBRDFTypes);

    /*! Add light emitted by hit area light source. */
    if (!lightPath.ignoreVisibleLights && dg.light && !backfacing)
      L += dg.light->Le(dg,wo);

    /*! Check if any BRDF component uses direct lighting. */
    bool useDirectLighting = false;
    for (size_t i=0; i<brdfs.size(); i++)
      useDirectLighting |= (brdfs[i]->type & directLightingBRDFTypes) != NONE;

    /*! Direct lighting. */
    if (useDirectLighting)
    {
		  std::vector<float> illumFactor;  // illumination factor for each ls
      float sum = 0;
      LightSample ls;

		  if ( wi.pdf > 0.0f )
	    {
		    Ray r( dg.P, wi.value, dg.error*epsilon, inf );
		    DifferentialGeometry diff;
		    scene->accel->intersect( r, diff );
		    scene->postIntersect( r, diff );

// 		    Col3f red = Col3f( 1.0f, 0.0f, 0.0f);
		    Col3f radiance =  Col3f( 0.0f,0.0f,0.0f );
		    if ( diff.light  ) // if BRDF sampling hits the light
		    {
		      radiance = diff.light->Le(diff, -wi.value );
		    }
        
        if ( dot( diff.Ng, -r.dir ) > 0 && type != GLOSSY_REFLECTION)	
	        L += radiance * c / wi.pdf;
      }

		/*! Run through all the lightsources and sample or compute the distribution function for rnd gen */
		for (size_t i=0; i<scene->allLights.size(); i++)
      {
        /*! Either use precomputed samples for the light or sample light now. */
        if (scene->allLights[i]->precompute()) ls = sampler->getLightSample(precomputedLightSampleID[i]);
        else ls.L = scene->allLights[i]->sample(dg, ls.wi, ls.tMax, sampler->getVec2f(lightSampleID));

		    /*! Start using only one random lightsource after first Lambertian reflection
        * in case of the direct illumination MIS this heuristics is ommited */ 
		    if (true)//donedif
		    {
			    /*! run through all the lighsources and compute radiance accumulatively */
          float boo = reduce_max(ls.L)/ls.tMax;  // incomming illuminance heuristic
          sum += boo;
			    illumFactor.push_back(boo);  // illumination factor
		    }
		    else  // if all the lights are sampled - take each sample and compute the addition
		    {
			    /*! Ignore zero radiance or illumination from the back. */
			    if (ls.L == Col3f(zero) || ls.wi.pdf == 0.0f || dot(dg.Ns,Vec3f(ls.wi)) <= 0.0f) continue;

			    /*! Test for shadows. */
			    bool inShadow = scene->accel->occluded(Ray(dg.P, ls.wi, dg.error*epsilon, ls.tMax-dg.error*epsilon));
			    numRays++;
			    if (inShadow) continue;

			    /*! Evaluate BRDF. */
			    L += ls.L * brdfs.eval(wo, dg, ls.wi, directLightingBRDFTypes) * rcp(ls.wi.pdf);
		    }
      }

	  /*! After fisrt Lambertian reflection pick one random lightsource and compute contribution
    * in case of MIS active this heuristic is ommited */
	  if (true && scene->allLights.size() != 0)//donedif
	  {
		  /*! Generate the random value */
		  unsigned int RndVal;  // random value
//       if (rand_s(&RndVal)) std::cout << "\nRND gen error!\n";
		  rand_r(&RndVal);
      float rnd((float)RndVal/(float)UINT_MAX);  // compute the 0-1 rnd value
		  
		  /*! Pick the particular lightsource according the intensity-given distribution */
		  size_t i = 0; 
      float accum = illumFactor[i]/sum;  // accumulative sum
		  while (i < scene->allLights.size() && rnd > accum)  // get the lightsource index accirding the Pr
      {
			  ++i;
        accum +=illumFactor[i]/sum;
      }

		  /*! Sample the selected lightsource and compute contribution */
		  if ( i >= scene->allLights.size() ) i = scene->allLights.size() -1;
      // if (usedLight != NULL)
      // std::cout << "direct light " << scene->allLights[i].ptr << "\n";
        float ql = illumFactor[i]/sum;  // Pr of given lightsource
		    // LightSample ls;
		    if (scene->allLights[i]->precompute()) ls = sampler->getLightSample(precomputedLightSampleID[i]);
		    else ls.L = scene->allLights[i]->sample(dg, ls.wi, ls.tMax, sampler->getVec2f(lightSampleID));

		    /*! Ignore zero radiance or illumination from the back. */
		    if (ls.L != Col3f(zero) && ls.wi.pdf != 0.0f && dot(dg.Ns,Vec3f(ls.wi)) > 0.0f) 
		    {
			    /*! Test for shadows. */
			    bool inShadow = scene->accel->occluded(Ray(dg.P, ls.wi, dg.error*epsilon, ls.tMax-dg.error*epsilon));
			    numRays++;
			    if (!inShadow) 
			    {
				    /*! Evaluate BRDF. */
				    L += ls.L * brdfs.eval(wo, dg, ls.wi, directLightingBRDFTypes) * rcp(ls.wi.pdf*ql) * weight;
			    }
        }
	  }
    }
	
	/* Add the resulting light */
	Lsum += coeff * L;

    /*! Global illumination. Pick one BRDF component and sample it. */
    if (lightPath.depth < maxDepth) //always true
    {
      /*! Continue only if we hit something valid. */
      if (c != Col3f(zero) && wi.pdf > 0.0f)
      {
        /*! detect the first diffuse */
        if (wi.pdf < 0.33) doneDiffuse = true;

        /*! Compute  simple volumetric effect. */
        const Col3f& transmission = lightPath.lastMedium.transmission;
        if (transmission != Col3f(one)) c *= pow(transmission,dg.t);

        /*! Tracking medium if we hit a medium interface. */
        Medium nextMedium = lightPath.lastMedium;
        if (type & TRANSMISSION) nextMedium = dg.material->nextMedium(lightPath.lastMedium);

        /*! Continue the path. */
		    float q = 1;
        if (doneDiffuse) { //std::cout << "\ndifusni\n";
          q = min(abs(reduce_max(c) * rcp(wi.pdf)), (float)1);
          // std::cout << q << "\n";
          unsigned int RndVal;
//           if (rand_s(&RndVal)) std::cout << "\nRND gen error!\n";
          rand_r(&RndVal);
          if ((float)RndVal/(float)UINT_MAX > q) { // std::cout << "konec";
            return Lsum;// + L*coeff;
          }
        }
        /*! Continue the path */
		    lightPath = lightPath.extended(Ray(dg.P, wi, dg.error*epsilon, inf), nextMedium, c, (type & directLightingBRDFTypes) != NONE);
		    coeff = coeff * c * rcp(q * wi.pdf);
      }else done = true;   // end the path
    }
  }
  return Lsum;
  }
Ejemplo n.º 7
0
  Color PathTraceIntegrator::Li(LightPath& lightPath, const Ref<BackendScene>& scene, IntegratorState& state)
  {
    /*! Terminate path if too long or contribution too low. */
    if (lightPath.depth >= maxDepth || reduce_max(lightPath.throughput) < minContribution)
      return zero;

    /*! Traverse ray. */
    DifferentialGeometry dg;
    //scene->intersector->intersect(lightPath.lastRay);
    rtcIntersect(scene->scene,(RTCRay&)lightPath.lastRay);
    scene->postIntersect(lightPath.lastRay,dg);
    state.numRays++;
    //return Color(dg.st.x,dg.st.y,0.0f);

    Color L = zero;
    const Vector3f wo = -lightPath.lastRay.dir;
    BRDFType directLightingBRDFTypes = (BRDFType)(DIFFUSE); 
    BRDFType giBRDFTypes = (BRDFType)(ALL);
    if (sampleLightForGlossy) {
      directLightingBRDFTypes = (BRDFType)(DIFFUSE|GLOSSY); 
      giBRDFTypes = (BRDFType)(SPECULAR);
    }

    /*! Environment shading when nothing hit. */
    if (!lightPath.lastRay)
    {
      if (backplate && lightPath.unbend) {
        const int x = clamp(int(state.pixel.x * backplate->width ), 0, int(backplate->width )-1);
        const int y = clamp(int(state.pixel.y * backplate->height), 0, int(backplate->height)-1);
        L = backplate->get(x, y);
      }
      else {
        if (!lightPath.ignoreVisibleLights)
          for (size_t i=0; i<scene->envLights.size(); i++)
            L += scene->envLights[i]->Le(wo);
      }
      return L;
    }

    /*! face forward normals */
    bool backfacing = false;
    if (dot(dg.Ng, lightPath.lastRay.dir) > 0) {
      backfacing = true; dg.Ng = -dg.Ng; dg.Ns = -dg.Ns;
    }

    /*! Shade surface. */
    CompositedBRDF brdfs;
    if (dg.material) dg.material->shade(lightPath.lastRay, lightPath.lastMedium, dg, brdfs);

    /*! Add light emitted by hit area light source. */
    if (!lightPath.ignoreVisibleLights && dg.light && !backfacing)
      L += dg.light->Le(dg,wo);

    /*! Global illumination. Pick one BRDF component and sample it. */
    if (lightPath.depth < maxDepth)
    {
      /*! sample brdf */
      Sample3f wi; BRDFType type;
      Vec2f s  = state.sample->getVec2f(firstScatterSampleID     + lightPath.depth);
      float ss = state.sample->getFloat(firstScatterTypeSampleID + lightPath.depth);
      Color c = brdfs.sample(wo, dg, wi, type, s, ss, giBRDFTypes);

      /*! Continue only if we hit something valid. */
      if (c != Color(zero) && wi.pdf > 0.0f)
      {
        /*! Compute  simple volumetric effect. */
        const Color& transmission = lightPath.lastMedium.transmission;
        if (transmission != Color(one)) c *= pow(transmission,lightPath.lastRay.tfar);

        /*! Tracking medium if we hit a medium interface. */
        Medium nextMedium = lightPath.lastMedium;
        if (type & TRANSMISSION) nextMedium = dg.material->nextMedium(lightPath.lastMedium);

        /*! Continue the path. */
        LightPath scatteredPath = lightPath.extended(Ray(dg.P, wi, dg.error*epsilon, inf, lightPath.lastRay.time), 
                                                     nextMedium, c, (type & directLightingBRDFTypes) != NONE);
        L += c * Li(scatteredPath, scene, state) * rcp(wi.pdf);
      }
    }

    /*! Check if any BRDF component uses direct lighting. */
    bool useDirectLighting = false;
    for (size_t i=0; i<brdfs.size(); i++)
      useDirectLighting |= (brdfs[i]->type & directLightingBRDFTypes) != NONE;

    /*! Direct lighting. Shoot shadow rays to all light sources. */
    if (useDirectLighting)
    {
      for (size_t i=0; i<scene->allLights.size(); i++)
      {
        if ((scene->allLights[i]->illumMask & dg.illumMask) == 0)
          continue;

        /*! Either use precomputed samples for the light or sample light now. */
        LightSample ls;
        if (scene->allLights[i]->precompute()) ls = state.sample->getLightSample(precomputedLightSampleID[i]);
        else ls.L = scene->allLights[i]->sample(dg, ls.wi, ls.tMax, state.sample->getVec2f(lightSampleID));

        /*! Ignore zero radiance or illumination from the back. */
        //if (ls.L == Color(zero) || ls.wi.pdf == 0.0f || dot(dg.Ns,Vector3f(ls.wi)) <= 0.0f) continue; 
        if (ls.L == Color(zero) || ls.wi.pdf == 0.0f) continue;

        /*! Evaluate BRDF */
        Color brdf = brdfs.eval(wo, dg, ls.wi, directLightingBRDFTypes);
        if (brdf == Color(zero)) continue;

        /*! Test for shadows. */
        Ray shadowRay(dg.P, ls.wi, dg.error*epsilon, ls.tMax-dg.error*epsilon, lightPath.lastRay.time,dg.shadowMask);
        rtcOccluded(scene->scene,(RTCRay&)shadowRay);
        state.numRays++;
        if (shadowRay) continue;

        /*! Evaluate BRDF. */
        L += ls.L * brdf * rcp(ls.wi.pdf);
      }
    }
    
    return L;
  }