// performs a free path sampling, aka. Woodcock tracking FreePathResult FreePathSampling(const Ray& ray, Flow& flow) { // initialize result data structure to 'no hit' (d > dmax). FreePathResult res; res.d = std::numeric_limits<double>::max(); res.dmax = 0; res.ftle = 0; // first, test if the ray hits the domain at all (hits are sorted, thus if global_hit.y<0 then both are negative) Vec2d global_hit; if (!flow.IntersectRay(ray, global_hit) || global_hit.y < 0) return res; // set extinction coefficient (global upper bound) and entry and exit of the run double km = majorantExtinction; double dmin = std::max(0.0,global_hit.x); res.dmax = global_hit.y; // perform the free path run (Alg. 1) res.d = dmin - log(1-rnd(rng)) / km; while (res.d <= res.dmax && ExtinctionCoefficient(res.ftle = flow.SampleFTLE(ray.Origin + res.d * ray.Direction, startTime, endTime, stepSize, Vec3d(separationDistance))) / km < rnd(rng)) res.d = res.d - log(1-rnd(rng)) / km; return res; }
// ============================================================= // ============================================================= // ============================================================= int main() { printf("Small MCFTLE: Computing...\n"); // ============================================================= // initialize // ============================================================= Vec3d eye(1,0.5,2.3), lookAt(1,0.5,0.5), up(0,1,0); // camera parameters double fov = 45.0; Camera camera(eye, lookAt, up, fov, screenResolution); // pinhole camera Frame frame(screenResolution); // stores pixel statistics Flow flow; // vector field Timer timer; // timer to measure runtime // ============================================================= // progressive rendering // ============================================================= for (int it=0; it<maxIterationsPerPixel; ++it) { timer.tic(); // start runtime measurement #ifdef NDEBUG #pragma omp parallel for schedule(dynamic,16) #endif for (int px=0; px<screenResolution.x*screenResolution.y; ++px) { // select pixel and generate a ray Vec2i pxCoord(px%screenResolution.x, px/screenResolution.x); Ray viewRay = camera.GenerateRay(pxCoord); // intersect ray with domain (entry and exit) Vec2d t; if (!flow.IntersectRay(viewRay, t)) continue; // free path sampling to find a scattering event FreePathResult fprView = FreePathSampling(viewRay, flow); Vec3d radiance(1,1,1); // initialize with radiance of the background (white) // if we found a scattering event if (fprView.d <= fprView.dmax) { // compute the scattering albedo c Vec3d albedo = ScatteringAlbedo(fprView.ftle); // compute scattering location x_s and generate shadow ray Vec3d x_s = viewRay.Origin + fprView.d * viewRay.Direction; Ray shadowRay(x_s, -lightDirection); // intersect shadow ray with domain (determine exit) if (!flow.IntersectRay(shadowRay, t)) continue; // free path sampling to find a scattering event on the shadow ray FreePathResult fprLight = FreePathSampling(shadowRay, flow); // if we have scattered, we are in shadow. (actually visibility would be zero now, but for vis purposes we keep a little bit.) double visibility = 1; if (fprLight.d <= fprLight.dmax) visibility = visibility_minimum; // phase function double phase = 1.0 / (4.0 * M_PI); // isotropic phase function // compute in-scattered radiance radiance = albedo * phase * visibility * lightRadiance; } // add the result to the pixel statistics frame.AddPixel(pxCoord, radiance); } timer.toc(); // finish runtime measurement (prints result to console) // write intermediate result to file frame.ExportPfm("result.pfm"); frame.ExportBmp("result.bmp", contrast, brightness, gamma_val); printf("Iteration: %i / %i\n", it+1, maxIterationsPerPixel); } printf("\nComplete!\n"); return 0; }