diff --git a/Makefile b/Makefile index bdedd79..6181002 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CXX = g++ TARGET = ray -OBJECTS = main.o disk.o plane.o sphere.o directional_light.o phong_brdf.o point_light.o spot_light.o tracer.o path_tracer.o whitted_tracer.o +OBJECTS = main.o sampling.o camera.o disk.o plane.o sphere.o directional_light.o phong_brdf.o point_light.o spot_light.o tracer.o path_tracer.o whitted_tracer.o DEPENDS = $(OBJECTS:.o=.d) CXXFLAGS = -ansi -pedantic -Wall -DGLM_FORCE_RADIANS -fopenmp LDLIBS = -lfreeimage diff --git a/camera.cpp b/camera.cpp new file mode 100644 index 0000000..880d942 --- /dev/null +++ b/camera.cpp @@ -0,0 +1,64 @@ +#include + +#include "camera.hpp" +#include "sampling.hpp" + +using glm::vec4; +using glm::rotate; +using glm::radians; +using glm::cross; + +void Camera::reset() { + m_inv_view_matrix = inverse(lookAt(m_eye, m_look, m_up)); +} + +void Camera::translate(vec3 t) { + mat4 t_matrix = glm::translate(mat4(1.0f), t); + vec4 new_eye = t_matrix * vec4(m_eye, 1.0f); + vec4 new_look = t_matrix * vec4(m_look, 1.0f); + m_eye = vec3(new_eye.x, new_eye.y, new_eye.z); + m_look = vec3(new_look.x, new_look.y, new_look.z); + reset(); +} + +void Camera::pitch(float angle) { + vec3 view_dir = normalize(m_look - m_eye); + vec3 left = cross(view_dir, m_up); + view_dir = rotate(view_dir, radians(angle), left); + m_up = normalize(rotate(m_up, radians(angle), left)); + m_look = m_eye + view_dir; + reset(); +} + +void Camera::yaw(float angle) { + vec3 view_dir = rotate(normalize(m_look - m_eye), radians(angle), m_up); + m_look = m_eye + view_dir; + reset(); +} + +void Camera::roll(float angle) { + m_up = normalize(rotate(m_up, radians(angle), normalize(m_look - m_eye))); + reset(); +} + +void Camera::view_to_world(Ray & r) const { + vec4 dir = m_inv_view_matrix * vec4(r.m_direction, 0.0f); + vec4 orig = m_inv_view_matrix * vec4(r.m_origin, 1.0f); + + r.m_direction = vec3(dir.x, dir.y, dir.z); + r.m_origin = vec3(orig.x, orig.y, orig.z); +} + +vec2 Camera::sample_pixel(int i, int j) const { + float pxNDC; + float pyNDC; + float pxS; + float pyS; + pyNDC = (static_cast(i) + random01()) / m_h; + pyS = (1.0f - (2.0f * pyNDC)) * glm::tan(radians(m_fov / 2.0f)); + pxNDC = (static_cast(j) + random01()) / m_w; + pxS = (2.0f * pxNDC) - 1.0f; + pxS *= m_a_ratio * glm::tan(radians(m_fov / 2.0f)); + + return vec2(pxS, pyS); +} diff --git a/camera.hpp b/camera.hpp new file mode 100644 index 0000000..5b1c6e1 --- /dev/null +++ b/camera.hpp @@ -0,0 +1,51 @@ +#pragma once +#ifndef CAMERA_HPP +#define CAMERA_HPP + +#include +#include + +#include "ray.hpp" + +using glm::mat4; +using glm::vec2; +using glm::vec3; +using glm::normalize; +using glm::lookAt; +using glm::inverse; + +class Camera { +public: + int m_h; + int m_w; + float m_fov; + float m_a_ratio; + vec3 m_eye; + vec3 m_look; + vec3 m_up; + + Camera(int h = 480, int w = 640, float fov = 90.0f, vec3 _e = vec3(0.0f), vec3 _l = vec3(0.0f, 0.0f, -1.0f), vec3 _u = vec3(0.0f, 1.0f, 0.0f)): + m_h(h), + m_w(w), + m_fov(fov), + m_eye(_e), + m_look(_l), + m_up(normalize(_u)) + { + m_a_ratio = static_cast(w) / static_cast(h); + m_inv_view_matrix = inverse(lookAt(_e, _l, _u)); + } + + void reset(); + void translate(vec3 t); + void pitch(float angle); + void yaw(float angle); + void roll(float angle); + vec2 sample_pixel(int i, int j) const; + void view_to_world(Ray & r) const; + +private: + mat4 m_inv_view_matrix; +}; + +#endif diff --git a/main.cpp b/main.cpp index ce8e13b..621ba7b 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,7 @@ #include #include +#include "camera.hpp" #include "ray.hpp" #include "figure.hpp" #include "sphere.hpp" @@ -32,10 +33,10 @@ 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 scene_1(vector
& vf, vector & vl, Camera * c); +static void scene_2(vector
& vf, vector & vl, Camera * c); +static void scene_3(vector
& vf, vector & vl, Camera * c); +static void scene_4(vector
& vf, vector & vl, Camera * c); static void print_usage(char ** const argv); static void parse_args(int argc, char ** const argv); @@ -70,30 +71,31 @@ int main(int argc, char ** argv) { Tracer * tracer; size_t total; size_t current = 0; - mat4x4 i_model_view; - vec4 dir, orig; FIBITMAP * output_bitmap; FREE_IMAGE_FORMAT fif; BYTE * bits; int bpp; + Camera * cam; parse_args(argc, argv); // Initialize everything. FreeImage_Initialise(); + + cam = new Camera(g_h, g_w, g_fov); 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); + scene_2(figures, lights, cam); // Create the tracer object. if (g_tracer == WHITTED) - tracer = static_cast(new WhittedTracer(g_h, g_w, g_fov, g_max_depth)); + tracer = static_cast(new WhittedTracer(g_max_depth)); else if(g_tracer == MONTE_CARLO) - tracer = static_cast(new PathTracer(g_h, g_w, g_fov, g_max_depth)); + tracer = static_cast(new PathTracer(g_max_depth)); else if(g_tracer == JENSEN) { cerr << "Photon mapping coming soon." << endl; return EXIT_FAILURE; @@ -102,17 +104,16 @@ int main(int argc, char ** argv) { print_usage(argv); return EXIT_FAILURE; } - + // Generate the image. total = g_h * g_w * g_samples; -#pragma omp parallel for schedule(dynamic, 1) private(r, sample, dir, orig) shared(current) +#pragma omp parallel for schedule(dynamic, 1) private(r, sample) shared(current) for (int i = 0; i < g_h; i++) { for (int j = 0; j < g_w; j++) { for (int k = 0; k < g_samples; k++) { - sample = tracer->sample_pixel(i, j); - dir = i_model_view * normalize(vec4(sample, -0.5f, 1.0f) - vec4(0.0f, 0.0f, 0.0f, 1.0f)); - 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); + sample = cam->sample_pixel(i, j); + r = Ray(normalize(vec3(sample, -0.5f) - vec3(0.0f)), vec3(0.0f)); + cam->view_to_world(r); image[i][j] += tracer->trace_ray(r, figures, lights, 0); #pragma omp atomic current++; @@ -146,6 +147,7 @@ int main(int argc, char ** argv) { if (g_out_file_name != NULL) free(g_out_file_name); + delete cam; delete tracer; for (size_t i = 0; i < figures.size(); i++) { @@ -292,7 +294,7 @@ void parse_args(int argc, char ** const argv) { g_input_file = argv[optind]; } -void scene_1(vector
& vf, vector & vl, mat4x4 & i_model_view) { +void scene_1(vector
& vf, vector & vl, Camera * c) { Sphere * s; Plane * p; Disk * d; @@ -367,12 +369,12 @@ void scene_1(vector
& vf, vector & vl, mat4x4 & i_model_view) vl.push_back(static_cast(l)); } -void scene_2(vector
& vf, vector & vl, mat4x4 & i_model_view) { +void scene_2(vector
& vf, vector & vl, Camera * c) { Sphere * s; Plane * p; Disk * d; PointLight * l; - + s = new Sphere(0.2f, 0.0f, -0.75f, 0.25f); s->m_mat->m_diffuse = vec3(1.0f); s->m_mat->m_rho = 0.2f; @@ -435,14 +437,19 @@ void scene_2(vector
& vf, vector & vl, mat4x4 & i_model_view) vl.push_back(static_cast(l)); } -void scene_3(vector
& vf, vector & vl, mat4x4 & i_model_view) { +void scene_3(vector
& vf, vector & vl, Camera * c) { Sphere * s; Plane * p; DirectionalLight * l; vec3 eye = vec3(0.0f, 1.5f, 0.0f); vec3 center = vec3(0.0f, 0.0f, -2.0f); vec3 left = vec3(-1.0f, 0.0f, 0.0f); - vec3 up = cross(center - eye, left); + + c->m_eye = eye; + c->m_look = center; + c->m_up = cross(normalize(center - eye), left); + c->translate(vec3(1.0f, 0.0f, 0.0f)); + c->roll(15.0f); s = new Sphere(0.0f, -0.15f, -2.0f, 1.0f); s->m_mat->m_diffuse = vec3(1.0f, 0.5f, 0.0f); @@ -498,11 +505,9 @@ void scene_3(vector
& vf, vector & vl, mat4x4 & i_model_view) l->m_position = normalize(vec3(1.0f, 0.0f, 1.0f)); l->m_diffuse = vec3(0.5f); vl.push_back(static_cast(l)); - - i_model_view = inverse(lookAt(eye, center, up)); } -void scene_4(vector
& vf, vector & vl, mat4x4 & i_model_view) { +void scene_4(vector
& vf, vector & vl, Camera * c) { Sphere * s; Plane * p; diff --git a/output.png b/output.png index 5c19ea2..89d0d87 100644 Binary files a/output.png and b/output.png differ diff --git a/path_tracer.cpp b/path_tracer.cpp index f0ccd17..9da631f 100644 --- a/path_tracer.cpp +++ b/path_tracer.cpp @@ -3,14 +3,13 @@ #include #include "path_tracer.hpp" +#include "sampling.hpp" using std::numeric_limits; using namespace glm; PathTracer::~PathTracer() { } -static const float PDF = (1.0f / (2.0f * pi())); - vec3 PathTracer::trace_ray(Ray & r, vector
& v_figures, vector & v_lights, unsigned int rec_level) const { float t, _t; Figure * _f; @@ -121,5 +120,5 @@ vec3 PathTracer::trace_ray(Ray & r, vector
& v_figures, vector + +#include +#include + +#include "sampling.hpp" + +using glm::mat3; +using glm::abs; +using glm::normalize; +using glm::cross; +using glm::pi; + +const float PDF = (1.0f / (2.0f * pi())); + +float random01() { + return static_cast(rand()) / static_cast(RAND_MAX); +} + +/* Sampling functions pretty much taken from scratchapixel.com */ +void create_coords_system(const vec3 &n, vec3 &nt, vec3 &nb) { + if (abs(n.x) > abs(n.y)) + nt = normalize(vec3(n.z, 0.0f, -n.x)); + else + nt = normalize(vec3(0.0f, -n.z, n.y)); + nb = normalize(cross(n, nt)); +} + +vec3 sample_hemisphere(const float r1, float r2) { + float sin_t = glm::sqrt(1.0f - (r1 * r1)); + float phi = 2 * pi() * r2; + float x = sin_t * glm::cos(phi); + float z = sin_t * glm::sin(phi); + return vec3(x, r1, z); +} + +void rotate_sample(vec3 & sample, vec3 & n) { + vec3 nt, nb; + mat3 rot_m; + + create_coords_system(n, nt, nb); + sample = vec3(sample.x * nb.x + sample.y * n.x + sample.z * nt.x, + sample.x * nb.y + sample.y * n.y + sample.z * nt.y, + sample.x * nb.z + sample.y * n.z + sample.z * nt.z); +} diff --git a/sampling.hpp b/sampling.hpp new file mode 100644 index 0000000..b62ed6d --- /dev/null +++ b/sampling.hpp @@ -0,0 +1,16 @@ +#pragma once +#ifndef SAMPLING_HPP +#define SAMPLING_HPP + +#include + +using glm::vec3; + +extern const float PDF; + +extern float random01(); +extern void create_coords_system(const vec3 &n, vec3 &nt, vec3 &nb); +extern vec3 sample_hemisphere(const float r1, float r2); +extern void rotate_sample(vec3 & sample, vec3 & n); + +#endif diff --git a/tracer.cpp b/tracer.cpp index 422c47c..bb97ced 100644 --- a/tracer.cpp +++ b/tracer.cpp @@ -1,23 +1,17 @@ -#include - -#include - #include "tracer.hpp" -using namespace glm; +using glm::dot; +using glm::normalize; +using glm::refract; const float BIAS = 0.000001f; const vec3 BCKG_COLOR = vec3(1.0f); -float Tracer::random01() const { - return static_cast(rand()) / static_cast(RAND_MAX); -} - float Tracer::fresnel(const vec3 & i, const vec3 & n, const float ir1, const float ir2) const { float cos_t1 = dot(i, n); float cos_t2 = dot(normalize(refract(i, n, ir1 / ir2)), n); - float sin_t2 = (ir1 / ir2) * sqrt(1.0f - (cos_t2 * cos_t2)); + float sin_t2 = (ir1 / ir2) * glm::sqrt(1.0f - (cos_t2 * cos_t2)); if (sin_t2 >= 1.0f) return 1.0f; @@ -27,44 +21,3 @@ float Tracer::fresnel(const vec3 & i, const vec3 & n, const float ir1, const flo return ((fr_par * fr_par) + (fr_per * fr_per)) / 2.0f; } - -vec2 Tracer::sample_pixel(int i, int j) const { - float pxNDC; - float pyNDC; - float pxS; - float pyS; - pyNDC = (static_cast(i) + random01()) / m_h; - pyS = (1.0f - (2.0f * pyNDC)) * tan(radians(m_fov / 2)); - pxNDC = (static_cast(j) + random01()) / m_w; - pxS = (2.0f * pxNDC) - 1.0f; - pxS *= m_a_ratio * tan(radians(m_fov / 2)); - - return vec2(pxS, pyS); -} - -/* Helper functions pretty much taken from scratchapixel.com */ -void Tracer::create_coords_system(const vec3 &n, vec3 &nt, vec3 &nb) const { - if (abs(n.x) > abs(n.y)) - nt = normalize(vec3(n.z, 0.0f, -n.x)); - else - nt = normalize(vec3(0.0f, -n.z, n.y)); - nb = normalize(cross(n, nt)); -} - -vec3 Tracer::sample_hemisphere(const float r1, const float r2) const { - float sin_t = sqrt(1.0f - (r1 * r1)); - float phi = 2 * pi() * r2; - float x = sin_t * cos(phi); - float z = sin_t * sin(phi); - return vec3(x, r1, z); -} - -void Tracer::rotate_sample(vec3 & sample, const vec3 & n) const { - vec3 nt, nb; - mat3 rot_m; - - create_coords_system(n, nt, nb); - sample = vec3(sample.x * nb.x + sample.y * n.x + sample.z * nt.x, - sample.x * nb.y + sample.y * n.y + sample.z * nt.y, - sample.x * nb.z + sample.y * n.z + sample.z * nt.z); -} diff --git a/tracer.hpp b/tracer.hpp index 7deafe3..871a5df 100644 --- a/tracer.hpp +++ b/tracer.hpp @@ -20,29 +20,18 @@ extern const vec3 BCKG_COLOR; class Tracer { public: - int m_h; - 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), m_max_depth(5) { } + Tracer(): m_max_depth(5) { } - 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); - }; + Tracer(unsigned int max_depth): m_max_depth(max_depth) { } virtual ~Tracer() { } - vec2 sample_pixel(int i, int j) const; virtual vec3 trace_ray(Ray & r, vector
& v_figures, vector & v_lights, unsigned int rec_level) const = 0; protected: - float random01() const; float fresnel(const vec3 & i, const vec3 & n, const float ir1, const float ir2) const; - void create_coords_system(const vec3 &n, vec3 &nt, vec3 &nb) const; - vec3 sample_hemisphere(const float r1, const float r2) const; - void rotate_sample(vec3 & sample, const vec3 & n) const; }; #endif diff --git a/whitted_tracer.hpp b/whitted_tracer.hpp index 61d4620..e781266 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, unsigned int max_depth): Tracer(h, w, fov, max_depth) { }; + WhittedTracer(unsigned int max_depth): Tracer(max_depth) { }; virtual ~WhittedTracer();