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