void PathCPURenderThread::DirectLightSampling( const float u0, const float u1, const float u2, const float u3, const float u4, const Spectrum &pathThrouput, const BSDF &bsdf, const int depth, Spectrum *radiance) { PathCPURenderEngine *engine = (PathCPURenderEngine *)renderEngine; Scene *scene = engine->renderConfig->scene; if (!bsdf.IsDelta()) { // Pick a light source to sample float lightPickPdf; const LightSource *light = scene->SampleAllLights(u0, &lightPickPdf); Vector lightRayDir; float distance, directPdfW; Spectrum lightRadiance = light->Illuminate(scene, bsdf.hitPoint, u1, u2, u3, &lightRayDir, &distance, &directPdfW); if (!lightRadiance.Black()) { BSDFEvent event; float bsdfPdfW; Spectrum bsdfEval = bsdf.Evaluate(lightRayDir, &event, &bsdfPdfW); if (!bsdfEval.Black()) { const float epsilon = Max(MachineEpsilon::E(bsdf.hitPoint), MachineEpsilon::E(distance)); Ray shadowRay(bsdf.hitPoint, lightRayDir, epsilon, distance - epsilon); RayHit shadowRayHit; BSDF shadowBsdf; Spectrum connectionThroughput; // Check if the light source is visible if (!scene->Intersect(device, false, u4, &shadowRay, &shadowRayHit, &shadowBsdf, &connectionThroughput)) { const float cosThetaToLight = AbsDot(lightRayDir, bsdf.shadeN); const float directLightSamplingPdfW = directPdfW * lightPickPdf; const float factor = cosThetaToLight / directLightSamplingPdfW; if (depth >= engine->rrDepth) { // Russian Roulette bsdfPdfW *= Max(bsdfEval.Filter(), engine->rrImportanceCap); } // MIS between direct light sampling and BSDF sampling const float weight = PowerHeuristic(directLightSamplingPdfW, bsdfPdfW); *radiance += (weight * factor) * pathThrouput * connectionThroughput * lightRadiance * bsdfEval; } } } } }
void SchlickScatter::Pdf(const HitPoint &hitPoint, const Vector &localLightDir, const Vector &localEyeDir, float *directPdfW, float *reversePdfW) const { const Spectrum gValue = g->GetSpectrumValue(hitPoint).Clamp(-1.f, 1.f); const Spectrum k = gValue * (Spectrum(1.55f) - .55f * gValue * gValue); const float gFilter = k.Filter(); const float dotEyeLight = Dot(localEyeDir, localLightDir); // 1+k*cos instead of 1-k*cos because localEyeDir is reversed compared to the // standard phase function definition const float compcostFilter = 1.f + gFilter * dotEyeLight; const float pdf = (1.f - gFilter * gFilter) / (compcostFilter * compcostFilter * (4.f * M_PI)); if (directPdfW) *directPdfW = pdf; if (reversePdfW) *reversePdfW = pdf; }
Spectrum SchlickScatter::Sample(const HitPoint &hitPoint, const Vector &localFixedDir, Vector *localSampledDir, const float u0, const float u1, const float passThroughEvent, float *pdfW, float *absCosSampledDir, BSDFEvent *event, const BSDFEvent requestedEvent) const { if (!(requestedEvent & (DIFFUSE | REFLECT))) return Spectrum(); const Spectrum gValue = g->GetSpectrumValue(hitPoint).Clamp(-1.f, 1.f); const Spectrum k = gValue * (Spectrum(1.55f) - .55f * gValue * gValue); const float gFilter = k.Filter(); // Add a - because localEyeDir is reversed compared to the standard phase // function definition const float cost = -(2.f * u0 + gFilter - 1.f) / (2.f * gFilter * u0 - gFilter + 1.f); Vector x, y; CoordinateSystem(localFixedDir, &x, &y); *localSampledDir = SphericalDirection(sqrtf(Max(0.f, 1.f - cost * cost)), cost, 2.f * M_PI * u1, x, y, localFixedDir); // The - becomes a + because cost has been reversed above const float compcost = 1.f + gFilter * cost; *pdfW = (1.f - gFilter * gFilter) / (compcost * compcost * (4.f * M_PI)); if (*pdfW <= 0.f) return Spectrum(); *absCosSampledDir = fabsf(localSampledDir->z); *event = DIFFUSE | REFLECT; Spectrum r = volume->SigmaS(hitPoint); const Spectrum sigmaA = volume->SigmaA(hitPoint); for (u_int i = 0; i < COLOR_SAMPLES; ++i) { if (r.c[i] > 0.f) r.c[i] /= r.c[i] + sigmaA.c[i]; else r.c[i] = 1.f; } return r; }
Spectrum SchlickScatter::Evaluate(const HitPoint &hitPoint, const Vector &localLightDir, const Vector &localEyeDir, BSDFEvent *event, float *directPdfW, float *reversePdfW) const { Spectrum r = volume->SigmaS(hitPoint); const Spectrum sigmaA = volume->SigmaA(hitPoint); for (u_int i = 0; i < COLOR_SAMPLES; ++i) { if (r.c[i] > 0.f) r.c[i] /= r.c[i] + sigmaA.c[i]; else r.c[i] = 1.f; } const Spectrum gValue = g->GetSpectrumValue(hitPoint).Clamp(-1.f, 1.f); const Spectrum k = gValue * (Spectrum(1.55f) - .55f * gValue * gValue); *event = DIFFUSE | REFLECT; const float dotEyeLight = Dot(localEyeDir, localLightDir); const float kFilter = k.Filter(); // 1+k*cos instead of 1-k*cos because localEyeDir is reversed compared to the // standard phase function definition const float compcostFilter = 1.f + kFilter * dotEyeLight; const float pdf = (1.f - kFilter * kFilter) / (compcostFilter * compcostFilter * (4.f * M_PI)); if (directPdfW) *directPdfW = pdf; if (reversePdfW) *reversePdfW = pdf; // 1+k*cos instead of 1-k*cos because localEyeDir is reversed compared to the // standard phase function definition const Spectrum compcostValue = Spectrum(1.f) + k * dotEyeLight; return r * (Spectrum(1.f) - k * k) / (compcostValue * compcostValue * (4.f * M_PI)); }
void LightCPURenderThread::RenderFunc() { //SLG_LOG("[LightCPURenderThread::" << threadIndex << "] Rendering thread started"); //-------------------------------------------------------------------------- // Initialization //-------------------------------------------------------------------------- LightCPURenderEngine *engine = (LightCPURenderEngine *)renderEngine; RandomGenerator *rndGen = new RandomGenerator(engine->seedBase + threadIndex); Scene *scene = engine->renderConfig->scene; PerspectiveCamera *camera = scene->camera; Film *film = threadFilm; // Setup the sampler double metropolisSharedTotalLuminance, metropolisSharedSampleCount; Sampler *sampler = engine->renderConfig->AllocSampler(rndGen, film, &metropolisSharedTotalLuminance, &metropolisSharedSampleCount); const u_int sampleBootSize = 11; const u_int sampleEyeStepSize = 4; const u_int sampleLightStepSize = 5; const u_int sampleSize = sampleBootSize + // To generate the initial setup engine->maxPathDepth * sampleEyeStepSize + // For each eye vertex engine->maxPathDepth * sampleLightStepSize; // For each light vertex sampler->RequestSamples(sampleSize); //-------------------------------------------------------------------------- // Trace light paths //-------------------------------------------------------------------------- vector<SampleResult> sampleResults; while (!boost::this_thread::interruption_requested()) { sampleResults.clear(); // Select one light source float lightPickPdf; const LightSource *light = scene->SampleAllLights(sampler->GetSample(2), &lightPickPdf); // Initialize the light path float lightEmitPdfW; Ray nextEventRay; Spectrum lightPathFlux = light->Emit(scene, sampler->GetSample(3), sampler->GetSample(4), sampler->GetSample(5), sampler->GetSample(6), &nextEventRay.o, &nextEventRay.d, &lightEmitPdfW); if (lightPathFlux.Black()) { sampler->NextSample(sampleResults); continue; } lightPathFlux /= lightEmitPdfW * lightPickPdf; assert (!lightPathFlux.IsNaN() && !lightPathFlux.IsInf()); // Sample a point on the camera lens Point lensPoint; if (!camera->SampleLens(sampler->GetSample(7), sampler->GetSample(8), &lensPoint)) { sampler->NextSample(sampleResults); continue; } //---------------------------------------------------------------------- // I don't try to connect the light vertex directly with the eye // because InfiniteLight::Emit() returns a point on the scene bounding // sphere. Instead, I trace a ray from the camera like in BiDir. // This is also a good why to test the Film Per-Pixel-Normalization and // the Per-Screen-Normalization Buffers used by BiDir. //---------------------------------------------------------------------- TraceEyePath(sampler, &sampleResults); //---------------------------------------------------------------------- // Trace the light path //---------------------------------------------------------------------- int depth = 1; while (depth <= engine->maxPathDepth) { const u_int sampleOffset = sampleBootSize + sampleEyeStepSize * engine->maxPathDepth + (depth - 1) * sampleLightStepSize; RayHit nextEventRayHit; BSDF bsdf; Spectrum connectionThroughput; if (scene->Intersect(device, true, sampler->GetSample(sampleOffset), &nextEventRay, &nextEventRayHit, &bsdf, &connectionThroughput)) { // Something was hit lightPathFlux *= connectionThroughput; //-------------------------------------------------------------- // Try to connect the light path vertex with the eye //-------------------------------------------------------------- ConnectToEye(sampler->GetSample(sampleOffset + 1), bsdf, lensPoint, lightPathFlux, sampleResults); if (depth >= engine->maxPathDepth) break; //-------------------------------------------------------------- // Build the next vertex path ray //-------------------------------------------------------------- float bsdfPdf; Vector sampledDir; BSDFEvent event; float cosSampleDir; const Spectrum bsdfSample = bsdf.Sample(&sampledDir, sampler->GetSample(sampleOffset + 2), sampler->GetSample(sampleOffset + 3), &bsdfPdf, &cosSampleDir, &event); if (bsdfSample.Black()) break; if (depth >= engine->rrDepth) { // Russian Roulette const float prob = Max(bsdfSample.Filter(), engine->rrImportanceCap); if (sampler->GetSample(sampleOffset + 4) < prob) bsdfPdf *= prob; else break; } lightPathFlux *= bsdfSample * (cosSampleDir / bsdfPdf); assert (!lightPathFlux.IsNaN() && !lightPathFlux.IsInf()); nextEventRay = Ray(bsdf.hitPoint, sampledDir); ++depth; } else { // Ray lost in space... break; } } sampler->NextSample(sampleResults); #ifdef WIN32 // Work around Windows bad scheduling renderThread->yield(); #endif } delete sampler; delete rndGen; //SLG_LOG("[LightCPURenderThread::" << threadIndex << "] Rendering thread halted"); }
void PathCPURenderThread::RenderFunc() { //SLG_LOG("[PathCPURenderEngine::" << threadIndex << "] Rendering thread started"); //-------------------------------------------------------------------------- // Initialization //-------------------------------------------------------------------------- PathCPURenderEngine *engine = (PathCPURenderEngine *)renderEngine; RandomGenerator *rndGen = new RandomGenerator(engine->seedBase + threadIndex); Scene *scene = engine->renderConfig->scene; PerspectiveCamera *camera = scene->camera; Film * film = threadFilm; const unsigned int filmWidth = film->GetWidth(); const unsigned int filmHeight = film->GetHeight(); // Setup the sampler double metropolisSharedTotalLuminance, metropolisSharedSampleCount; Sampler *sampler = engine->renderConfig->AllocSampler(rndGen, film, &metropolisSharedTotalLuminance, &metropolisSharedSampleCount); const unsigned int sampleBootSize = 4; const unsigned int sampleStepSize = 9; const unsigned int sampleSize = sampleBootSize + // To generate eye ray engine->maxPathDepth * sampleStepSize; // For each path vertex sampler->RequestSamples(sampleSize); //-------------------------------------------------------------------------- // Trace paths //-------------------------------------------------------------------------- vector<SampleResult> sampleResults(1); sampleResults[0].type = PER_PIXEL_NORMALIZED; while (!boost::this_thread::interruption_requested()) { float alpha = 1.f; Ray eyeRay; const float screenX = min(sampler->GetSample(0) * filmWidth, (float)(filmWidth - 1)); const float screenY = min(sampler->GetSample(1) * filmHeight, (float)(filmHeight - 1)); camera->GenerateRay(screenX, screenY, &eyeRay, sampler->GetSample(2), sampler->GetSample(3)); int depth = 1; bool lastSpecular = true; float lastPdfW = 1.f; Spectrum radiance; Spectrum pathThrouput(1.f, 1.f, 1.f); BSDF bsdf; while (depth <= engine->maxPathDepth) { const unsigned int sampleOffset = sampleBootSize + (depth - 1) * sampleStepSize; RayHit eyeRayHit; Spectrum connectionThroughput; if (!scene->Intersect(device, false, sampler->GetSample(sampleOffset), &eyeRay, &eyeRayHit, &bsdf, &connectionThroughput)) { // Nothing was hit, look for infinitelight DirectHitInfiniteLight(lastSpecular, pathThrouput * connectionThroughput, eyeRay.d, lastPdfW, &radiance); if (depth == 1) alpha = 0.f; break; } pathThrouput *= connectionThroughput; // Something was hit // Check if it is a light source if (bsdf.IsLightSource()) { DirectHitFiniteLight(lastSpecular, pathThrouput, eyeRayHit.t, bsdf, lastPdfW, &radiance); } // Note: pass-through check is done inside SceneIntersect() //------------------------------------------------------------------ // Direct light sampling //------------------------------------------------------------------ DirectLightSampling(sampler->GetSample(sampleOffset + 1), sampler->GetSample(sampleOffset + 2), sampler->GetSample(sampleOffset + 3), sampler->GetSample(sampleOffset + 4), sampler->GetSample(sampleOffset + 5), pathThrouput, bsdf, depth, &radiance); //------------------------------------------------------------------ // Build the next vertex path ray //------------------------------------------------------------------ Vector sampledDir; BSDFEvent event; float cosSampledDir; const Spectrum bsdfSample = bsdf.Sample(&sampledDir, sampler->GetSample(sampleOffset + 6), sampler->GetSample(sampleOffset + 7), &lastPdfW, &cosSampledDir, &event); if (bsdfSample.Black()) break; lastSpecular = ((event & SPECULAR) != 0); if ((depth >= engine->rrDepth) && !lastSpecular) { // Russian Roulette const float prob = Max(bsdfSample.Filter(), engine->rrImportanceCap); if (sampler->GetSample(sampleOffset + 8) < prob) lastPdfW *= prob; else break; } pathThrouput *= bsdfSample * (cosSampledDir / lastPdfW); assert (!pathThrouput.IsNaN() && !pathThrouput.IsInf()); eyeRay = Ray(bsdf.hitPoint, sampledDir); ++depth; } assert (!radiance.IsNaN() && !radiance.IsInf()); sampleResults[0].screenX = screenX; sampleResults[0].screenY = screenY; sampleResults[0].radiance = radiance; sampleResults[0].alpha = alpha; sampler->NextSample(sampleResults); #ifdef WIN32 // Work around Windows bad scheduling renderThread->yield(); #endif } delete sampler; delete rndGen; //SLG_LOG("[PathCPURenderEngine::" << threadIndex << "] Rendering thread halted"); }