diff --git a/Makefile b/Makefile index b5e5e50..2a8eb74 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -raytracer: camera.hpp color.hpp hittable.hpp hittable_list.hpp material.hpp ray.hpp rtweekend.hpp sphere.hpp vec3.hpp +raytracer: camera.hpp color.hpp hittable.hpp hittable_list.hpp main.cpp material.hpp ray.hpp rtweekend.hpp sphere.hpp vec3.hpp @g++ -g -O2 -Wall -Wextra -Wpedantic main.cpp -o raytracer image: raytracer diff --git a/camera.hpp b/camera.hpp index 3a2f1a8..38e7ceb 100644 --- a/camera.hpp +++ b/camera.hpp @@ -8,29 +8,45 @@ struct camera { point3 origin; point3 lower_left_corner; vec3 horizontal; - vec3 vertical; + vec3 vertical; + vec3 u,v,w; + double lens_radius; /* Constructors */ - camera() + camera(point3 lookfrom, + point3 lookat, + vec3 vup, + double vfov, + double aspect_ratio, + double aperture, + double focus_dist) { - double aspect_ratio = 16.0 / 9; - double viewport_height = 2.0; + double theta = degrees_to_radians(vfov); + double h = tan(theta/2); + double viewport_height = 2.0 * h; double viewport_width = aspect_ratio * viewport_height; - double focal_length = 1.0; - origin = vec3(0,0,0); - horizontal = vec3(viewport_width, 0, 0); - vertical = vec3(0, viewport_height, 0); - lower_left_corner = origin - horizontal/2 - vertical/2 - vec3(0, 0, focal_length); + w = normalize(lookfrom - lookat); + u = normalize(cross(vup,w)); + v = cross(w, u); + + origin = lookfrom; + horizontal = focus_dist * viewport_width * u; + vertical = focus_dist * viewport_height * v; + lower_left_corner = origin - horizontal/2 - vertical/2 - focus_dist*w; + + lens_radius = aperture/2; } /* Methods */ - ray get_ray(double u, double v) const + ray get_ray(double s, double t) const { - return ray(origin, lower_left_corner + u*horizontal + v*vertical - origin); + vec3 rd = lens_radius * random_in_unit_disk(); + vec3 offset = u * rd.x + v * rd.y; + + return ray(origin + offset, lower_left_corner + s*horizontal + t*vertical - origin - offset); }; - }; #endif diff --git a/main.cpp b/main.cpp index c7ca4f0..f6b7e83 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "rtweekend.hpp" @@ -8,8 +9,63 @@ #include "sphere.hpp" #include "camera.hpp" + color ray_color(const ray& r, const hittable& world, int32_t depth); double hit_sphere(const point3& center, double radius, const ray& r); +hittable_list random_scene(); + +hittable_list random_scene() { + hittable_list world; + + auto ground_material = make_shared(color(0.5, 0.5, 0.5)); + world.add(make_shared(point3(0,-1000,0), 1000, ground_material)); + + for (int32_t a = -11; a < 11; a++) + { + for (int32_t b = -11; b < 11; b++) + { + double choose_mat = random_double(); + point3 center(a + 0.9*random_double(), 0.2, b + 0.9*random_double()); + + if ((center - point3(4, 0.2, 0)).length() > 0.9) + { + shared_ptr sphere_material; + if (choose_mat < 0.8) + { + // diffuse + color albedo = color::random() * color::random(); + sphere_material = make_shared(albedo); + world.add(make_shared(center, 0.2, sphere_material)); + } + else if (choose_mat < 0.95) + { + // metal + color albedo = color::random(0.5, 1); + double fuzz = random_double(0, 0.5); + sphere_material = make_shared(albedo, fuzz); + world.add(make_shared(center, 0.2, sphere_material)); + } + else + { + // glass + sphere_material = make_shared(1.5); + world.add(make_shared(center, 0.2, sphere_material)); + } + } + } + } + + auto material1 = make_shared(1.5); + world.add(make_shared(point3(0, 1, 0), 1.0, material1)); + + auto material2 = make_shared(color(0.4, 0.2, 0.1)); + world.add(make_shared(point3(-4, 1, 0), 1.0, material2)); + + auto material3 = make_shared(color(0.7, 0.6, 0.5), 0.0); + world.add(make_shared(point3(4, 1, 0), 1.0, material3)); + + return world; +} color ray_color(const ray& r, const hittable& world, int32_t depth) { @@ -50,18 +106,16 @@ double hit_sphere(const point3& center, double radius, const ray& r) else return (-half_b - sqrt(discriminant)) / a; } - -int main() +int32_t main() { - // Image - double aspect_ratio = 16.0 / 9; - const int32_t image_width = 400; + const double aspect_ratio = 3.0 / 2.0; + const int32_t image_width = 1200; const int32_t image_height = (int32_t) (image_width / aspect_ratio); - int32_t samples_per_pixel = 100; - int32_t max_depth = 50; + int32_t samples_per_pixel = 500; + const int32_t max_depth = 50; if (getenv("SPP")) { @@ -71,21 +125,16 @@ int main() // World - hittable_list world; - - std::shared_ptr material_ground = make_shared(color(0.8, 0.8, 0.0)); - std::shared_ptr material_center = make_shared(color(0.7, 0.3, 0.3)); - std::shared_ptr material_left = make_shared(color(0.8, 0.8, 0.8), 0.3); - std::shared_ptr material_right = make_shared(color(0.8, 0.6, 0.2), 1.0); - - world.add(make_shared(point3( 0.0, -100.5, -1.0), 100.0, material_ground)); - world.add(make_shared(point3( 0.0, 0.0, -1.0), 0.5, material_center)); - world.add(make_shared(point3(-1.0, 0.0, -1.0), 0.5, material_left)); - world.add(make_shared(point3( 1.0, 0.0, -1.0), 0.5, material_right)); - + hittable_list world = random_scene(); // Camera - camera cam; + point3 lookfrom(13,2,3); + point3 lookat(0,0,0); + vec3 vup(0,1,0); + double dist_to_focus = 10.0; + double aperture = 0.1; + + camera cam(lookfrom, lookat, vup, 20, aspect_ratio, aperture, dist_to_focus); // Render printf("P3\n%d %d\n255\n", image_width, image_height); diff --git a/material.hpp b/material.hpp index b8f93e8..5792e38 100644 --- a/material.hpp +++ b/material.hpp @@ -54,4 +54,47 @@ struct metal : material { } }; +struct dielectric : material +{ + /* Attributes */ + double ri; // refraction index + + // Constructor + dielectric(double refraction_index) { ri = refraction_index; } + + /* Methods */ + + // Schlick's approximation of reflectance + static double reflectance(double cosine, double ref_idx) + { + double r0 = (1-ref_idx) / (1+ref_idx); + r0 = r0*r0; + return r0 + (1-r0)*pow((1 - cosine), 5); + } + + + /* Virtual methods */ + virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override + { + attenuation = color(1,1,1); + double refraction_ratio = rec.front_face ? (1.0/ri) : ri; + + vec3 unit_direction = normalize(r_in.direction); + + double cos_theta = fmin(dot(-unit_direction, rec.normal), 1); + double sin_theta = sqrt(1.0 - cos_theta*cos_theta); + + bool cannot_refract = refraction_ratio * sin_theta > 1.0; + vec3 direction; + + if (cannot_refract || reflectance(cos_theta, refraction_ratio) > random_double()) + direction = reflect(unit_direction, rec.normal); + else + direction = refract(unit_direction, rec.normal, refraction_ratio); + + scattered = ray(rec.p, direction); + return true; + } +}; + #endif diff --git a/vec3.hpp b/vec3.hpp index 751257b..8d51c82 100644 --- a/vec3.hpp +++ b/vec3.hpp @@ -188,4 +188,22 @@ vec3 reflect(const vec3& v, const vec3 n) return v - 2*dot(v,n)*n; } +vec3 refract (const vec3& uv, const vec3& n, double etai_over_etat) +{ + double cos_theta = fmin(dot(-uv, n), 1.0); + vec3 r_out_perp = etai_over_etat * (uv + cos_theta*n); + vec3 r_out_parallel = -sqrt(fabs(1.0 - r_out_perp.length_squared())) * n; + return r_out_perp + r_out_parallel; +} + +vec3 random_in_unit_disk() +{ + while (true) + { + auto p = vec3(random_double(-1,1), random_double(-1,1), 0); + if (p.length_squared() >= 1) continue; + return p; + } +} + #endif