diff --git a/Makefile b/Makefile index 636239c..70db39f 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ CXX = g++ TARGET = ray -OBJECTS = main.o sampling.o camera.o environment.o disk.o plane.o sphere.o phong_brdf.o hsa_brdf.o directional_light.o point_light.o spot_light.o tracer.o path_tracer.o whitted_tracer.o +OBJECTS = main.o sampling.o camera.o environment.o disk.o plane.o sphere.o phong_brdf.o hsa_brdf.o directional_light.o point_light.o spot_light.o scene.o tracer.o path_tracer.o whitted_tracer.o DEPENDS = $(OBJECTS:.o=.d) CXXFLAGS = -ansi -pedantic -Wall -DGLM_FORCE_RADIANS -fopenmp -LDLIBS = -lfreeimage +LDLIBS = -lfreeimage -ljson_spirit .PHONY: all all: CXXFLAGS += -O2 -DNDEBUG diff --git a/camera.cpp b/camera.cpp index 880d942..65c989a 100644 --- a/camera.cpp +++ b/camera.cpp @@ -1,7 +1,6 @@ #include #include "camera.hpp" -#include "sampling.hpp" using glm::vec4; using glm::rotate; @@ -48,17 +47,3 @@ void Camera::view_to_world(Ray & r) const { 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 index c9ea965..e1705bd 100644 --- a/camera.hpp +++ b/camera.hpp @@ -20,14 +20,10 @@ public: 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)): + Camera(vec3 _e = vec3(0.0f), vec3 _l = vec3(0.0f, 0.0f, -1.0f), vec3 _u = vec3(0.0f, 1.0f, 0.0f)): m_eye(_e), m_look(_l), m_up(normalize(_u)), - m_h(h), - m_w(w), - m_fov(fov), - m_a_ratio(static_cast(w) / static_cast(h)), m_inv_view_matrix(inverse(lookAt(_e, _l, normalize(_u)))) { } @@ -36,14 +32,9 @@ public: 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: - int m_h; - int m_w; - float m_fov; - float m_a_ratio; mat4 m_inv_view_matrix; }; diff --git a/main.cpp b/main.cpp index 1e88ab6..9240500 100644 --- a/main.cpp +++ b/main.cpp @@ -11,6 +11,8 @@ #include #include +#include "sampling.hpp" +#include "scene.hpp" #include "camera.hpp" #include "ray.hpp" #include "figure.hpp" @@ -61,9 +63,10 @@ typedef enum TRACERS { NONE, WHITTED, MONTE_CARLO, JENSEN } tracer_t; static char * g_input_file = NULL; static char * g_out_file_name = NULL; static int g_samples = 25; -static float g_fov = 45.f; +static float g_fov = 45.0f; static int g_w = 640; static int g_h = 480; +static float g_a_ratio = 640.0f / 480.0f; static vec3 ** image; static tracer_t g_tracer = NONE; static unsigned int g_max_depth = 5; @@ -89,18 +92,26 @@ int main(int argc, char ** argv) { int pitch; Camera * cam; Environment * env = NULL; + Scene * scn; parse_args(argc, argv); // Initialize everything. FreeImage_Initialise(); - cam = new Camera(g_h, g_w, g_fov); + cam = new Camera(); image = new vec3*[g_h]; for (int i = 0; i < g_h; i++) { image[i] = new vec3[g_w]; } + + try { + scn = new Scene("scenes/scene3.json"); + delete scn; + } catch (SceneError & e) { + cout << e.what() << endl; + } scene_3(figures, lights, env, cam); @@ -135,7 +146,7 @@ int main(int argc, char ** argv) { 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 = cam->sample_pixel(i, j); + sample = sample_pixel(i, j, g_w, g_h, g_a_ratio, g_fov); 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, env, 0); @@ -186,7 +197,7 @@ int main(int argc, char ** argv) { } figures.clear(); - for (size_t i = 0; i < figures.size(); i++) { + for (size_t i = 0; i < lights.size(); i++) { delete lights[i]; } lights.clear(); @@ -288,6 +299,7 @@ void parse_args(int argc, char ** const argv) { print_usage(argv); exit(EXIT_FAILURE); } + g_a_ratio = static_cast(g_w) / static_cast(g_h); } break; diff --git a/material.hpp b/material.hpp index bf144b8..840f807 100644 --- a/material.hpp +++ b/material.hpp @@ -20,7 +20,15 @@ public: bool m_refract; BRDF * m_brdf; - Material(BRDF * _brdf = NULL): m_diffuse(vec3(1.0f)), m_specular(vec3(1.0f)), m_rho(0.0f), m_shininess(89.0f), m_ref_index(1.0f), m_refract(false) { + Material(BRDF * _brdf = NULL): + m_emission(vec3(0.0f)), + m_diffuse(vec3(1.0f)), + m_specular(vec3(1.0f)), + m_rho(0.0f), + m_shininess(89.0f), + m_ref_index(1.0f), + m_refract(false) + { m_brdf = _brdf != NULL ? _brdf : static_cast(new PhongBRDF()); } diff --git a/sampling.cpp b/sampling.cpp index cacc394..58afe36 100644 --- a/sampling.cpp +++ b/sampling.cpp @@ -9,6 +9,7 @@ using glm::mat3; using glm::abs; using glm::normalize; using glm::cross; +using glm::radians; using glm::pi; const float PDF = (1.0f / (2.0f * pi())); @@ -17,6 +18,20 @@ float random01() { return static_cast(rand()) / static_cast(RAND_MAX); } +vec2 sample_pixel(int i, int j, float w, float h, float a_ratio, float fov) { + float pxNDC; + float pyNDC; + float pxS; + float pyS; + pyNDC = (static_cast(i) + random01()) / h; + pyS = (1.0f - (2.0f * pyNDC)) * glm::tan(radians(fov / 2.0f)); + pxNDC = (static_cast(j) + random01()) / w; + pxS = (2.0f * pxNDC) - 1.0f; + pxS *= a_ratio * glm::tan(radians(fov / 2.0f)); + + return vec2(pxS, pyS); +} + /* 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)) diff --git a/sampling.hpp b/sampling.hpp index b62ed6d..06e3ba1 100644 --- a/sampling.hpp +++ b/sampling.hpp @@ -2,13 +2,16 @@ #ifndef SAMPLING_HPP #define SAMPLING_HPP +#include #include +using glm::vec2; using glm::vec3; extern const float PDF; extern float random01(); +extern vec2 sample_pixel(int i, int j, float w, float h, float a_ratio, float fov); 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); diff --git a/scene.cpp b/scene.cpp new file mode 100644 index 0000000..ff8f673 --- /dev/null +++ b/scene.cpp @@ -0,0 +1,222 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "scene.hpp" + +using std::cerr; +using std::endl; +using std::string; +using std::ostringstream; +using std::ifstream; +using std::ios; +using std::streamsize; +using glm::vec3; +using glm::normalize; +using glm::cross; +using json_spirit::read; +using json_spirit::Value; +using json_spirit::Error_position; +using json_spirit::Object; +using json_spirit::Array; + +static const string ENV_KEY = "environment"; +static const string CAM_KEY = "camera"; +static const string SPH_KEY = "sphere"; +static const string PLN_KEY = "plane"; +static const string DSK_KEY = "disk"; +static const string MSH_KEY = "mesh"; +static const string DLT_KEY = "directional_light"; +static const string PLT_KEY = "point_light"; +static const string SLT_KEY = "spot_light"; +static const string SAL_KEY = "sphere_area_light"; +static const string PAL_KEY = "planar_area_light"; + +static const string ENV_TEX_KEY = "texture"; +static const string ENV_LPB_KEY = "light_probe"; +static const string ENV_COL_KEY = "color"; + +static const string CAM_EYE_KEY = "eye"; +static const string CAM_CNT_KEY = "look"; +static const string CAM_LFT_KEY = "left"; +static const string CAM_UPV_KEY = "up"; + +static const string FIG_POS_KEY = "position"; +static const string FIG_MAT_KEY = "material"; +static const string FIG_RAD_KEY = "radius"; +static const string FIG_NOR_KEY = "normal"; + +static const string PLN_PNT_KEY = "point"; + +static const string MLT_EMS_KEY = "emission"; +static const string MLT_DIF_KEY = "diffuse"; +static const string MLT_SPC_KEY = "specular"; +static const string MLT_RHO_KEY = "rho"; +static const string MLT_SHN_KEY = "shininess"; +static const string MLT_RFI_KEY = "ref_index"; +static const string MLT_BRF_KEY = "transmissive"; +static const string MLT_BRD_KEY = "brdf"; + +static void read_vector(Value & val, vec3 & vec) throw(SceneError); +static void read_environment(Value & v, Environment * & e) throw(SceneError); +static void read_camera(Value & v, Camera * & c) throw(SceneError); + +Scene::Scene(const char * file_name, int h, int w, float fov) throw(SceneError) { + ostringstream oss; + ifstream ifs(file_name, ios::in); + Value val; + Object top_level; + + m_cam = NULL; + m_env = NULL; + + if (ifs.is_open()) { + try { + read_or_throw(ifs, val); + } catch (Error_position & e) { + ifs.close(); + oss << "Failed to parse the input file: " << endl << "Reason: " << e.reason_ << endl << "Line: " << e.line_ << endl << "Column: " << e.column_; + throw SceneError(oss.str()); + } + + ifs.close(); + + top_level = val.get_value(); + + try { + for(Object::iterator it = top_level.begin(); it != top_level.end(); it++) { + if ((*it).name_ == ENV_KEY) + read_environment((*it).value_, m_env); + + else if ((*it).name_ == CAM_KEY) + read_camera((*it).value_, m_cam); + + else if ((*it).name_ == SPH_KEY) { + + } else if ((*it).name_ == PLN_KEY) { + + } else if ((*it).name_ == DSK_KEY) { + + } else if ((*it).name_ == DLT_KEY) { + + } else if ((*it).name_ == PLT_KEY) { + + } else if ((*it).name_ == SLT_KEY) { + + } else + cerr << "Unrecognized key \"" << (*it).name_ << "\" in the input file." << endl; + } + } catch (SceneError & e) { + throw e; + } + + // If there were no camera and/or environment defined, create some default ones. + if (m_cam == NULL) + m_cam = new Camera(); + if (m_env == NULL) + m_env = new Environment(); + + } else + throw SceneError("Could not open the input file."); +} + +Scene::~Scene() { + delete m_env; + delete m_cam; + + for (size_t i = 0; i < m_figures.size(); i++) { + delete m_figures[i]; + } + m_figures.clear(); + + for (size_t i = 0; i < m_lights.size(); i++) { + delete m_lights[i]; + } + m_lights.clear(); +} + +inline void read_vector(Value & val, vec3 & vec) throw(SceneError) { + Array a = val.get_value(); + + if (a.size() < 3) + throw SceneError("Vector value must have 3 elements."); + + vec = vec3(a[0].get_value(), a[1].get_value(), a[2].get_value()); +} + +void read_environment(Value & v, Environment * & e) throw(SceneError) { + string t_name = ""; + bool l_probe = false, has_tex = false, has_color = false; + vec3 color; + Object env_obj = v.get_value(); + + for(Object::iterator it = env_obj.begin(); it != env_obj.end(); it++) { + if ((*it).name_ == ENV_TEX_KEY) + t_name = (*it).value_.get_value(); + + else if ((*it).name_ == ENV_LPB_KEY) + l_probe = (*it).value_.get_value(); + + else if ((*it).name_ == ENV_COL_KEY) + try { + read_vector((*it).value_, color); + } catch (SceneError & e) { + throw e; + } + + else + cerr << "Unrecognized key \"" << (*it).name_ << "\" in input file." << endl; + } + + if (!has_tex && !has_color) + throw SceneError("Environment must specify either a texture or color."); + + e = new Environment(has_tex ? t_name.c_str() : NULL , l_probe, color); +} + +void read_camera(Value & v, Camera * & c) throw(SceneError) { + bool has_up = false, has_left = false, has_eye = false, has_look = false; + vec3 eye, look, left, up; + Object cam_obj = v.get_value(); + + for(Object::iterator it = cam_obj.begin(); it != cam_obj.end(); it++) { + if ((*it).name_ == CAM_EYE_KEY) { + read_vector((*it).value_, eye); + has_eye = true; + + } else if ((*it).name_ == CAM_CNT_KEY) { + read_vector((*it).value_, look); + has_look = true; + + } else if ((*it).name_ == CAM_LFT_KEY) { + read_vector((*it).value_, left); + has_left = true; + + } else if ((*it).name_ == CAM_UPV_KEY) { + read_vector((*it).value_, up); + has_up = true; + + } else + cerr << "Unrecognized key \"" << (*it).name_ << "\" in input file." << endl; + } + + if (!has_eye) + throw SceneError("Must specify an eye position for the camera."); + + if (!has_look) + throw SceneError("Must specify a look position for the camera."); + + if (has_up) + c = new Camera(eye, look, up); + else if(!has_up && has_left) { + up = cross(normalize(look - eye), left); + c = new Camera(eye, look, up); + } else + throw SceneError("Must specify either an up or left vector for the camera."); +} diff --git a/scene.hpp b/scene.hpp new file mode 100644 index 0000000..c68ba59 --- /dev/null +++ b/scene.hpp @@ -0,0 +1,34 @@ +#pragma once +#ifndef SCENE_HPP +#define SCENE_HPP + +#include +#include +#include + +#include "camera.hpp" +#include "figure.hpp" +#include "light.hpp" +#include "environment.hpp" + +using std::string; +using std::vector; +using std::runtime_error; + +class SceneError: public runtime_error { +public: + explicit SceneError(const string & what_arg): runtime_error(what_arg) { } +}; + +class Scene { +public: + vector
m_figures; + vector m_lights; + Environment * m_env; + Camera * m_cam; + + Scene(const char * file_name, int h = 480, int w = 640, float fov = 90.0f) throw(SceneError); + ~Scene(); +}; + +#endif diff --git a/scenes/scene3.json b/scenes/scene3.json new file mode 100644 index 0000000..db6bbbc --- /dev/null +++ b/scenes/scene3.json @@ -0,0 +1,49 @@ +{ + "environment": { + "texture": "textures/pisa.hdr", + "light_probe": false + }, + + "camera": { + "eye": [0.0, 1.5, 1.0], + "look": [0.0, 0.0, -2.0], + "left": [-1.0, 0.0, 0.0] + }, + + "sphere": { + "position": [2.0, 0.0, -2.0], + "radius": 1.5, + "material": { + "diffuse": [1.0f, 0.0f, 1.0f], + "shininess": 128.0 + } + }, + + "sphere":{ + "position": [-1.0, 0.0, -3.25], + "radius": 1.5, + "material": { + "diffuse": [1.0, 0.0, 1.0], + "rho": 0.4 + } + }, + + "sphere":{ + "position": [1.0, 0.0, -3.25], + "radius": 1.5, + "material": { + "diffuse": [1.0, 1.0, 1.0], + "rho": 0.4 + } + }, + + "disk": { + "position": [1.0, -1.5, -3.25], + "normal": [0.0, 1.0, 0.0], + "radius": 3.0, + "material": { + "diffuse": [0.0, 0.5, 0.5], + "specular": [0.0, 0.0, 0.0] + } + } +}