raytrace.cpp

Go to the documentation of this file.
00001 
00002 
00003 
00004 
00005 
00006 
00007 #include <Magick++.h>
00008 #include <cstdlib>
00009 #include <ctime>
00010 #include <dlfcn.h>
00011 #include <getopt.h>
00012 #include <glob.h>
00013 #include <iostream>
00014 #include <ncurses.h>
00015 #include <unistd.h>
00016 #include <signal.h>
00017 #include <string>
00018 
00019 #include "raytrace_defs.h"
00020 #include "renderable.h"
00021 #include "ray.h"
00022 #include "scene.h"
00023 #include "color.h"
00024 
00025 using std::vector;
00026 using std::cout;
00027 using std::endl;
00028 using std::string;
00029 using Magick::Image;
00030 using Magick::Color;
00031 using Magick::ColorRGB;
00032 
00033 struct raytrace_info rt_info;
00034 
00035 void trace_ray(ColorRGB &pixel, const Ray &ray, int depth);
00036 
00041 //{{{
00042 unsigned long trace_rays(Image & img, Point3D eye) {
00043     //The image canvas is located around the origin of world space coordinates (0,0,0)
00044     double min_x, max_x;
00045     double min_y, max_y;
00046     Scene * scene = Scene::get_instance();
00047 
00048     max_x = 16;
00049     min_x = -max_x;
00050     max_y = 16 * ((double)scene->get_viewport_pixel_height() / (double)scene->get_viewport_pixel_width());
00051     min_y = -max_y;
00052 
00053     // dx and dy are the amount to add to each pixel to go to the next pixel
00054     double dx = (max_x - min_x) / scene->get_viewport_pixel_width();
00055     double dy = (max_y - min_y) / scene->get_viewport_pixel_height();
00056 
00057     // Start out in the middle of the top-most left-most pixel.
00058     double start_x = min_x + (dx * 0.5);
00059     double start_y = min_y + (dy * 0.5);
00060 
00065 
00066     // This is where the ray will intersect the viewing plane, the window that the 'eye' is
00067     // looking out of.
00068     Point3D screen_intersection(start_x, start_y, 0.0);
00069     rt_info.rendered_pixels = 0;
00070     for (int y = 0; y < scene->get_viewport_pixel_height(); ++y) {
00071         screen_intersection.x = start_x;
00072         for (int x = 0; x < scene->get_viewport_pixel_width(); ++x) {
00073             rt_info.rendered_pixels++;
00074 
00075             // Shoot a ray through the center of the pixel and the next pixel
00076             // and compare the color values, if they match, there is no need
00077             // to subdivide the pixel.  If they don't match, then subdivide.
00078             double modifier = 1.0;
00079             if(x + 1 == scene->get_viewport_pixel_width()) {
00080                 modifier = -1.0;
00081             }
00082 
00083             ColorRGB color;
00084             Point3D p = Point3D(screen_intersection.x, screen_intersection.y, screen_intersection.z);
00085             Ray ray(eye, p - eye);
00086             trace_ray(color, ray, 0);
00087 
00088             p = Point3D(screen_intersection.x + (dx * modifier), screen_intersection.y, screen_intersection.z);
00089             ColorRGB next_color;
00090             Ray next_ray(eye, p - eye);
00091             trace_ray(next_color, next_ray, 0);
00092 
00093             if(!(color == next_color)) {
00094                 vector<ColorRGB> colors;
00095                 // Implement stochastic anti-aliasing.  Which is just a fancy way
00096                 // to say break up the pixel into subpixels, determine the center
00097                 // point of the subpixel, add a random offset and shoot a ray
00098                 // through it.
00099 
00100                 // dx and dy are the distance from the center of this pixel to the center of the next
00101                 // pixel.  dx/2 and dy/2 are the distance from the center of this pixel to the boundary
00102                 // of this pixel.  So the pixel is bounded by (x - dx/2, y - dy/2) and (x + dx/2, y + dy/2).
00103                 //
00104                 // To break this pixel further into subpixels, start at (x-dx/2, y - dy/2), or the top
00105                 // left corner of the pixel.  To this add dx/2 divided by the number of x subpixels, and
00106                 // dy/2 divided by the number of y subpixels.  This determines the center of the top
00107                 // most subpixel.  From that point, add dx/number of x subpixels to reach each
00108                 // successive pixel.
00109 
00110                 // Determine the subpixel dx and dy values, these are used to
00111                 // shift the screen intersection within each pixel
00112                 double sdx = dx / (2 * scene->get_subpixel_sqrt());
00113                 double sdy = dy / (2 * scene->get_subpixel_sqrt());
00114 
00115                 // Determine the y-coordinate for the top most subpixel.
00116                 double sy = screen_intersection.y - (dy * 0.5) + (sdy * 0.5);
00117                 for (int i = 0; i < scene->get_subpixel_sqrt(); i++) {
00118 
00119                     // Determine the x-coordinate for the left most subpixel.
00120                     double sx = screen_intersection.x - (dx * 0.5) + (sdx * 0.5);
00121                     for (int j = 0; j < scene->get_subpixel_sqrt(); j++) {
00122                         double sy_jitter = sy + jitter(sdy);
00123                         double sx_jitter = sx + jitter(sdx);
00124 
00125                         // THIS IS NOT FOCAL LENGTH!  DON'T CHANGE BACK.
00126                         Point3D subpixel(sx_jitter, sy_jitter, screen_intersection.z);
00127 
00128                         // Calculate the ray from the eye to the point where it
00129                         // intersect the viewport.
00130                         Ray ray(eye, subpixel - eye);
00131 
00132                         // Trace the ray into the scene, recording the pixel's color value.
00133                         ColorRGB color;
00134                         trace_ray(color, ray, 0);
00135                         colors.push_back(color);
00136 
00137                         sx += sdx;
00138                     }
00139                     sy += sdy;
00140                 }
00141 
00142                 // Average all the colors to get the color value at the window.
00143                 double red, green, blue;
00144                 red = green = blue = 0.0;
00145                 vector<ColorRGB>::iterator iter, end;
00146                 for (iter = colors.begin(), end = colors.end(); iter != end; iter++) {
00147                     red   += iter->red();
00148                     green += iter->green();
00149                     blue  += iter->blue();
00150                 }
00151                 int colors_size = colors.size();
00152                 color = ColorRGB((red / colors_size), (green / colors_size), (blue / colors_size));
00153             }
00154 
00155             img.pixelColor(x, y, color);
00156 
00157             screen_intersection.x += dx;
00158         }
00159 
00160         screen_intersection.y += dy;
00161     }
00162 
00163     return rt_info.traced_rays;
00164 }
00165 //}}}
00166 
00172 //{{{
00173 void trace_ray(ColorRGB &pixel, const Ray &ray, int depth) {
00174     Renderable * primitive = NULL;
00175     double dist = INF;
00176     Scene * scene = Scene::get_instance();
00177     int max_depth = scene->get_max_recurse_depth();
00178 
00179     if (depth <= max_depth) {
00180 
00181         if ( depth == 0 ) {
00182             rt_info.primary_rays++;
00183         }
00184 
00185         // Track statistics.    {{{
00186         rt_info.traced_rays++;
00187         if (rt_info.traced_rays % REPORT_FACTOR == 0) {
00188             int x, y;
00189             getyx(stdscr, y, x);
00190             long t = (long)difftime(time(NULL), rt_info.start_time);
00191             mvprintw(y - 1, 0, "Elapsed time:  %02i:%02i:%02i", t / 3600, (t / 60) % 60, t % 60);
00192             mvprintw(y, x + 2, "%02.2f%% done", 100 * (double)(rt_info.rendered_pixels) / (double)(scene->get_viewport_pixel_width() * scene->get_viewport_pixel_height()));
00193             move(y, x);
00194             refresh();
00195         }
00196         //}}}
00197 
00198         if ((primitive = scene->find_collision(ray, dist)) != NULL) {
00199             Vector dir = ray.direction();
00200             Point3D intersection_point = ray.origin() + (dir * dist);
00201             Vector reflect;
00202             Vector refract;
00203 
00204             // Determine the main color from directly striking the object.  {{{
00205             pixel += primitive->get_color_contribution(intersection_point, ray, reflect, refract);
00206             //}}}
00207             // Reflect the ray, if the surface is reflective.   {{{
00208             double reflection_coefficient = primitive->get_reflection(intersection_point);
00209 
00210             //TODO we seem to be missing an important part: applying the coefficient.
00211             if (reflection_coefficient > 0.0) {
00212                 depth++;
00213                 trace_ray(pixel, Ray(intersection_point, reflect), depth);
00214                 depth--;
00215             }
00216             //}}}
00217             // Trace the refracted ray, if the primitive is transparent.    {{{
00218             if (primitive->get_opacity(intersection_point) > OPAQUE) {
00219                 depth++;
00220                 trace_ray(pixel, Ray(intersection_point, refract), depth);
00221                 depth--;
00222             }
00223             //}}}
00224         }
00225     }
00226 }
00227 //}}}
00228 
00230 //{{{
00231 void print_stats(string fname, int elapsed, long primary_rays, long traced_rays) {
00232     int hours, minutes, seconds;
00233     seconds = elapsed;
00234     hours = seconds / 3600;
00235     seconds %= 3600;
00236     minutes = seconds / 60;
00237     seconds %= 60;
00238     cout << endl;
00239     cout << endl;
00240     cout << "Traced " << rt_info.traced_rays << " light rays into the scene!" << endl;
00241     cout << endl;
00242     cout << endl;
00243 
00244     printf("Rendering %s took %i seconds (%02i:%02i:%02i)\n", fname.c_str(), elapsed, hours, minutes, seconds);
00245 
00246     cout.setf(cout.fixed);
00247     cout.precision(5);
00248     cout << "Average rays per primary ray      :  " << (double)traced_rays / (double)primary_rays << endl;
00249     cout << "Average time per " << REPORT_FACTOR << " primary rays:  " << ((double)elapsed / (double)primary_rays) * REPORT_FACTOR << "s" << endl;
00250     cout << "Average time per " << REPORT_FACTOR << " rays        :  " << ((double)elapsed / (double)traced_rays ) * REPORT_FACTOR << "s" << endl;
00251 }
00252 //}}}
00253 
00255 //{{{
00256 void load_plugins() {
00257     glob_t glob_data;
00258     glob("plugins/*.so", 0, NULL, &glob_data);
00259 
00260     char ** iter = glob_data.gl_pathv;
00261     for (; *iter != NULL ; ++iter) {
00262         // No need to keep the handle to the shared object;
00263         // we don't need to get any symbols from it 
00264         log_info("Attempting to load plugin '%s'...\n", *iter);
00265         printf("Attempting to load plugin '%s'...\n", *iter);
00266         if(dlopen(*iter, RTLD_NOW)) {
00267             log_info("Finished loading plugin '%s'\n", *iter);
00268             printf("Finished loading plugin '%s'\n", *iter);
00269         }
00270         else {
00271             log_crit("Failed to load plugin '%s' with error %s\n", *iter, dlerror());
00272             exit_mbrt(EXIT_FAILURE);
00273         }
00274     }
00275 
00276     globfree(&glob_data);
00277 }
00278 //}}}
00279 
00282 //{{{
00283 void register_signal_handlers() {
00284     struct sigaction sigact;
00285     sigact.sa_handler = exit_mbrt;
00286     sigaction(15, &sigact, NULL);
00287     //sigaction(11, &sigact, NULL);
00288 }
00289 //}}}
00290 
00291 //{{{
00292 int main(int argc, char ** argv) {
00293     time_t start_time = time(NULL);
00294     string filename("scene.xml");
00295     srand(time(NULL));
00296 
00297     openlog("mbrt", LOG_CONS, LOG_SYSLOG);
00298     log_info("******************  Starting mbrt  ******************\n");
00299 
00300     // Do initialization stuff.
00301     register_signal_handlers();
00302     load_plugins();
00303 
00304 
00305     // Process command line arguments.
00306     extern char *optarg;
00307     extern int optind, opterr, optopt;
00308     int option_index = 0;
00309     static struct option long_options[] = {
00310         {"scene",  1, NULL, 's'},
00311         {"output", 1, NULL, 'o'},
00312         {0, 0, 0, 0}
00313     };
00314 
00315     log_info("5*****************  Starting mbrt  ******************\n");
00316     string outfname("");
00317     int opt_val = -2;
00318     while ( (opt_val = getopt_long (argc, argv, "s:o:", long_options, &option_index)) != -1 ) {
00319         switch (opt_val) {
00320         case 's':
00321             filename = optarg;
00322             break;
00323         case 'o':
00324             outfname = optarg;
00325             break;
00326         }
00327     }
00328     log_info("4*****************  Starting mbrt  ******************\n");
00329     unsigned long traced_rays;
00330     int x, y;
00331     initscr();
00332     getmaxyx(stdscr, y, x);
00333     mvprintw(y - 1, 0, "Tracing %s...", filename.c_str());
00334 
00335     log_info("3*****************  Starting mbrt  ******************\n");
00336 
00338     // Render //
00340     // Read the scene description XML file and build the Scene object.
00341     log_info("3.9*****************  Starting mbrt  ******************\n");
00342     Scene * scene = Scene::get_instance(filename);
00343     log_info("3.8*****************  Starting mbrt  ******************\n");
00344     if(outfname == "") {
00345     log_info("3.7*****************  Starting mbrt  ******************\n");
00346         outfname = scene->get_output_filename();
00347     }
00348     log_info("3.6*****************  Starting mbrt  ******************\n");
00349 
00350     log_info("2*****************  Starting mbrt  ******************\n");
00351 
00352     // Create the Image object.
00353     Image img(scene->get_geometry(), "red");
00354     log_info("1*****************  Starting mbrt  ******************\n");
00355 
00356     // Trace the scene.
00357     traced_rays = trace_rays(img, scene->get_camera());
00358 
00359     // Save the image.
00360     img.write(outfname);
00361 
00362 
00363     // Do post render stuff.
00364     time_t end_time = time(NULL);
00365     int elapsed = end_time - start_time;
00366     int primary_rays = 4 * scene->get_viewport_pixel_width() * scene->get_viewport_pixel_height();
00367     endwin();
00368     cout << outfname << endl;
00369     print_stats(filename, elapsed, primary_rays, traced_rays);
00370 
00371     log_info("******************  Exiting mbrt  ******************\n");
00372     closelog();
00373     exit(EXIT_SUCCESS);
00374 }
00375 //}}}

Generated on Tue Oct 30 22:12:15 2007 for mbrt by  doxygen 1.5.2