From a9a1d11af287f4473eafa70f8bef160799c9bc64 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Sokolov" Date: Wed, 26 May 2021 15:58:11 +0200 Subject: [PATCH] facelift --- CMakeLists.txt | 2 +- geometry.h | 105 ++++++++++++------------------- tinyraytracer.cpp | 155 +++++++++++++++++++++------------------------- 3 files changed, 113 insertions(+), 149 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b5aeae6..0bcc539 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ endfunction() enable_cxx_compiler_flag_if_supported("-Wall") enable_cxx_compiler_flag_if_supported("-Wextra") enable_cxx_compiler_flag_if_supported("-pedantic") -enable_cxx_compiler_flag_if_supported("-std=c++11") +enable_cxx_compiler_flag_if_supported("-std=c++14") enable_cxx_compiler_flag_if_supported("-O3") enable_cxx_compiler_flag_if_supported("-fopenmp") diff --git a/geometry.h b/geometry.h index 8bba575..4bafac7 100644 --- a/geometry.h +++ b/geometry.h @@ -1,84 +1,61 @@ #ifndef __GEOMETRY_H__ #define __GEOMETRY_H__ - #include -#include #include #include -template struct vec { - vec() { for (size_t i=DIM; i--; data_[i] = T()); } - T& operator[](const size_t i) { assert(i struct vec { + float& operator[](const size_t i) { assert(i Vec2f; -typedef vec<3, float> Vec3f; -typedef vec<3, int > Vec3i; -typedef vec<4, float> Vec4f; - -template struct vec<2,T> { - vec() : x(T()), y(T()) {} - vec(T X, T Y) : x(X), y(Y) {} - template vec<2,T>(const vec<2,U> &v); - T& operator[](const size_t i) { assert(i<2); return i<=0 ? x : y; } - const T& operator[](const size_t i) const { assert(i<2); return i<=0 ? x : y; } - T x,y; -}; - -template struct vec<3,T> { - vec() : x(T()), y(T()), z(T()) {} - vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {} - T& operator[](const size_t i) { assert(i<3); return i<=0 ? x : (1==i ? y : z); } - const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); } - float norm() { return std::sqrt(x*x+y*y+z*z); } - vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; } - T x,y,z; -}; - -template struct vec<4,T> { - vec() : x(T()), y(T()), z(T()), w(T()) {} - vec(T X, T Y, T Z, T W) : x(X), y(Y), z(Z), w(W) {} - T& operator[](const size_t i) { assert(i<4); return i<=0 ? x : (1==i ? y : (2==i ? z : w)); } - const T& operator[](const size_t i) const { assert(i<4); return i<=0 ? x : (1==i ? y : (2==i ? z : w)); } - T x,y,z,w; -}; - -template T operator*(const vec& lhs, const vec& rhs) { - T ret = T(); - for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]); - return ret; -} - -templatevec operator+(vec lhs, const vec& rhs) { - for (size_t i=DIM; i--; lhs[i]+=rhs[i]); - return lhs; -} - -templatevec operator-(vec lhs, const vec& rhs) { - for (size_t i=DIM; i--; lhs[i]-=rhs[i]); - return lhs; -} - -template vec operator*(const vec &lhs, const U& rhs) { - vec ret; +template vec operator*(const vec &lhs, const float rhs) { + vec ret; for (size_t i=DIM; i--; ret[i]=lhs[i]*rhs); return ret; } -template vec operator-(const vec &lhs) { - return lhs*T(-1); +template float operator*(const vec& lhs, const vec& rhs) { + float ret = 0; + for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]); + return ret; } -template vec<3,T> cross(vec<3,T> v1, vec<3,T> v2) { - return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x); +template vec operator+(vec lhs, const vec& rhs) { + for (size_t i=DIM; i--; lhs[i]+=rhs[i]); + return lhs; } -template std::ostream& operator<<(std::ostream& out, const vec& v) { - for(unsigned int i=0; i vec operator-(vec lhs, const vec& rhs) { + for (size_t i=DIM; i--; lhs[i]-=rhs[i]); + return lhs; +} + +template vec operator-(const vec &lhs) { + return lhs*(-1.f); +} + +template <> struct vec<3> { + float& operator[](const size_t i) { assert(i<3); return i==0 ? x : (1==i ? y : z); } + const float& operator[](const size_t i) const { assert(i<3); return i==0 ? x : (1==i ? y : z); } + float norm() { return std::sqrt(x*x+y*y+z*z); } + vec<3> & normalize(float l=1) { *this = (*this)*(l/norm()); return *this; } + float x = 0, y = 0, z = 0; +}; + +typedef vec<3> vec3; +typedef vec<4> vec4; + +vec3 cross(vec3 v1, vec3 v2) { + return { v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x }; +} + +template std::ostream& operator<<(std::ostream& out, const vec& v) { + for (size_t i=0; i +#include "geometry.h" + #include -#include #include #include #include -#include "geometry.h" struct Light { - Light(const Vec3f &p, const float i) : position(p), intensity(i) {} - Vec3f position; + vec3 position; float intensity; }; struct Material { - Material(const float r, const Vec4f &a, const Vec3f &color, const float spec) : refractive_index(r), albedo(a), diffuse_color(color), specular_exponent(spec) {} - Material() : refractive_index(1), albedo(1,0,0,0), diffuse_color(), specular_exponent() {} - float refractive_index; - Vec4f albedo; - Vec3f diffuse_color; - float specular_exponent; + float refractive_index = 1; + vec4 albedo = {1,0,0,0}; + vec3 diffuse_color = {0,0,0}; + float specular_exponent = 0; }; struct Sphere { - Vec3f center; + vec3 center; float radius; Material material; - - Sphere(const Vec3f &c, const float r, const Material &m) : center(c), radius(r), material(m) {} - - bool ray_intersect(const Vec3f &orig, const Vec3f &dir, float &t0) const { - Vec3f L = center - orig; - float tca = L*dir; - float d2 = L*L - tca*tca; - if (d2 > radius*radius) return false; - float thc = sqrtf(radius*radius - d2); - t0 = tca - thc; - float t1 = tca + thc; - if (t0 < 0) t0 = t1; - if (t0 < 0) return false; - return true; - } }; -Vec3f reflect(const Vec3f &I, const Vec3f &N) { +bool ray_sphere_intersect(const vec3 &orig, const vec3 &dir, const Sphere &s, float &t0) { + vec3 L = s.center - orig; + float tca = L*dir; + float d2 = L*L - tca*tca; + if (d2 > s.radius*s.radius) return false; + float thc = sqrtf(s.radius*s.radius - d2); + t0 = tca - thc; + float t1 = tca + thc; + if (t0 < 1e-3) t0 = t1; // offset the original point to avoid occlusion by the object itself + if (t0 < 1e-3) return false; + return true; +} + +vec3 reflect(const vec3 &I, const vec3 &N) { return I - N*2.f*(I*N); } -Vec3f refract(const Vec3f &I, const Vec3f &N, const float eta_t, const float eta_i=1.f) { // Snell's law +vec3 refract(const vec3 &I, const vec3 &N, const float eta_t, const float eta_i=1.f) { // Snell's law float cosi = - std::max(-1.f, std::min(1.f, I*N)); if (cosi<0) return refract(I, -N, eta_i, eta_t); // if the ray comes from the inside the object, swap the air and the media float eta = eta_i / eta_t; float k = 1 - eta*eta*(1 - cosi*cosi); - return k<0 ? Vec3f(1,0,0) : I*eta + N*(eta*cosi - sqrtf(k)); // k<0 = total reflection, no ray to refract. I refract it anyways, this has no physical meaning + return k<0 ? vec3{1,0,0} : I*eta + N*(eta*cosi - std::sqrt(k)); // k<0 = total reflection, no ray to refract. I refract it anyways, this has no physical meaning } -bool scene_intersect(const Vec3f &orig, const Vec3f &dir, const std::vector &spheres, Vec3f &hit, Vec3f &N, Material &material) { +bool scene_intersect(const vec3 &orig, const vec3 &dir, const std::vector &spheres, vec3 &hit, vec3 &N, Material &material) { float spheres_dist = std::numeric_limits::max(); - for (size_t i=0; i < spheres.size(); i++) { + for (const Sphere &s : spheres) { float dist_i; - if (spheres[i].ray_intersect(orig, dir, dist_i) && dist_i < spheres_dist) { + if (ray_sphere_intersect(orig, dir, s, dist_i) && dist_i < spheres_dist) { spheres_dist = dist_i; hit = orig + dir*dist_i; - N = (hit - spheres[i].center).normalize(); - material = spheres[i].material; + N = (hit - s.center).normalize(); + material = s.material; } } float checkerboard_dist = std::numeric_limits::max(); - if (fabs(dir.y)>1e-3) { + if (std::abs(dir.y)>1e-3) { // avoid division by zero float d = -(orig.y+4)/dir.y; // the checkerboard plane has equation y = -4 - Vec3f pt = orig + dir*d; - if (d>0 && fabs(pt.x)<10 && pt.z<-10 && pt.z>-30 && d1e-3 && fabs(pt.x)<10 && pt.z<-10 && pt.z>-30 && d &spheres, const std::vector &lights, size_t depth=0) { - Vec3f point, N; +vec3 cast_ray(const vec3 &orig, const vec3 &dir, const std::vector &spheres, const std::vector &lights, size_t depth=0) { + vec3 point, N; Material material; - if (depth>4 || !scene_intersect(orig, dir, spheres, point, N, material)) { - return Vec3f(0.2, 0.7, 0.8); // background color - } + if (depth>4 || !scene_intersect(orig, dir, spheres, point, N, material)) + return vec3{0.2, 0.7, 0.8}; // background color - Vec3f reflect_dir = reflect(dir, N).normalize(); - Vec3f refract_dir = refract(dir, N, material.refractive_index).normalize(); - Vec3f reflect_orig = reflect_dir*N < 0 ? point - N*1e-3 : point + N*1e-3; // offset the original point to avoid occlusion by the object itself - Vec3f refract_orig = refract_dir*N < 0 ? point - N*1e-3 : point + N*1e-3; - Vec3f reflect_color = cast_ray(reflect_orig, reflect_dir, spheres, lights, depth + 1); - Vec3f refract_color = cast_ray(refract_orig, refract_dir, spheres, lights, depth + 1); + vec3 reflect_dir = reflect(dir, N).normalize(); + vec3 refract_dir = refract(dir, N, material.refractive_index).normalize(); + vec3 reflect_color = cast_ray(point, reflect_dir, spheres, lights, depth + 1); + vec3 refract_color = cast_ray(point, refract_dir, spheres, lights, depth + 1); float diffuse_light_intensity = 0, specular_light_intensity = 0; - for (size_t i=0; i &spheres, const std::vector &lights) { const int width = 1024; const int height = 768; const float fov = M_PI/3.; - std::vector framebuffer(width*height); + std::vector framebuffer(width*height); #pragma omp parallel for for (size_t j = 0; j &spheres, const std::vector &lights float dir_x = (i + 0.5) - width/2.; float dir_y = -(j + 0.5) + height/2.; // this flips the image at the same time float dir_z = -height/(2.*tan(fov/2.)); - framebuffer[i+j*width] = cast_ray(Vec3f(0,0,0), Vec3f(dir_x, dir_y, dir_z).normalize(), spheres, lights); + framebuffer[i+j*width] = cast_ray(vec3{0,0,0}, vec3{dir_x, dir_y, dir_z}.normalize(), spheres, lights); } } std::ofstream ofs; // save the framebuffer to file - ofs.open("./out.ppm",std::ios::binary); + ofs.open("./out.ppm", std::ios::binary); ofs << "P6\n" << width << " " << height << "\n255\n"; - for (size_t i = 0; i < height*width; ++i) { - Vec3f &c = framebuffer[i]; + for (vec3 &c : framebuffer) { float max = std::max(c[0], std::max(c[1], c[2])); if (max>1) c = c*(1./max); - for (size_t j = 0; j<3; j++) { - ofs << (char)(255 * std::max(0.f, std::min(1.f, framebuffer[i][j]))); - } + ofs << (char)(255 * c[0]) << (char)(255 * c[1]) << (char)(255 * c[2]); } ofs.close(); } int main() { - Material ivory(1.0, Vec4f(0.6, 0.3, 0.1, 0.0), Vec3f(0.4, 0.4, 0.3), 50.); - Material glass(1.5, Vec4f(0.0, 0.5, 0.1, 0.8), Vec3f(0.6, 0.7, 0.8), 125.); - Material red_rubber(1.0, Vec4f(0.9, 0.1, 0.0, 0.0), Vec3f(0.3, 0.1, 0.1), 10.); - Material mirror(1.0, Vec4f(0.0, 10.0, 0.8, 0.0), Vec3f(1.0, 1.0, 1.0), 1425.); + const Material ivory = {1.0, {0.6, 0.3, 0.1, 0.0}, {0.4, 0.4, 0.3}, 50.}; + const Material glass = {1.5, {0.0, 0.5, 0.1, 0.8}, {0.6, 0.7, 0.8}, 125.}; + const Material red_rubber = {1.0, {0.9, 0.1, 0.0, 0.0}, {0.3, 0.1, 0.1}, 10.}; + const Material mirror = {1.0, {0.0, 10.0, 0.8, 0.0}, {1.0, 1.0, 1.0}, 1425.}; - std::vector spheres; - spheres.push_back(Sphere(Vec3f(-3, 0, -16), 2, ivory)); - spheres.push_back(Sphere(Vec3f(-1.0, -1.5, -12), 2, glass)); - spheres.push_back(Sphere(Vec3f( 1.5, -0.5, -18), 3, red_rubber)); - spheres.push_back(Sphere(Vec3f( 7, 5, -18), 4, mirror)); + std::vector spheres = { + Sphere{vec3{-3, 0, -16}, 2, ivory}, + Sphere{vec3{-1.0, -1.5, -12}, 2, glass}, + Sphere{vec3{ 1.5, -0.5, -18}, 3, red_rubber}, + Sphere{vec3{ 7, 5, -18}, 4, mirror} + }; - std::vector lights; - lights.push_back(Light(Vec3f(-20, 20, 20), 1.5)); - lights.push_back(Light(Vec3f( 30, 50, -25), 1.8)); - lights.push_back(Light(Vec3f( 30, 20, 30), 1.7)); + std::vector lights = { + {{-20, 20, 20}, 1.5}, + {{ 30, 50, -25}, 1.8}, + {{ 30, 20, 30}, 1.7} + }; render(spheres, lights); - return 0; }