diff --git a/Makefile b/Makefile index 2e51ea8..343b615 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ CXX = g++ TARGET = ray -OBJECTS = main.o disk.o plane.o sphere.o directional_light.o point_light.o tracer.o path_tracer.o +OBJECTS = main.o disk.o plane.o sphere.o directional_light.o point_light.o tracer.o path_tracer.o whitted_tracer.o DEPENDS = $(OBJECTS:.o=.d) CXXFLAGS = -ansi -pedantic -Wall -DGLM_FORCE_RADIANS -fopenmp -LDLIBS = +LDLIBS = -lfreeimage .PHONY: all all: CXXFLAGS += -O2 -DNDEBUG diff --git a/README.org b/README.org index 6364a8d..d18de0f 100644 --- a/README.org +++ b/README.org @@ -6,4 +6,4 @@ An implementation of path tracing with photon mapping. An image generated with the current version of the program: -[[file:output.ppm]] +[[file:output.png]] diff --git a/main.cpp b/main.cpp index 115725b..86ce74f 100644 --- a/main.cpp +++ b/main.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "ray.hpp" #include "figure.hpp" @@ -23,7 +24,7 @@ using namespace std; using namespace glm; -static const char * OUT_FILE = "output.ppm"; +static const char * OUT_FILE = "output.png"; static char * input_file; static int g_samples = 25; @@ -38,7 +39,6 @@ static void scene_3(vector
& vf, vector & vl, mat4x4 & i_mode static void scene_4(vector
& vf, vector & vl, mat4x4 & i_model_view); int main(int argc, char ** argv) { - FILE * out; Ray r; vec2 sample; vector
figures; @@ -48,7 +48,12 @@ int main(int argc, char ** argv) { size_t current = 0; mat4x4 i_model_view; vec4 dir, orig; + FIBITMAP * output_bitmap; + FREE_IMAGE_FORMAT fif; + BYTE * bits; + int bpp; + // Check command line arguments. if(argc < 2 || argc > 7) { cerr << "USAGE: " << argv[0] << " IN FILE [OUT FILE [HEIGHT WIDTH [SAMPLES [FIELD OF VIEW]]]]" << endl; return EXIT_FAILURE; @@ -90,19 +95,18 @@ int main(int argc, char ** argv) { } } - out = fopen(argc >= 3 ? argv[2] : OUT_FILE, "wb"); + FreeImage_Initialise(); + // Create the image, scene and tracer. image = new vec3*[g_h]; for (int i = 0; i < g_h; i++) { image[i] = new vec3[g_w]; } - scene_2(figures, lights, i_model_view); + tracer = static_cast(new PathTracer(g_h, g_w, g_fov)); - tracer = static_cast(new PathTracer(g_h, g_w, g_fov, true)); - + // Generate the image. total = g_h * g_w * g_samples; - #pragma omp parallel for schedule(dynamic, 1) private(r, sample, dir, orig) shared(current) for (int i = 0; i < g_h; i++) { for (int j = 0; j < g_w; j++) { @@ -112,20 +116,35 @@ int main(int argc, char ** argv) { orig = i_model_view * vec4(0.0f, 0.0f, 0.0f, 1.0f); r = Ray(dir.x, dir.y, dir.z, orig.x, orig.y, orig.z); image[i][j] += tracer->trace_ray(r, figures, lights, 0); -#pragma omp critical - { - current++; - } +#pragma omp atomic + current++; } image[i][j] /= g_samples; } #pragma omp critical - { - cout << "\r" << setw(3) << static_cast((static_cast(current) / static_cast(total)) * 100.0) << "% done"; - } + cout << "\r" << setw(3) << static_cast((static_cast(current) / static_cast(total)) * 100.0) << "% done"; } cout << endl; + // Copy the pixels to the output bitmap. + output_bitmap = FreeImage_Allocate(g_w, g_h, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); + bpp = FreeImage_GetLine(output_bitmap) / FreeImage_GetWidth(output_bitmap); + for (unsigned int y = 0; y < FreeImage_GetHeight(output_bitmap); y++) { + bits = FreeImage_GetScanLine(output_bitmap, y); + for (unsigned int x = 0; x < FreeImage_GetWidth(output_bitmap); x++) { + bits[FI_RGBA_RED] = static_cast(pow(image[g_h - 1 - y][x].r, 1.0f / 2.2f) * 255.0f); + bits[FI_RGBA_GREEN] = static_cast(pow(image[g_h - 1 - y][x].g, 1.0f / 2.2f) * 255.0f); + bits[FI_RGBA_BLUE] = static_cast(pow(image[g_h - 1 - y][x].b, 1.0f / 2.2f) * 255.0f); + bits += bpp; + } + } + + // Save the output image. + fif = FreeImage_GetFIFFromFilename(argc >= 3 ? argv[2] : OUT_FILE); + FreeImage_Save(fif, output_bitmap, argc >= 3 ? argv[2] : OUT_FILE); + FreeImage_Unload(output_bitmap); + + // Clean up. delete tracer; for (size_t i = 0; i < figures.size(); i++) { @@ -138,20 +157,12 @@ int main(int argc, char ** argv) { } lights.clear(); - fprintf(out, "P6 %d %d %d ", g_w, g_h, 255); - for (int i = 0; i < g_h; i++) { - for (int j = 0; j < g_w; j++) { - fputc(static_cast(pow(image[i][j].r, 1.0f / 2.2f) * 255.0f), out); - fputc(static_cast(pow(image[i][j].g, 1.0f / 2.2f) * 255.0f), out); - fputc(static_cast(pow(image[i][j].b, 1.0f / 2.2f) * 255.0f), out); - } - } - fclose(out); - for (int i = 0; i < g_h; i++) delete[] image[i]; delete[] image; + FreeImage_DeInitialise(); + return EXIT_SUCCESS; } diff --git a/output.png b/output.png new file mode 100644 index 0000000..b524a6e Binary files /dev/null and b/output.png differ diff --git a/output.ppm b/output.ppm deleted file mode 100644 index 91da841..0000000 Binary files a/output.ppm and /dev/null differ diff --git a/path_tracer.cpp b/path_tracer.cpp index 4fb648e..bb00803 100644 --- a/path_tracer.cpp +++ b/path_tracer.cpp @@ -57,8 +57,8 @@ vec3 PathTracer::trace_ray(Ray & r, vector
& v_figures, vectorspecular(n, r, t, _f->m_mat) : vec3(0.0f); } - // If enabled, calculate indirect lighting contribution. - if (indirect_l && rec_level < MAX_RECURSION) { + // Calculate indirect lighting contribution. + if (rec_level < MAX_RECURSION) { r1 = random01(); r2 = random01(); sample = sample_hemisphere(r1, r2); diff --git a/path_tracer.hpp b/path_tracer.hpp index 5e4f321..6541301 100644 --- a/path_tracer.hpp +++ b/path_tracer.hpp @@ -6,11 +6,9 @@ class PathTracer: public Tracer { public: - bool indirect_l; + PathTracer(): Tracer() { } - PathTracer(): Tracer(), indirect_l(false) { } - - PathTracer(int h, int w, float fov, bool il): Tracer(h, w, fov), indirect_l(il) { }; + PathTracer(int h, int w, float fov): Tracer(h, w, fov) { }; virtual ~PathTracer(); diff --git a/whitted_tracer.cpp b/whitted_tracer.cpp new file mode 100644 index 0000000..6e01248 --- /dev/null +++ b/whitted_tracer.cpp @@ -0,0 +1,92 @@ +#include + +#include + +#include "whitted_tracer.hpp" + +using std::numeric_limits; +using namespace glm; + +WhittedTracer::~WhittedTracer() { } + +vec3 WhittedTracer::trace_ray(Ray & r, vector
& v_figures, vector & v_lights, unsigned int rec_level) const { + float t, _t; + Figure * _f; + vec3 n, color, i_pos, ref, dir_diff_color, dir_spec_color; + Ray mv_r, sr, rr; + bool vis; + float kr; + + t = numeric_limits::max(); + _f = NULL; + + // Find the closest intersecting surface. + for (size_t f = 0; f < v_figures.size(); f++) { + if (v_figures[f]->intersect(r, _t) && _t < t) { + t = _t; + _f = v_figures[f]; + } + } + + // If this ray intersects something: + if (_f != NULL) { + // Take the intersection point and the normal of the surface at that point. + i_pos = r.m_origin + (t * r.m_direction); + n = _f->normal_at_int(r, t); + + // Check if the material is not reflective/refractive. + if( !_f->m_mat.m_refract) { + // Calculate the direct lighting. + for (size_t l = 0; l < v_lights.size(); l++) { + // For every light source + vis = true; + + // Cast a shadow ray to determine visibility. + sr = Ray(v_lights[l]->direction(i_pos), i_pos + n * BIAS); + for (size_t f = 0; f < v_figures.size(); f++) { + if (v_figures[f]->intersect(sr, _t) && _t < v_lights[l]->distance(i_pos)) { + vis = false; + break; + } + } + + // Evaluate the shading model accounting for visibility. + dir_diff_color += vis ? v_lights[l]->diffuse(n, r, t, _f->m_mat) : vec3(0.0f); + dir_spec_color += vis ? v_lights[l]->specular(n, r, t, _f->m_mat) : vec3(0.0f); + } + + color += (dir_diff_color * (_f->m_mat.m_diffuse / pi())) + dir_spec_color; + + // Determine the specular reflection color. + if (_f->m_mat.m_rho > 0.0f && rec_level < MAX_RECURSION) { + 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) + return vec3(0.0f); + + } else { + // If the material has transmission enabled, calculate the Fresnel term. + kr = fresnel(r.m_direction, n, r.m_ref_index, _f->m_mat.m_ref_index); + + // Determine the specular reflection color. + if (kr > 0.0f && rec_level < MAX_RECURSION) { + 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) + return vec3(0.0f); + + // Determine the transmission color. + if (_f->m_mat.m_refract && kr < 1.0f && rec_level < MAX_RECURSION) { + 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) + return vec3(0.0f); + + } + + // Return final color. + return clamp(color, 0.0f, 1.0f); + + } else + return BCKG_COLOR; +} diff --git a/whitted_tracer.hpp b/whitted_tracer.hpp new file mode 100644 index 0000000..6fe388f --- /dev/null +++ b/whitted_tracer.hpp @@ -0,0 +1,20 @@ +#pragma once +#ifndef WHITTED_TRACER_HPP +#define WHITTED_TRACER_HPP + +#include "tracer.hpp" + +class WhittedTracer: public Tracer { +public: + bool indirect_l; + + WhittedTracer(): Tracer(), indirect_l(false) { } + + WhittedTracer(int h, int w, float fov, bool il): Tracer(h, w, fov), indirect_l(il) { }; + + virtual ~WhittedTracer(); + + virtual vec3 trace_ray(Ray & r, vector
& v_figures, vector & v_lights, unsigned int rec_level) const; +}; + +#endif