Spectrum VolumeIntegrator::Li(Ray ray, Spectrum *T) { int numSamples; double step; int numVolumes = mScene->getVolumeCount(); VolumeRegion **volumes = mScene->getVolumes(); if(numVolumes < 1) { return 0.; } double t1, t0 = 0; Vec3 n; Surface surface; if(!volumes[0]->bounds().test_intersect(ray, &t0, &t1, &n)) return 0; if(ray.maxt < t1) t1 = ray.maxt; numSamples = (int)((t1 - t0) / mStepSize); if(numSamples == 0) numSamples = 1; //printf("NumSamples: %d\n", numSamples); step = (t1 - t0) / (double)numSamples; t0 += ((rand() / (float)RAND_MAX)) * step; //printf("T02: %f\n", t0); //printf("step: %f\n", step); //TODO: NOTE: multiple volumes will need aggregators! //*T = *T * Color(0.5); Spectrum Lv(0); LightSource** lights = mScene->getLightSources(); Vec3 p = ray(t0); Vec3 pPrev; Vec3 w = ray.direction * -1; Spectrum Tr(1.); for( int i = 0; i < numSamples; ++i, t0 += step ) { pPrev = p; p = ray(t0); //printf("T03: %f\n", t0); //printf("Step: %f\n", step); //printf("Difference: %s %s\n", p.str(), ray(t0 + step).str()); //printf("Curr T: %f\n", (p - pPrev).length()); Ray tRay = Ray(pPrev, p - pPrev, 0.0, step); Spectrum stepTau = volumes[0]->tau(tRay, 0.5 * step, 0); Tr = Tr * Exp(stepTau * -1); if (Tr.toTrans() < 1e-3) { const float continueProb = .5f; if ((rand() / (float)RAND_MAX) > continueProb) break; Tr = Tr / continueProb; }//else if (stepTau.toTrans() > 0.99995) // continue; Lv = Lv + volumes[0]->Lve(p); Spectrum ss = volumes[0]->sigma_s(p); if(!ss.isBlack()) { //printf("Lighting?\n"); LightSource *light = lights[0]; Vec3 wL = light->position - p; wL.norm(); Ray shadRay = Ray(p, wL); if(!mScene->intersect(shadRay, &surface) || (surface.t > (p-wL).length())) { Spectrum lTr = Exp(volumes[0]->tau(shadRay, mStepSize * 2, 0.0) * -1); //printf("P: %s\n", p.str()); //printf("LTR: %f, %f, %f\n", lTr.r(), lTr.g(), lTr.b()); Spectrum Ld = lTr * lights[0]->color; //printf("Not black.\n\tcurloc: %f, %f, %f\n\tlightTrn: %.2f, %.2f, %.2f\n\tLightCol: %.2f, %.2f, %.2f\n\tSingleSc: %.2f, %.2f, %.2f\n\tCurTrans: %.2f, %.2f, %.2f\n", \ p.x(), p.y(), p.z(), lTr.r(), lTr.g(), lTr.b(), lights[0]->color.r(), lights[0]->color.g(), lights[0]->color.b(), ss.r(), ss.g(), ss.b(), Tr.r(), Tr.g(), Tr.b()); Lv = Lv + Ld * ss * Tr;// * volumes[0]->phase(p, w, wL * 1); //printf("Phase: %f\n", volumes[0]->phase(p, w, wL)); } }
Spectrum WhittedIntegrator::li(const Scene& scene, const Renderer& renderer, const Ray& ray, const Intersection& intersection) const { Spectrum l(0.f); // If primitive is an area light, simply return its emited light AreaLight* areaLight = intersection.primitive->getAreaLight(); if (areaLight) { return areaLight->le(ray, &intersection); } // Initialize common variables const vec3& n = intersection.normal; vec3 wo = -ray.direction; l += GetDirectLighting(scene, renderer, ray, intersection); // Trace rays for specular reflection and refraction if (ray.depth < _maxRayDepth) { Material::BxDFType type; // Reflected light { vec3 wi; Spectrum f = intersection.material->sampleBSDF(wo, &wi, intersection, Material::BSDFReflection, &type); if (!f.isBlack() && glm::dot(wi, n) != 0.0f) { Ray reflectedRay; reflectedRay.origin = intersection.point; reflectedRay.direction = wi; reflectedRay.tmin = intersection.rayEpsilon; reflectedRay.depth = ray.depth + 1; Spectrum li = renderer.li(scene, reflectedRay); l += f * li; } } // Transmitted light { vec3 wi; Spectrum f = intersection.material->sampleBSDF(wo, &wi, intersection, Material::BSDFTransmission, &type); if (!f.isBlack() && glm::dot(wi, n) != 0.0f) { Ray transmittedRay; transmittedRay.origin = intersection.point; transmittedRay.direction = wi; transmittedRay.tmin = intersection.rayEpsilon; transmittedRay.depth = ray.depth + 1; Spectrum li = renderer.li(scene, transmittedRay); // Absorbtion if (glm::dot(wi, n) < 0 && transmittedRay.tmax != INFINITY) { li = li * intersection.material->transmittedLight(transmittedRay.tmax); } l += f * li; } } } return l; }
Spectrum Path::computeLi( const vec2f &pFilm, const SceneData &i_scene, const Camera &i_camera, const Sampler &i_sampler, int i_depth ) { Camera::CameraSample cameraSample; cameraSample.pixelSample = pFilm + i_sampler.sample2D(); cameraSample.lensSample = i_sampler.sample2D(); Ray ray; float weight = i_camera.generateRay( cameraSample, &ray ); // Radiance Spectrum L = Spectrum( 0.0f ); // Importance Spectrum beta = Spectrum( 1.0 ); for ( int bounces = 0;; bounces++ ) { RayIntersection intersection; bool foundIntersection = i_scene.intersect( ray, intersection ); float maxDepth = 5; if ( !foundIntersection || bounces >= maxDepth ) { break; } Spectrum Le = intersection.Le(); if ( Le.isBlack() ) { // Direct illumination sampling Spectrum Ld = beta * uniformSampleOneLight( intersection, i_sampler, i_scene ); L += Ld; // BSDF Sampling const shading::BSDF &bsdf = intersection.getBSDF(); const vec3f &surfacePosition = intersection.getSurfacePoint().pos; vec3f woLocal = -intersection.worldToSurface( -intersection.dir ); vec3f wiLocal; float pdf = 1.0; const vec2f &bsdfSample = i_sampler.sample2D(); Spectrum f = bsdf.sampleF( woLocal, &wiLocal, bsdfSample, &pdf ); // No point of continuing if no color if ( f.isBlack() || pdf == 0.0 ) { break; } // Transform to world space vec3f wiWorld = intersection.surfaceToWorld( wiLocal ); // Generate new ray ray = Ray( surfacePosition, wiWorld ); beta *= ( f * absDot( wiWorld, intersection.getSurfacePoint().normal ) ) / pdf; // Russian Roulette if ( beta.y() < 1.0f && bounces > 3 ) { float q = std::max( .05f, 1 - beta.maxComponentValue() ); if ( i_sampler.sample1D() > q ) { beta /= ( 1.0 - q ); } else { break; } } } else { L += beta * intersection.Le() / 50.0; break; } } return weight * L; }
void KajiyaSampler ::evaluate(const Scene &scene, const Path &p, std::vector<Result> &results) const { gpcpu::uint2 subpathLengths = p.getSubpathLengths(); const PathVertex &light = p[subpathLengths.sum() - 1]; Spectrum L; // note that we could start at eyeLength == 1 and connect // length-2 paths, which would get rid of the special case of eyeLength == 2 // below. however, we wouldn't get to take advantage of the stratified sampling // over the image plane, and we would also lose the nice property that this PathSampler's // Results always touch the same pixel for(size_t eyeLength = 2; eyeLength <= subpathLengths[0]; ++eyeLength) { const PathVertex &e = p[eyeLength - 1]; if(eyeLength == 2 || e.mFromDelta) { // evaluate the material's emission L = e.mThroughput * e.mEmission->evaluate(e.mToPrev, e.mDg); if(L != Spectrum::black()) { // add a new result results.resize(results.size() + 1); Result &r = results.back(); r.mThroughput = L; r.mPdf = e.mAccumulatedPdf; r.mWeight = 1.0f; r.mEyeLength = eyeLength; r.mLightLength = 0; } // end if } // end if if(!e.mScattering->isSpecular()) { // connect a path // compute the throughput of the partial path L = e.mThroughput * light.mThroughput; // XXX make compute throughput take the connection vector and geometric term L *= p.computeThroughputAssumeVisibility(eyeLength, e, 1, light); if(!L.isBlack() && !scene.intersect(Ray(e.mDg.getPoint(), light.mDg.getPoint()))) { // add a new result results.resize(results.size() + 1); Result &r= results.back(); // multiply by the connection throughput r.mThroughput = L; // set pdf, weight, and (s,t) r.mPdf = e.mAccumulatedPdf * light.mAccumulatedPdf; r.mWeight = 1.0f; r.mEyeLength = eyeLength; r.mLightLength = 1; } // end if } // end if } // end for eyeLength } // end KajiyaSampler::evaluate()
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; }