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