diff --git a/main.cpp b/main.cpp index 041cb04..2d13611 100644 --- a/main.cpp +++ b/main.cpp @@ -28,27 +28,39 @@ using namespace std; using namespace glm; +//////////////////////////////////////////// // Function prototypes. +//////////////////////////////////////////// static void scene_1(vector
& vf, vector & vl, mat4x4 & i_model_view); static void scene_2(vector
& vf, vector & vl, mat4x4 & i_model_view); static void scene_3(vector
& vf, vector & vl, mat4x4 & i_model_view); static void scene_4(vector
& vf, vector & vl, mat4x4 & i_model_view); static void print_usage(char ** const argv); +static void parse_args(int argc, char ** const argv); +//////////////////////////////////////////// // Constants. +//////////////////////////////////////////// static const char * OUT_FILE = "output.png"; +//////////////////////////////////////////// // Global variables. +//////////////////////////////////////////// typedef enum TRACERS { NONE, WHITTED, MONTE_CARLO } tracer_t; -static char * input_file; +static char * g_input_file; +static char * g_out_file_name = NULL; static int g_samples = 25; static float g_fov = 45.f; static int g_w = 640; static int g_h = 480; static vec3 ** image; static tracer_t g_tracer = NONE; +static unsigned int g_max_depth = 5; +//////////////////////////////////////////// +// Main function. +//////////////////////////////////////////// int main(int argc, char ** argv) { Ray r; vec2 sample; @@ -63,94 +75,9 @@ int main(int argc, char ** argv) { FREE_IMAGE_FORMAT fif; BYTE * bits; int bpp; - int opt; - int x_pos; - char * out_file_name = NULL; - - // Check command line arguments. - if(argc == 1) { - print_usage(argv); - return EXIT_FAILURE; - } - - while((opt = getopt(argc, argv, ":t:s:w:f:o:")) != -1) { - switch (opt) { - case 't': - if (strcmp("whitted", optarg) == 0 ) - g_tracer = WHITTED; - else if(strcmp("monte_carlo", optarg) == 0) - g_tracer = MONTE_CARLO; - else { - cerr << "Invalid ray tracer: " << optarg << endl; - print_usage(argv); - return EXIT_FAILURE; - } - - break; - - case 'w': - for (x_pos = 0; optarg[x_pos]; x_pos++) - if (optarg[x_pos] == 'x') - break; - - if (optarg[x_pos] == '\0') { - cerr << "Invalid screen resolution: " << optarg << endl; - print_usage(argv); - return EXIT_FAILURE; - } else { - optarg[x_pos] = '\0'; - g_w = atoi(optarg); - g_h = atoi(&optarg[x_pos + 1]); - if (g_w <= 0 || g_h <= 0) { - cerr << "Invalid screen resolution: " << optarg << endl; - print_usage(argv); - return EXIT_FAILURE; - } - } - - break; - - case 's': - g_samples = atoi(optarg); - if (g_samples == 0) { - cerr << "Samples per pixel must be a positive integer." << endl; - print_usage(argv); - return EXIT_FAILURE; - } - - break; - - case 'o': - out_file_name = (char*)malloc((strlen(optarg) + 1) * sizeof(char)); - strcpy(out_file_name, optarg); - - break; - - case 'f': - g_fov = atof(optarg); - if (g_fov < 1.0f) { - cerr << "FoV must be greater than or equal to 1.0 degrees." << endl; - print_usage(argv); - return EXIT_FAILURE; - } - - break; - - case ':': - cerr << "Option \"-" << static_cast(optopt) << "\" requires an argument." << endl; - print_usage(argv); - return EXIT_FAILURE; - - break; - - case '?': - default: - cerr << "Unrecognized option: \"-" << static_cast(optopt) << "\"." << endl; - } - } - - input_file = argv[optind]; + parse_args(argc, argv); + // Initialize everything. FreeImage_Initialise(); @@ -162,9 +89,9 @@ int main(int argc, char ** argv) { scene_2(figures, lights, i_model_view); if (g_tracer == WHITTED) - tracer = static_cast(new WhittedTracer(g_h, g_w, g_fov)); + tracer = static_cast(new WhittedTracer(g_h, g_w, g_fov, g_max_depth)); else if(g_tracer == MONTE_CARLO) - tracer = static_cast(new PathTracer(g_h, g_w, g_fov)); + tracer = static_cast(new PathTracer(g_h, g_w, g_fov, g_max_depth)); else { cerr << "Must specify a ray tracer with \"-t\"." << endl; print_usage(argv); @@ -206,13 +133,13 @@ int main(int argc, char ** argv) { } // Save the output image. - fif = FreeImage_GetFIFFromFilename(out_file_name != NULL ? out_file_name : OUT_FILE); - FreeImage_Save(fif, output_bitmap, out_file_name != NULL ? out_file_name : OUT_FILE); + fif = FreeImage_GetFIFFromFilename(g_out_file_name != NULL ? g_out_file_name : OUT_FILE); + FreeImage_Save(fif, output_bitmap, g_out_file_name != NULL ? g_out_file_name : OUT_FILE); FreeImage_Unload(output_bitmap); // Clean up. - if (out_file_name != NULL) - free(out_file_name); + if (g_out_file_name != NULL) + free(g_out_file_name); delete tracer; @@ -235,23 +162,128 @@ int main(int argc, char ** argv) { return EXIT_SUCCESS; } -static void print_usage(char ** const argv) { +//////////////////////////////////////////// +// Helper functions. +//////////////////////////////////////////// +void print_usage(char ** const argv) { cerr << "USAGE: " << argv[0] << " [OPTIONS]... FILE" << endl; - cerr << "Renders the scene specified by the scene file FILE." << endl; + cerr << "Renders the scene specified by the scene file FILE." << endl << endl; cerr << "Mandatory options: " << endl; cerr << " -t\tRay tracing method to use." << endl; - cerr << " \tValid values: \"whitted\" \"monte_carlo\"." << endl; - cerr << "Optional options:" << endl; - cerr << " -o\t Output image file name with extension." << endl; + cerr << " \tValid values: \"whitted\" \"monte_carlo\"." << endl << endl; + cerr << "Extra options:" << endl; + cerr << " -o\tOutput image file name with extension." << endl; + cerr << " \tDefaults to \"output.png\"." << endl; cerr << " -f\tField of view to use in degrees." << endl; cerr << " \tDefaults to 45.0 degrees." << endl; cerr << " -s\tNumber of samples per pixel." << endl; cerr << " \tDefaults to 25 samples." << endl; cerr << " -w\tImage size in pixels as \"WIDTHxHEIGHT\"." << endl; cerr << " \tDefaults to 640x480 pixels." << endl; + cerr << " -r\tMaxmimum recursion depth." << endl; + cerr << " \tDefaults to 5." << endl; } -static void scene_1(vector
& vf, vector & vl, mat4x4 & i_model_view) { +void parse_args(int argc, char ** const argv) { + int opt; + int x_pos; + + // Check command line arguments. + if(argc == 1) { + print_usage(argv); + exit(EXIT_FAILURE); + } + + while((opt = getopt(argc, argv, ":t:s:w:f:o:r:")) != -1) { + switch (opt) { + case 't': + if (strcmp("whitted", optarg) == 0 ) + g_tracer = WHITTED; + else if(strcmp("monte_carlo", optarg) == 0 || strcmp("montecarlo", optarg) == 0) + g_tracer = MONTE_CARLO; + else { + cerr << "Invalid ray tracer: " << optarg << endl; + print_usage(argv); + exit(EXIT_FAILURE); + } + + break; + + case 'w': + for (x_pos = 0; optarg[x_pos]; x_pos++) + if (optarg[x_pos] == 'x') + break; + + if (optarg[x_pos] == '\0') { + cerr << "Invalid screen resolution: " << optarg << endl; + print_usage(argv); + exit(EXIT_FAILURE); + } else { + optarg[x_pos] = '\0'; + g_w = atoi(optarg); + g_h = atoi(&optarg[x_pos + 1]); + if (g_w <= 0 || g_h <= 0) { + cerr << "Invalid screen resolution: " << optarg << endl; + print_usage(argv); + exit(EXIT_FAILURE); + } + } + + break; + + case 's': + g_samples = atoi(optarg); + if (g_samples <= 0) { + cerr << "Samples per pixel must be a positive integer." << endl; + print_usage(argv); + exit(EXIT_FAILURE); + } + + break; + + case 'o': + g_out_file_name = (char*)malloc((strlen(optarg) + 1) * sizeof(char)); + strcpy(g_out_file_name, optarg); + + break; + + case 'f': + g_fov = atof(optarg); + if (g_fov < 1.0f) { + cerr << "FoV must be greater than or equal to 1.0 degrees." << endl; + print_usage(argv); + exit(EXIT_FAILURE); + } + + break; + + case 'r': + g_max_depth = static_cast(abs(atoi(optarg))); + if (g_max_depth == 0) { + cerr << "Recursion depth must be a positive integer." << endl; + print_usage(argv); + exit(EXIT_FAILURE); + } + + break; + + case ':': + cerr << "Option \"-" << static_cast(optopt) << "\" requires an argument." << endl; + print_usage(argv); + exit(EXIT_FAILURE); + + break; + + case '?': + default: + cerr << "Unrecognized option: \"-" << static_cast(optopt) << "\"." << endl; + } + } + + g_input_file = argv[optind]; +} + +void scene_1(vector
& vf, vector & vl, mat4x4 & i_model_view) { Sphere * s; Plane * p; Disk * d; @@ -326,7 +358,7 @@ static void scene_1(vector
& vf, vector & vl, mat4x4 & i_mode vl.push_back(static_cast(l)); } -static void scene_2(vector
& vf, vector & vl, mat4x4 & i_model_view) { +void scene_2(vector
& vf, vector & vl, mat4x4 & i_model_view) { Sphere * s; Plane * p; Disk * d; @@ -394,7 +426,7 @@ static void scene_2(vector
& vf, vector & vl, mat4x4 & i_mode vl.push_back(static_cast(l)); } -static void scene_3(vector
& vf, vector & vl, mat4x4 & i_model_view) { +void scene_3(vector
& vf, vector & vl, mat4x4 & i_model_view) { Sphere * s; Plane * p; DirectionalLight * l; @@ -461,7 +493,7 @@ static void scene_3(vector
& vf, vector & vl, mat4x4 & i_mode i_model_view = inverse(lookAt(eye, center, up)); } -static void scene_4(vector
& vf, vector & vl, mat4x4 & i_model_view) { +void scene_4(vector
& vf, vector & vl, mat4x4 & i_model_view) { Sphere * s; Plane * p; diff --git a/output.png b/output.png index e7da965..96b8073 100644 Binary files a/output.png and b/output.png differ diff --git a/path_tracer.cpp b/path_tracer.cpp index 72d2d87..baccbd7 100644 --- a/path_tracer.cpp +++ b/path_tracer.cpp @@ -58,7 +58,7 @@ vec3 PathTracer::trace_ray(Ray & r, vector
& v_figures, vector & v_figures, vectorm_mat.m_diffuse / pi())) + (_f->m_mat.m_diffuse * dir_spec_color); // Determine the specular reflection color. - if (_f->m_mat.m_rho > 0.0f && rec_level < MAX_RECURSION) { + if (_f->m_mat.m_rho > 0.0f && rec_level < m_max_depth) { rr = Ray(normalize(reflect(r.m_direction, n)), i_pos + n * BIAS); color += _f->m_mat.m_rho * trace_ray(rr, v_figures, v_lights, rec_level + 1); - } else if (_f->m_mat.m_rho > 0.0f && rec_level >= MAX_RECURSION) + } else if (_f->m_mat.m_rho > 0.0f && rec_level >= m_max_depth) return vec3(0.0f); } else { @@ -102,17 +102,17 @@ vec3 PathTracer::trace_ray(Ray & r, vector
& v_figures, vectorm_mat.m_ref_index); // Determine the specular reflection color. - if (kr > 0.0f && rec_level < MAX_RECURSION) { + if (kr > 0.0f && rec_level < m_max_depth) { rr = Ray(normalize(reflect(r.m_direction, n)), i_pos + n * BIAS); color += kr * trace_ray(rr, v_figures, v_lights, rec_level + 1); - } else if (rec_level >= MAX_RECURSION) + } else if (rec_level >= m_max_depth) return vec3(0.0f); // Determine the transmission color. - if (_f->m_mat.m_refract && kr < 1.0f && rec_level < MAX_RECURSION) { + if (_f->m_mat.m_refract && kr < 1.0f && rec_level < m_max_depth) { rr = Ray(normalize(refract(r.m_direction, n, r.m_ref_index / _f->m_mat.m_ref_index)), i_pos - n * BIAS, _f->m_mat.m_ref_index); color += (1.0f - kr) * trace_ray(rr, v_figures, v_lights, rec_level + 1); - } else if (rec_level >= MAX_RECURSION) + } else if (rec_level >= m_max_depth) return vec3(0.0f); } diff --git a/path_tracer.hpp b/path_tracer.hpp index 6541301..cde6f8a 100644 --- a/path_tracer.hpp +++ b/path_tracer.hpp @@ -8,7 +8,7 @@ class PathTracer: public Tracer { public: PathTracer(): Tracer() { } - PathTracer(int h, int w, float fov): Tracer(h, w, fov) { }; + PathTracer(int h, int w, float fov, unsigned int max_depth): Tracer(h, w, fov, max_depth) { }; virtual ~PathTracer(); diff --git a/tracer.cpp b/tracer.cpp index 7a91bb5..422c47c 100644 --- a/tracer.cpp +++ b/tracer.cpp @@ -6,6 +6,8 @@ using namespace glm; +const float BIAS = 0.000001f; + const vec3 BCKG_COLOR = vec3(1.0f); float Tracer::random01() const { diff --git a/tracer.hpp b/tracer.hpp index 0ef6957..7deafe3 100644 --- a/tracer.hpp +++ b/tracer.hpp @@ -14,8 +14,7 @@ using std::vector; using glm::vec2; using glm::vec3; -#define MAX_RECURSION 3 -#define BIAS 0.000001f +extern const float BIAS; extern const vec3 BCKG_COLOR; @@ -25,10 +24,11 @@ public: int m_w; float m_fov; float m_a_ratio; + unsigned int m_max_depth; - Tracer(): m_h(480), m_w(640), m_fov(90.0f), m_a_ratio(640.0f / 480.0f) { } + Tracer(): m_h(480), m_w(640), m_fov(90.0f), m_a_ratio(640.0f / 480.0f), m_max_depth(5) { } - Tracer(int h, int w, float fov): m_h(h), m_w(w), m_fov(fov) { + Tracer(int h, int w, float fov, unsigned int max_depth): m_h(h), m_w(w), m_fov(fov), m_max_depth(max_depth) { m_a_ratio = static_cast(w) / static_cast(h); }; diff --git a/whitted_tracer.cpp b/whitted_tracer.cpp index b2f4e3b..0e6f81c 100644 --- a/whitted_tracer.cpp +++ b/whitted_tracer.cpp @@ -58,10 +58,10 @@ vec3 WhittedTracer::trace_ray(Ray & r, vector
& v_figures, vectorm_mat.m_diffuse / pi())) + (_f->m_mat.m_diffuse * dir_spec_color); // Determine the specular reflection color. - if (_f->m_mat.m_rho > 0.0f && rec_level < MAX_RECURSION) { + if (_f->m_mat.m_rho > 0.0f && rec_level < m_max_depth) { rr = Ray(normalize(reflect(r.m_direction, n)), i_pos + n * BIAS); color += _f->m_mat.m_rho * trace_ray(rr, v_figures, v_lights, rec_level + 1); - } else if (_f->m_mat.m_rho > 0.0f && rec_level >= MAX_RECURSION) + } else if (_f->m_mat.m_rho > 0.0f && rec_level >= m_max_depth) return vec3(0.0f); } else { @@ -69,17 +69,17 @@ vec3 WhittedTracer::trace_ray(Ray & r, vector
& v_figures, vectorm_mat.m_ref_index); // Determine the specular reflection color. - if (kr > 0.0f && rec_level < MAX_RECURSION) { + if (kr > 0.0f && rec_level < m_max_depth) { rr = Ray(normalize(reflect(r.m_direction, n)), i_pos + n * BIAS); color += kr * trace_ray(rr, v_figures, v_lights, rec_level + 1); - } else if (rec_level >= MAX_RECURSION) + } else if (rec_level >= m_max_depth) return vec3(0.0f); // Determine the transmission color. - if (_f->m_mat.m_refract && kr < 1.0f && rec_level < MAX_RECURSION) { + if (_f->m_mat.m_refract && kr < 1.0f && rec_level < m_max_depth) { rr = Ray(normalize(refract(r.m_direction, n, r.m_ref_index / _f->m_mat.m_ref_index)), i_pos - n * BIAS, _f->m_mat.m_ref_index); color += (1.0f - kr) * trace_ray(rr, v_figures, v_lights, rec_level + 1); - } else if (rec_level >= MAX_RECURSION) + } else if (rec_level >= m_max_depth) return vec3(0.0f); } diff --git a/whitted_tracer.hpp b/whitted_tracer.hpp index c9f9d2a..61d4620 100644 --- a/whitted_tracer.hpp +++ b/whitted_tracer.hpp @@ -8,7 +8,7 @@ class WhittedTracer: public Tracer { public: WhittedTracer(): Tracer() { } - WhittedTracer(int h, int w, float fov): Tracer(h, w, fov) { }; + WhittedTracer(int h, int w, float fov, unsigned int max_depth): Tracer(h, w, fov, max_depth) { }; virtual ~WhittedTracer();