// Integrator Utility Functions Spectrum UniformSampleAllLights(const Scene *scene, const Renderer *renderer, MemoryArena &arena, const Point &p, const Normal &n, const Vector &wo, float rayEpsilon, float time, BSDF *bsdf, const Sample *sample, RNG &rng, const LightSampleOffsets *lightSampleOffsets, const BSDFSampleOffsets *bsdfSampleOffsets) { Spectrum L(0.); for (uint32_t i = 0; i < scene->lights.size(); ++i) { Light *light = scene->lights[i]; int nSamples = lightSampleOffsets ? lightSampleOffsets[i].nSamples : 1; // Estimate direct lighting from _light_ samples Spectrum Ld(0.); for (int j = 0; j < nSamples; ++j) { // Find light and BSDF sample values for direct lighting estimate LightSample lightSample; BSDFSample bsdfSample; if (lightSampleOffsets != NULL && bsdfSampleOffsets != NULL) { lightSample = LightSample(sample, lightSampleOffsets[i], j); bsdfSample = BSDFSample(sample, bsdfSampleOffsets[i], j); } else { lightSample = LightSample(rng); bsdfSample = BSDFSample(rng); } Ld += EstimateDirect(scene, renderer, arena, light, p, n, wo, rayEpsilon, time, bsdf, rng, lightSample, bsdfSample, BxDFType(BSDF_ALL & ~BSDF_SPECULAR)); } L += Ld / nSamples; } return L; }
Spectrum UniformSampleOneLight(const Scene *scene, const Renderer *renderer, MemoryArena &arena, const Point &p, const Normal &n, const Vector &wo, float rayEpsilon, float time, BSDF *bsdf, const Sample *sample, RNG &rng, int lightNumOffset, const LightSampleOffsets *lightSampleOffset, const BSDFSampleOffsets *bsdfSampleOffset) { // Randomly choose a single light to sample, _light_ int nLights = int(scene->lights.size()); if (nLights == 0) return Spectrum(0.); int lightNum; if (lightNumOffset != -1) lightNum = Floor2Int(sample->oneD[lightNumOffset][0] * nLights); else lightNum = Floor2Int(rng.RandomFloat() * nLights); lightNum = min(lightNum, nLights-1); Light *light = scene->lights[lightNum]; // Initialize light and bsdf samples for single light sample LightSample lightSample; BSDFSample bsdfSample; if (lightSampleOffset != NULL && bsdfSampleOffset != NULL) { lightSample = LightSample(sample, *lightSampleOffset, 0); bsdfSample = BSDFSample(sample, *bsdfSampleOffset, 0); } else { lightSample = LightSample(rng); bsdfSample = BSDFSample(rng); } return (float)nLights * EstimateDirect(scene, renderer, arena, light, p, n, wo, rayEpsilon, time, bsdf, rng, lightSample, bsdfSample, BxDFType(BSDF_ALL & ~BSDF_SPECULAR)); }
// Preprocess void InstantRadiosity::PreProcess() { m_pVirtualLightSources = new list<VirtualLightSource>[m_nLightPathSet]; for( int k = 0 ; k < m_nLightPathSet ; ++k ) { for( int i = 0 ; i < m_nLightPaths ; ++i ) { // pick a light first float light_pick_pdf; const Light* light = scene.SampleLight( sort_canonical() , &light_pick_pdf ); // sample a ray from the light source float light_emission_pdf = 0.0f; float light_pdfa = 0.0f; Ray ray; float cosAtLight = 1.0f; Spectrum le = light->sample_l( LightSample(true) , ray , &light_emission_pdf , &light_pdfa , &cosAtLight ); Spectrum throughput = le * cosAtLight / ( light_pick_pdf * light_emission_pdf ); int current_depth = 0; Intersection intersect; while( true ) { if (false == scene.GetIntersect(ray, &intersect)) break; VirtualLightSource ls; ls.power = throughput; ls.intersect = intersect; ls.wi = -ray.m_Dir; ls.depth = ++current_depth; m_pVirtualLightSources[k].push_back( ls ); float bsdf_pdf; Vector wo; Bsdf* bsdf = intersect.primitive->GetMaterial()->GetBsdf(&intersect); Spectrum bsdf_value = bsdf->sample_f(ls.wi, wo, BsdfSample(true), &bsdf_pdf, BXDF_ALL); if( bsdf_pdf == 0.0f ) break; // apply russian roulette float continueProperbility = min( 1.0f , throughput.GetIntensity() ); if( sort_canonical() > continueProperbility ) break; throughput /= continueProperbility; // update throughput throughput *= bsdf_value * ( AbsDot(wo, intersect.normal) / bsdf_pdf ); // update next ray ray = Ray(intersect.intersect, wo, 0, 0.001f); } } } }
// generate samples void PathTracing::GenerateSample( const Sampler* sampler , PixelSample* samples , unsigned ps , const Scene& scene ) const { Integrator::GenerateSample( sampler , samples , ps , scene ); if( sampler->RoundSize( ps ) == ps ) { float* data_1d = samples[0].data; float* data_2d = samples[0].data + ps; sampler->Generate1D( data_1d , ps ); sampler->Generate2D( data_2d , ps ); for( unsigned k = 0 ; k < ps ; ++k ) { int two_k = 2*k; samples[k].bsdf_sample[0].t = data_1d[k]; samples[k].bsdf_sample[0].u = data_2d[two_k]; samples[k].bsdf_sample[0].v = data_2d[two_k+1]; } sampler->Generate1D( data_1d , ps ); sampler->Generate2D( data_2d , ps ); for( unsigned k = 0 ; k < ps ; ++k ) { int two_k = 2*k; samples[k].bsdf_sample[1].t = data_1d[k]; samples[k].bsdf_sample[1].u = data_2d[two_k]; samples[k].bsdf_sample[1].v = data_2d[two_k+1]; } sampler->Generate1D( data_1d , ps ); sampler->Generate2D( data_2d , ps ); for( unsigned k = 0 ; k < ps ; ++k ) { int two_k = 2*k; samples[k].light_sample[0].t = data_1d[k]; samples[k].light_sample[0].u = data_2d[two_k]; samples[k].light_sample[0].v = data_2d[two_k+1]; } }else { for (unsigned k = 0; k < ps; ++k) { samples[k].bsdf_sample[0] = BsdfSample(true); samples[k].bsdf_sample[1] = BsdfSample(true); samples[k].light_sample[0] = LightSample(true); } } }
void PointLight::illuminate(const glm::vec3 & point, unsigned, LightSamples & samples) const { glm::vec3 dir = m_position - point; const real d = glm::length(dir); const real normInv = real(1) / (d*d); dir *= (1 / d); const glm::vec3 radiance = m_intensity * normInv; samples.resize(1); samples[0] = LightSample(m_position, dir, d, real(1), radiance); }
// WhittedIntegrator Method Definitions Spectrum WhittedIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Intersection &isect, const Sample *sample, MemoryArena &arena) const { Spectrum L(0.); // Compute emitted and reflected light at ray intersection point // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena); // Initialize common variables for Whitted integrator const Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; // Compute emitted light if ray hit an area light source L += isect.Le(wo); // Add contribution of each light source Vector wi; for (u_int i = 0; i < scene->lights.size(); ++i) { VisibilityTester visibility; float pdf; Spectrum Li = scene->lights[i]->Sample_L(p, isect.rayEpsilon, LightSample(*sample->rng), sample->time, &wi, &pdf, &visibility); if (Li.IsBlack() || pdf == 0.f) continue; Li /= pdf; Spectrum f = bsdf->f(wo, wi); if (!f.IsBlack() && visibility.Unoccluded(scene)) L += f * Li * AbsDot(wi, n) * visibility.Transmittance(scene, renderer, sample, NULL, arena); } if (ray.depth + 1 < maxDepth) { // Trace rays for specular reflection and refraction L += SpecularReflect(ray, bsdf, *sample->rng, isect, renderer, scene, sample, arena); L += SpecularTransmit(ray, bsdf, *sample->rng, isect, renderer, scene, sample, arena); } return L; }
// radiance along a specific ray direction Spectrum DirectLight::Li( const Ray& r , const PixelSample& ps ) const { if( r.m_Depth > max_recursive_depth ) return 0.0f; // get the intersection between the ray and the scene Intersection ip; // evaluate light directly if( false == scene.GetIntersect( r , &ip ) ) return scene.Le( r ); Spectrum li = ip.Le( -r.m_Dir ); // eavluate direct light unsigned light_num = scene.LightNum(); for( unsigned i = 0 ; i < light_num ; ++i ) { const Light* light = scene.GetLight(i); li += EvaluateDirect( r , scene , light , ip , LightSample(true) , BsdfSample(true), BXDF_TYPE( BXDF_ALL ) ); } return li; }
// private method of li Spectrum InstantRadiosity::_li( const Ray& r , bool ignoreLe , float* first_intersect_dist ) const { // return if it is larger than the maximum depth if( r.m_Depth > max_recursive_depth ) return 0.0f; // get intersection from camera ray Intersection ip; if( false == scene.GetIntersect( r , &ip ) ) return ignoreLe?0.0f:scene.Le( r ); // eavluate light path less than two vertices Spectrum radiance = ignoreLe?0.0f:ip.Le( -r.m_Dir ); unsigned light_num = scene.LightNum(); for( unsigned i = 0 ; i < light_num ; ++i ) { const Light* light = scene.GetLight(i); radiance += EvaluateDirect( r , scene , light , ip , LightSample(true) , BsdfSample(true) , BXDF_TYPE( BXDF_ALL ) ); } if( first_intersect_dist ) *first_intersect_dist = ip.t; // pick a virtual light source randomly const unsigned lps_id = min( m_nLightPathSet - 1 , (int)(sort_canonical() * m_nLightPathSet) ); list<VirtualLightSource> vps = m_pVirtualLightSources[lps_id]; Bsdf* bsdf = ip.primitive->GetMaterial()->GetBsdf(&ip); // evaluate indirect illumination Spectrum indirectIllum; list<VirtualLightSource>::const_iterator it = vps.begin(); while( it != vps.end() ) { if( r.m_Depth + it->depth > max_recursive_depth ) { ++it; continue; } Vector delta = ip.intersect - it->intersect.intersect; float sqrLen = delta.SquaredLength(); float len = sqrt( sqrLen ); Vector n_delta = delta / len; Bsdf* bsdf1 = it->intersect.primitive->GetMaterial()->GetBsdf(&(it->intersect)); float gterm = AbsDot( n_delta , ip.normal ) * AbsDot( n_delta , it->intersect.normal ) / max( m_fMinSqrDist , sqrLen ); Spectrum f0 = bsdf->f( -r.m_Dir , -n_delta ); Spectrum f1 = bsdf1->f( n_delta , it->wi ); Spectrum contr = gterm * f0 * f1 * it->power; if( !contr.IsBlack() ) { Visibility vis(scene); vis.ray = Ray( it->intersect.intersect , n_delta , 0 , 0.001f , len - 0.001f ); if( vis.IsVisible() ) indirectIllum += contr; } ++it; } radiance += indirectIllum / (float)m_nLightPaths; if( m_fMinDist > 0.0f ) { Vector wi; float bsdf_pdf; Spectrum f = bsdf->sample_f( -r.m_Dir , wi , BsdfSample( true ) , &bsdf_pdf ); if( !f.IsBlack() && bsdf_pdf != 0.0f ) { PixelSample ps; float gather_dist; Ray gather_ray( ip.intersect , wi , r.m_Depth + 1 , 0.001f , m_fMinDist - 0.001f ); Spectrum li = _li( gather_ray , true , &gather_dist ); if( !li.IsBlack() ) { float dgterm = AbsDot( wi , ip.normal ) * max( 0.0f , 1.0f - gather_dist * gather_dist / m_fMinSqrDist ); radiance += f * li * dgterm / bsdf_pdf; } } } return radiance; }
LightSample LightSample::Zero() { return LightSample(Vector3::Zero(),Color::Black()); }
// return the radiance of a specific direction // note : there are one factor makes the method biased. // there is a limitation on the number of vertexes in the path Spectrum PathTracing::Li( const Ray& ray , const PixelSample& ps ) const { Spectrum L = 0.0f; Spectrum throughput = 1.0f; int bounces = 0; Ray r = ray; while(true) { Intersection inter; // get the intersection between the ray and the scene // if it's a light , accumulate the radiance and break if( false == scene.GetIntersect( r , &inter ) ) { if( bounces == 0 ) return scene.Le( r ); break; } if( bounces == 0 ) L+=inter.Le(-r.m_Dir); // make sure there is intersected primitive Sort_Assert( inter.primitive != 0 ); // evaluate the light Bsdf* bsdf = inter.primitive->GetMaterial()->GetBsdf(&inter); float light_pdf = 0.0f; LightSample light_sample = (bounces==0)?ps.light_sample[0]:LightSample(true); BsdfSample bsdf_sample = (bounces==0)?ps.bsdf_sample[0]:BsdfSample(true); const Light* light = scene.SampleLight( light_sample.t , &light_pdf ); if( light_pdf > 0.0f ) L += throughput * EvaluateDirect( r , scene , light , inter , light_sample , bsdf_sample , BXDF_TYPE(BXDF_ALL) ) / light_pdf; // sample the next direction using bsdf float path_pdf; Vector wi; BXDF_TYPE bxdf_type; Spectrum f; BsdfSample _bsdf_sample = (bounces==0)?ps.bsdf_sample[1]:BsdfSample(true); f = bsdf->sample_f( -r.m_Dir , wi , _bsdf_sample , &path_pdf , BXDF_ALL , &bxdf_type ); if( f.IsBlack() || path_pdf == 0.0f ) break; // update path weight throughput *= f * AbsDot( wi , inter.normal ) / path_pdf; if( throughput.GetIntensity() == 0.0f ) break; if( bounces > 4 ) { float continueProperbility = min( 0.5f , throughput.GetIntensity() ); if( sort_canonical() > continueProperbility ) break; throughput /= continueProperbility; } r.m_Ori = inter.intersect; r.m_Dir = wi; r.m_fMin = 0.001f; ++bounces; // note : the following code makes the method biased // 'path_per_pixel' could be set very large to reduce the side-effect. if( bounces >= max_recursive_depth ) break; } return L; }
// WhittedIntegrator [ surface integrator ] Method Definitions Spectrum WhittedIntegrator::Li(const Scene *scene, const Renderer *renderer, const RayDifferential &ray, const Intersection &isect, const Sample *sample, RNG &rng, MemoryArena &arena) const { Spectrum L(0.); // Compute emitted and reflected light at ray intersection point // Evaluate BSDF at hit point BSDF *bsdf = isect.GetBSDF(ray, arena); // return BSDF of the material of the hit primitive // Initialize common variables for Whitted integrator const pbrt::Point &p = bsdf->dgShading.p; const Normal &n = bsdf->dgShading.nn; Vector wo = -ray.d; // Compute emitted light at the intersection point. The emiited light exists if // the intersection occured at an area light source; otherwise, it is zero. L += isect.Le(wo); // Add contribution of each light source for (uint32_t i = 0; i < scene->lights.size(); ++i) { Vector wi; float pdf; VisibilityTester visibility; Spectrum Li = scene->lights[i]->Sample_L(p, isect.rayEpsilon, LightSample(rng), ray.time, &wi, &pdf, &visibility); // if light is a delta light, then "reflection" direction wi is set to the light direction with pdf =1 if (Li.IsBlack() || pdf == 0.f) continue; Spectrum f = bsdf->f(wo, wi); // // compute the fraction of light to be reflected from wi to wo. // ONLY non-delta BXDF, e.g. Lambertian (diffuse reflection) contributes to f here. if (!f.IsBlack() && visibility.Unoccluded(scene)) // bsdf indicates that some of the incident // light from direction wi is in fact scattered to the direction wo L += f * Li * AbsDot(wi, n) * visibility.Transmittance(scene, renderer, sample, rng, arena) / pdf; } // consider the specular reflection and the specular transmission; // you can get this component only when the light sources are NOT delta distribution. // there's no possible way that the peaks of the two delta distributions (the glass // and the point light) match. it is not possible // to get a highlight of a point light source in a mirror. if (ray.depth + 1 < maxDepth) { // Trace rays for specular reflection and refraction L += SpecularReflect(ray, bsdf, rng, isect, renderer, scene, sample, arena); L += SpecularTransmit(ray, bsdf, rng, isect, renderer, scene, sample, arena); } return L; }