void LightCPURenderThread::TraceEyePath(Sampler *sampler, vector<SampleResult> *sampleResults) { LightCPURenderEngine *engine = (LightCPURenderEngine *)renderEngine; Scene *scene = engine->renderConfig->scene; PerspectiveCamera *camera = scene->camera; Film *film = threadFilm; const u_int filmWidth = film->GetWidth(); const u_int filmHeight = film->GetHeight(); // Sample offsets const u_int sampleBootSize = 11; const u_int sampleEyeStepSize = 3; 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(9), sampler->GetSample(10)); Spectrum radiance, eyePathThroughput(1.f, 1.f, 1.f); int depth = 1; while (depth <= engine->maxPathDepth) { const u_int sampleOffset = sampleBootSize + (depth - 1) * sampleEyeStepSize; RayHit eyeRayHit; BSDF bsdf; Spectrum connectionThroughput; const bool somethingWasHit = scene->Intersect(device, false, sampler->GetSample(sampleOffset), &eyeRay, &eyeRayHit, &bsdf, &connectionThroughput); if (!somethingWasHit) { // Nothing was hit, check infinite lights (including sun) const Spectrum throughput = eyePathThroughput * connectionThroughput; if (scene->envLight) radiance += throughput * scene->envLight->GetRadiance(scene, -eyeRay.d); if (scene->sunLight) radiance += throughput * scene->sunLight->GetRadiance(scene, -eyeRay.d); break; } else { // Something was hit, check if it is a light source if (bsdf.IsLightSource()) radiance = eyePathThroughput * connectionThroughput * bsdf.GetEmittedRadiance(scene); else { // Check if it is a specular bounce float bsdfPdf; Vector sampledDir; BSDFEvent event; float cosSampleDir; const Spectrum bsdfSample = bsdf.Sample(&sampledDir, sampler->GetSample(sampleOffset + 1), sampler->GetSample(sampleOffset + 2), &bsdfPdf, &cosSampleDir, &event); if (bsdfSample.Black() || ((depth == 1) && !(event & SPECULAR))) break; // If depth = 1 and it is a specular bounce, I continue to trace the // eye path looking for a light source eyePathThroughput *= connectionThroughput * bsdfSample * (cosSampleDir / bsdfPdf); assert (!eyePathThroughput.IsNaN() && !eyePathThroughput.IsInf()); eyeRay = Ray(bsdf.hitPoint, sampledDir); } ++depth; } } // Add a sample even if it is black in order to avoid aliasing problems // between sampled pixel and not sampled one (in PER_PIXEL_NORMALIZED buffer) AddSampleResult(*sampleResults, PER_PIXEL_NORMALIZED, screenX, screenY, radiance, (depth == 1) ? 1.f : 0.f); }
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"); }
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"); }
double PathHybridState::CollectResults(HybridRenderThread *renderThread) { PathHybridRenderThread *thread = (PathHybridRenderThread *)renderThread; PathHybridRenderEngine *renderEngine = (PathHybridRenderEngine *)thread->renderEngine; Scene *scene = renderEngine->renderConfig->scene; // Was a shadow ray traced ? if (!directLightRadiance.Black()) { // It was, check the result const Ray *shadowRay; const RayHit *shadowRayHit; thread->PopRay(&shadowRay, &shadowRayHit); BSDF bsdf; // "(depth - 1 - 1)" is used because it is a shadow ray of previous path vertex const unsigned int sampleOffset = sampleBootSize + (depth - 1 - 1) * sampleStepSize; const bool miss = FinalizeRay(thread, shadowRay, shadowRayHit, &bsdf, sampler->GetSample(sampleOffset + 5), &directLightRadiance); if (miss) { // The light source is visible, add the direct light sampling component sampleResults[0].radiance += directLightRadiance; } } // The next path vertex ray const Ray *ray; const RayHit *rayHit; thread->PopRay(&ray, &rayHit); BSDF bsdf; const unsigned int sampleOffset = sampleBootSize + (depth - 1) * sampleStepSize; const bool miss = FinalizeRay(thread, ray, rayHit, &bsdf, sampler->GetSample(sampleOffset), &throuput); if (miss) { // Nothing was hit, look for infinitelight DirectHitInfiniteLight(scene, ray->d); if (depth == 1) sampleResults[0].alpha = 0.f; SplatSample(thread); return 1.0; } else { // Something was hit // Check if a light source was hit if (bsdf.IsLightSource()) DirectHitFiniteLight(scene, rayHit->t, bsdf); // Direct light sampling (initialize directLightRay) DirectLightSampling(thread, sampler->GetSample(sampleOffset + 1), sampler->GetSample(sampleOffset + 2), sampler->GetSample(sampleOffset + 3), sampler->GetSample(sampleOffset + 4), bsdf); // 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()) { SplatSample(thread); return 1.0; } else { lastSpecular = ((event & SPECULAR) != 0); if ((depth >= renderEngine->rrDepth) && !lastSpecular) { // Russian Roulette const float prob = RenderEngine::RussianRouletteProb(bsdfSample, renderEngine->rrImportanceCap); if (sampler->GetSample(sampleOffset + 8) < prob) lastPdfW *= prob; else { SplatSample(thread); return 1.0; } } ++depth; // Check if I have reached the max. path depth if (depth > renderEngine->maxPathDepth) { SplatSample(thread); return 1.0; } else { throuput *= bsdfSample * (cosSampledDir / lastPdfW); assert (!throuput.IsNaN() && !throuput.IsInf()); nextPathVertexRay = Ray(bsdf.hitPoint.p, sampledDir); } } } return 0.0; }