Exemple #1
0
Color pathTrace(const Ray& ray,
                ShapeSet& scene,
                std::list<Shape*>& lights,
                Rng& rng,
                size_t lightSamplesHint,
                size_t maxRayDepth,
                size_t pixelSampleIndex,
                Sampler **bounceSamplers)
{
    // Accumulate total incoming radiance in 'result'
    Color result = Color(0.0f, 0.0f, 0.0f);
    // As we get through more and more bounces, we track how much the light is
    // diminished through each bounce
    Color throughput = Color(1.0f, 1.0f, 1.0f);
    
    // Start with the initial ray from the camera
    Ray currentRay = ray;
    
    // While we have bounces left we can still take...
    size_t numBounces = 0;
    while (numBounces < maxRayDepth)
    {
        // Trace the ray to see if we hit anything
        Intersection intersection(currentRay);
        if (!scene.intersect(intersection))
        {
            // No hit, return black (background)
            break;
        }
        
        // Add in emission when directly visible or via a specular bounce
        if (numBounces == 0)
        {
            result += throughput * intersection.m_pMaterial->emittance();
        }
        
        // Evaluate the material and intersection information at this bounce
        Point position = intersection.position();
        Vector normal = intersection.m_normal;
        Vector outgoing = -currentRay.m_direction;
        Brdf* pBrdf = NULL;
        float brdfWeight = 1.0f;
        Color matColor = intersection.m_pMaterial->evaluate(position,
                                                            normal,
                                                            outgoing,
                                                            pBrdf,
                                                            brdfWeight);
        // No BRDF?  We can't evaluate lighting, so bail.
        if (pBrdf == NULL)
        {
            return result;
        }
        
        // Evaluate direct lighting at this bounce.
        // For each light...
        for (std::list<Shape*>::iterator iter = lights.begin();
             iter != lights.end();
             ++iter)
        {
            // Set up samplers for evaluating this light
            StratifiedRandomSampler lightSampler(lightSamplesHint, lightSamplesHint, rng);
            StratifiedRandomSampler brdfSampler(lightSamplesHint, lightSamplesHint, rng);
            
            // Sample the light (with stratified random sampling to reduce noise)
            Color lightResult = Color(0.0f, 0.0f, 0.0f);
            size_t numLightSamples = lightSampler.total2DSamplesAvailable();
            for (size_t lightSampleIndex = 0; lightSampleIndex < numLightSamples; ++lightSampleIndex)
            {
                // Sample the light using MIS between the light and the BRDF.
                // This means we ask the light for a direction, and the likelihood
                // of having sampled that direction (the PDF).  Then we ask the
                // BRDF what it thinks of that direction (its PDF), and weight
                // the light sample with MIS.
                //
                // Then, we ask the BRDF for a direction, and the likelihood of
                // having sampled that direction (the PDF).  Then we ask the
                // light what it thinks of that direction (its PDF, and whether
                // that direction even runs into the light at all), and weight
                // the BRDF sample with MIS.
                //
                // By doing both samples and asking both the BRDF and light for
                // their PDF for each one, we can combine the strengths of both
                // sampling methods and get the best of both worlds.  It does
                // cost an extra shadow ray and evaluation, though, but it is
                // generally such an improvement in quality that it is very much
                // worth the overhead.
                
                // Ask the light for a random position/normal we can use for lighting
                Light *pLightShape = dynamic_cast<Light*>(*iter);
                float lsu, lsv;
                lightSampler.sample2D(lightSampleIndex, lsu, lsv);
                Point lightPoint;
                Vector lightNormal;
                float lightPdf = 0.0f;
                pLightShape->sampleSurface(position,
                                           normal,
                                           lsu, lsv,
                                           lightPoint,
                                           lightNormal,
                                           lightPdf);
                
                if (lightPdf > 0.0f)
                {   
                    // Ask the BRDF what it thinks of this light position (for MIS)
                    Vector lightIncoming = position - lightPoint;
                    float lightDistance = lightIncoming.normalize();
                    float brdfPdf = 0.0f;
                    float brdfResult = pBrdf->evaluateSA(lightIncoming,
                                                         outgoing,
                                                         normal,
                                                         brdfPdf);
                    if (brdfResult > 0.0f && brdfPdf > 0.0f)
                    {
                        // Fire a shadow ray to make sure we can actually see the light position
                        Ray shadowRay(position, -lightIncoming, lightDistance - kRayTMin);
                        if (!scene.doesIntersect(shadowRay))
                        {
                            // The light point is visible, so let's add that
                            // contribution (mixed by MIS)
                            float misWeightLight = powerHeuristic(1, lightPdf, 1, brdfPdf);
                            lightResult += pLightShape->emitted() *
                                           intersection.m_colorModifier * matColor *
                                           brdfResult *
                                           std::fabs(dot(-lightIncoming, normal)) *
                                           misWeightLight / (lightPdf * brdfWeight);
                        }
                    }
                }
                
                // Ask the BRDF for a sample direction
                float bsu, bsv;
                brdfSampler.sample2D(lightSampleIndex, bsu, bsv);
                Vector brdfIncoming;
                float brdfPdf = 0.0f;
                float brdfResult = pBrdf->sampleSA(brdfIncoming,
                                                   outgoing,
                                                   normal,
                                                   bsu,
                                                   bsv,
                                                   brdfPdf);
                if (brdfPdf > 0.0f && brdfResult > 0.0f)
                {
                    Intersection shadowIntersection(Ray(position, -brdfIncoming));
                    bool intersected = scene.intersect(shadowIntersection);
                    if (intersected && shadowIntersection.m_pShape == pLightShape)
                    {
                        // Ask the light what it thinks of this direction (for MIS)
                        lightPdf = pLightShape->intersectPdf(shadowIntersection);
                        if (lightPdf > 0.0f)
                        {
                            // BRDF chose the light, so let's add that
                            // contribution (mixed by MIS)
                            float misWeightBrdf = powerHeuristic(1, brdfPdf, 1, lightPdf);
                            lightResult += pLightShape->emitted() * 
                                           intersection.m_colorModifier * matColor * brdfResult *
                                           std::fabs(dot(-brdfIncoming, normal)) * misWeightBrdf /
                                           (brdfPdf * brdfWeight);
                        }
                    }
                }
            }
            // Average light samples
            if (numLightSamples)
            {
                lightResult /= numLightSamples;
            }
            
            // Add direct lighting at this bounce (modified by how much the
            // previous bounces have dimmed it)
            result += throughput * lightResult;
        }
        
        // Sample the BRDF to find the direction the next leg of the path goes in
        float brdfSampleU, brdfSampleV;
        bounceSamplers[numBounces]->sample2D(pixelSampleIndex, brdfSampleU, brdfSampleV);
        Vector incoming;
        float incomingBrdfPdf = 0.0f;
        float incomingBrdfResult = pBrdf->sampleSA(incoming,
                                                   outgoing,
                                                   normal,
                                                   brdfSampleU,
                                                   brdfSampleV,
                                                   incomingBrdfPdf);

        if (incomingBrdfPdf > 0.0f)
        {
            currentRay.m_origin = position;
            currentRay.m_direction = -incoming;
            currentRay.m_tMax = kRayTMax;
            // Reduce lighting effect for the next bounce based on this bounce's BRDF
            throughput *= intersection.m_colorModifier * matColor * incomingBrdfResult *
                          (std::fabs(dot(-incoming, normal)) /
                          (incomingBrdfPdf * brdfWeight));
        }
        else
        {
            break; // BRDF is zero, stop bouncing
        }
        
        numBounces++;
    }
    
    // This represents an estimate of the total light coming in along the path
    return result;
}
Exemple #2
0
Spectrum Path::estimateDirectLighting( const RayIntersection i_intersection, RenderablePtr i_light, const Sampler &i_sampler, const SceneData &i_scene )
{
    // Direct contribution
    Spectrum Ld = Spectrum::black();
    
    float lightPdf = 0.0f;
    float surfacePdf = 0.0f;
    
    vec3f wiWorld;
    
    const vec3f &woWorld = -i_intersection.dir;
    const vec3f &woLocal = i_intersection.worldToSurface( woWorld );
    const vec3f &normal = i_intersection.getSurfacePoint().normal;
    const shading::BSDF &bsdf = i_intersection.getBSDF();
    
    /* ==================== */
    /*     Sample Light     */
    /* ==================== */
    {
        VisibilityTester visibilityTester;
        const vec3f &uLight = i_sampler.sample3D();
        
        Spectrum Li = i_light->sampleIncidenceRadiance( i_intersection, uLight, &wiWorld, &lightPdf, &visibilityTester );
        
        // Convert to local (surface) coords
        const vec3f &wiLocal = i_intersection.worldToSurface( wiWorld );
        
        // Sample light
        if ( !Li.isBlack() && lightPdf > 0.0f ) {
            
            const Spectrum &f = bsdf.computeF( woLocal, wiLocal ) * absDot( wiWorld, normal );
            surfacePdf = bsdf.pdf( woLocal, wiLocal );
            
            if ( !f.isBlack() ) {
                
                // Ray is in shadow
                if ( visibilityTester.isOccluded( i_scene ) ) {
                    Li = Spectrum::black();
                }
                
                if ( !Li.isBlack() ) {
                    
                    // Weight with MIS
                    float misWeight = powerHeuristic( lightPdf, surfacePdf );
                    Ld += ( Li * f * misWeight ) / lightPdf;
                }
            }
        }
    }
    
    /* ==================== */
    /*     Sample BSDF      */
    /* ==================== */
    {
        const vec2f &uSurface = i_sampler.sample2D();
        vec3f wiLocal;
        
        const Spectrum &f = bsdf.sampleF( woLocal, &wiLocal, uSurface, &surfacePdf ) * absDot( wiWorld, normal );
        
        if ( !f.isBlack() && surfacePdf > 0.0f ) {
            
            // TODO computer light PDF
            lightPdf = 0.0;
            
            // No contribution, return
            if ( lightPdf == 0.0 ) {
                return Ld;
            }

            // Convert to local (surface) coords
            vec3f wiWorld = i_intersection.surfaceToWorld( wiLocal );
            
            const Ray bsdfRay = Ray( i_intersection.getSurfacePoint().pos, wiWorld );
            RayIntersection lightIntersection;
            
            bool foundIntersection = i_scene.intersect( bsdfRay, lightIntersection );
            Spectrum Li = Spectrum::black();
            
            if ( foundIntersection ) {
                
                if ( lightIntersection.m_shapeId == i_light->getIdentifier() ) {
                    Li = i_light->computeRadiance( lightIntersection.getSurfacePoint(), wiWorld );
                }
            }
            
            if ( !Li.isBlack() ) {
                
                // Weight with MIS
                float misWeight = powerHeuristic( lightPdf, surfacePdf );
                Ld += ( Li * f * misWeight ) / surfacePdf;
            }
        }
    }
    
    return Ld;
}