diff --git a/Makefile b/Makefile index afd67b5..6267335 100644 --- a/Makefile +++ b/Makefile @@ -2,15 +2,15 @@ CXX = g++ CXXFLAGS = -std=c++20 -Wall -Wextra -Wno-unused-parameter -march=native -O3 -flto -fopenmp -DEPS = util.h vec3.h color.h ray.h camera.h hittable.h hittable_list.h sphere.h material.h lodepng.h moving_sphere.h bvh.h aabb.h -OBJ = main.o material.o vec3.o lodepng.o moving_sphere.o bvh.o +DEPS = util.h vec3.h color.h ray.h camera.h hittable.h hittable_list.h sphere.h material.h lodepng.h moving_sphere.h bvh.h aabb.h texture.h +OBJ = main.o material.o vec3.o lodepng.o sphere.o moving_sphere.o bvh.o TARGET = toytracer all: $(TARGET) run run: $(TARGET) - time -f '%E elapsed' ./$(TARGET) + time -f '%E elapsed %M Kb memory' ./$(TARGET) eog image.png %.o: %.cpp $(DEPS) diff --git a/hittable.h b/hittable.h index caa95e1..2d40de3 100644 --- a/hittable.h +++ b/hittable.h @@ -14,6 +14,8 @@ struct hit_record { Vec3 normal; std::shared_ptr mat_ptr; double t; + double u; + double v; bool front_face; inline void set_face_normal(const Ray& r, const Vec3& outward_normal) { diff --git a/main.cpp b/main.cpp index f2ce875..115b234 100644 --- a/main.cpp +++ b/main.cpp @@ -39,7 +39,12 @@ Color ray_color(const Ray& r, const Hittable& world, int depth) { Bvh_node setup_random_scene(const int sph_i) { Hittable_list world; - auto ground_material = std::make_shared(Color(0.5, 0.5, 0.5)); + //auto ground_material = std::make_shared(std::make_shared(Color(0.5, 0.5, 0.5))); + auto checker = std::make_shared( + std::make_shared(0.2, 0.3, 0.1), + std::make_shared(0.9, 0.9, 0.9) + ); + auto ground_material = std::make_shared(checker); world.add(std::make_shared(Point3(0, -1000, 0), 1000, ground_material)); for (int a = -sph_i; a(albedo); - auto center2 = center + Vec3(0, random_double(0, 0.5), 0); + sphere_material = std::make_shared(std::make_shared(albedo)); + auto center2 = center + Vec3(0, random_double(0, 0.1), 0); world.add(std::make_shared(center, center2, 0.0, 1.0, 0.2, sphere_material)); } - if (choose_mat < 0.7) { + else if (choose_mat < 0.7) { // diffuse auto albedo = Color::random() * Color::random(); - sphere_material = std::make_shared(albedo); + sphere_material = std::make_shared(std::make_shared(albedo)); world.add(std::make_shared(center, 0.2, sphere_material)); } else if (choose_mat < 0.95) { // metal @@ -79,7 +84,7 @@ Bvh_node setup_random_scene(const int sph_i) { auto material1 = std::make_shared(1.45); world.add(std::make_shared(Point3(0, 1, 0), 1.0, material1)); - auto material2 = std::make_shared(Color(0.4, 0.2, 0.1)); + auto material2 = std::make_shared(std::make_shared(Color(0.4, 0.2, 0.1))); world.add(std::make_shared(Point3(-4, 1, 0), 1.0, material2)); auto material3 = std::make_shared(Color(0.7, 0.6, 0.5), 0.0); @@ -88,6 +93,22 @@ Bvh_node setup_random_scene(const int sph_i) { return world.generate_bvh(0, 1); } +Hittable_list two_spheres() { + Hittable_list objects; + + auto checker = std::make_shared( + std::make_shared(0.2, 0.3, 0.1), + std::make_shared(0.9, 0.9, 0.9) + ); + + auto mat = std::make_shared(checker); + + objects.add(std::make_shared(Point3(0, -10, 0), 10, mat)); + objects.add(std::make_shared(Point3(0, 10, 0), 10, mat)); + + return objects; +} + struct render_tile { int start_x; int start_y; @@ -107,8 +128,8 @@ int main() { const int samples_per_pixel = 400; const int max_depth = 50; - //const int tile_size = 128; - const int tile_size = 64; + const int tile_size = 128; + //const int tile_size = 64; //const int sph_i = 3; //const int sph_i = 5; @@ -116,14 +137,15 @@ int main() { //const int sph_i = 11; auto world = setup_random_scene(sph_i); + //auto world = two_spheres(); Point3 lookfrom(13, 2, 3); Point3 lookat(0, 0, 0); Vec3 vup(0, 1, 0); //auto dist_to_focus = (lookfrom - lookat).length(); auto dist_to_focus = 10; - //auto aperture = 0.1; - auto aperture = 0.0; + auto aperture = 0.1; + //auto aperture = 0.0; Camera cam(lookfrom, lookat, vup, 20, aspect_ratio, aperture, dist_to_focus, 0.0, 1.0); diff --git a/material.cpp b/material.cpp index b70b546..f3a6920 100644 --- a/material.cpp +++ b/material.cpp @@ -7,7 +7,7 @@ bool Lambertian::scatter(const Ray& r_in, const hit_record& rec, Color& attenuat Vec3 scatter_direction = rec.normal + random_unit_vector(); //Vec3 scatter_direction = random_in_hemisphere(rec.normal); scattered = Ray(rec.p, scatter_direction, r_in.time()); - attenuation = albedo; + attenuation = albedo->value(rec.u, rec.v, rec.p); return true; } diff --git a/material.h b/material.h index e223e1d..ab607be 100644 --- a/material.h +++ b/material.h @@ -3,6 +3,7 @@ #include "hittable.h" #include "vec3.h" +#include "texture.h" struct hit_record; @@ -13,12 +14,12 @@ public: class Lambertian : public Material { public: - Lambertian(const Color& a) : albedo(a) {} + Lambertian(std::shared_ptr a) : albedo(a) {} virtual bool scatter(const Ray& r_in, const hit_record& rec, Color& attenuation, Ray& scattered) const; private: - Color albedo; + std::shared_ptr albedo; }; class Metal : public Material { diff --git a/moving_sphere.cpp b/moving_sphere.cpp index c947431..802a713 100644 --- a/moving_sphere.cpp +++ b/moving_sphere.cpp @@ -1,10 +1,12 @@ #include "moving_sphere.h" +#include "sphere.h" Point3 Moving_sphere::center(double time) const { return center0 + ((time - time0) / (time1 - time0)) * (center1 - center0); } bool Moving_sphere::hit(const Ray& r, double tmin, double tmax, hit_record& rec) const { + compute_sphere_uv((rec.p - center(r.time()))/radius, rec.u, rec.v); Vec3 oc = r.origin() - center(r.time()); auto a = r.direction().length_squared(); auto half_b = dot(oc, r.direction()); diff --git a/sphere.cpp b/sphere.cpp new file mode 100644 index 0000000..2133d13 --- /dev/null +++ b/sphere.cpp @@ -0,0 +1,39 @@ +#include "sphere.h" + +bool Sphere::hit(const Ray& r, double tmin, double tmax, hit_record& rec) const { + compute_sphere_uv((rec.p - center)/radius, rec.u, rec.v); + Vec3 oc = r.origin() - center; + auto a = r.direction().length_squared(); + auto half_b = dot(oc, r.direction()); + auto c = oc.length_squared() - radius * radius; + auto discriminant = half_b*half_b - a*c; + if (discriminant > 0) { + auto root = std::sqrt(discriminant); + auto temp = (-half_b - root) / a; + if (temp < tmax && temp > tmin) { + rec.t = temp; + rec.p = r.at(rec.t); + Vec3 outward_normal = (rec.p - center) / radius; + rec.set_face_normal(r, outward_normal); + rec.mat_ptr = mat_ptr; + return true; + } + temp = (-half_b + root) / a; + if (temp < tmax && temp > tmin) { + rec.t = temp; + rec.p = r.at(rec.t); + Vec3 outward_normal = (rec.p - center) / radius; + rec.set_face_normal(r, outward_normal); + rec.mat_ptr = mat_ptr; + return true; + } + } + return false; +} + +bool Sphere::bounding_box(double t0, double t1, Aabb& output_box) const { + output_box = Aabb( + center - Vec3(radius, radius, radius), + center + Vec3(radius, radius, radius)); + return true; +} diff --git a/sphere.h b/sphere.h index cfa846f..3c41021 100644 --- a/sphere.h +++ b/sphere.h @@ -7,6 +7,7 @@ #include "hittable.h" #include "material.h" #include "vec3.h" +#include "util.h" class Sphere : public Hittable { public: @@ -22,42 +23,11 @@ private: std::shared_ptr mat_ptr; }; -bool Sphere::hit(const Ray& r, double tmin, double tmax, hit_record& rec) const { - Vec3 oc = r.origin() - center; - auto a = r.direction().length_squared(); - auto half_b = dot(oc, r.direction()); - auto c = oc.length_squared() - radius * radius; - auto discriminant = half_b*half_b - a*c; - if (discriminant > 0) { - auto root = std::sqrt(discriminant); - auto temp = (-half_b - root) / a; - if (temp < tmax && temp > tmin) { - rec.t = temp; - rec.p = r.at(rec.t); - Vec3 outward_normal = (rec.p - center) / radius; - rec.set_face_normal(r, outward_normal); - rec.mat_ptr = mat_ptr; - return true; - } - temp = (-half_b + root) / a; - if (temp < tmax && temp > tmin) { - rec.t = temp; - rec.p = r.at(rec.t); - Vec3 outward_normal = (rec.p - center) / radius; - rec.set_face_normal(r, outward_normal); - rec.mat_ptr = mat_ptr; - return true; - } - } - return false; +inline void compute_sphere_uv(const Vec3& p, double& u, double& v) { + auto phi = std::atan2(p.z(), p.x()); + auto theta = asin(p.y()); + u = 1 - (phi + pi) / (2 * pi); + v = (theta + pi/2) / pi; } -bool Sphere::bounding_box(double t0, double t1, Aabb& output_box) const { - output_box = Aabb( - center - Vec3(radius, radius, radius), - center + Vec3(radius, radius, radius)); - return true; -} - - #endif // SPHERE_H diff --git a/texture.h b/texture.h new file mode 100644 index 0000000..bd9b490 --- /dev/null +++ b/texture.h @@ -0,0 +1,46 @@ +#ifndef TEXTURE_H +#define TEXTURE_H + +#include + +#include "vec3.h" + +class Texture { + public: + virtual Color value(double u, double v, const Point3& p) const = 0; +}; + +class Solid_color : public Texture { + public: + Solid_color() {} + Solid_color(Color c) : color_value(c) {} + + Solid_color(double red, double green, double blue) + : Solid_color(Color(red, green, blue)) {} + + virtual Color value(double u, double v, const Vec3& p) const { + return color_value; + } + + private: + Color color_value; +}; + +class Checker_texture : public Texture { + public: + Checker_texture() {} + Checker_texture(std::shared_ptr t0, std::shared_ptr t1) : even(t0), odd(t1) {} + + virtual Color value(double u, double v, const Vec3& p) const { + auto sines = std::sin(10*p.x())*std::sin(10*p.y())*std::sin(10*p.z()); + if (sines < 0) + return odd->value(u, v, p); + else + return even->value(u, v, p); + } + private: + std::shared_ptr even; + std::shared_ptr odd; +}; + +#endif // TEXTURE_H diff --git a/util.h b/util.h index ad69adc..c26f25d 100644 --- a/util.h +++ b/util.h @@ -14,7 +14,8 @@ inline double degrees_to_radians(double degrees) { inline double random_double() { static std::uniform_real_distribution distribution(0.0, 1.0); - static std::mt19937 generator(std::time(nullptr)); + //static std::mt19937 generator(std::time(nullptr)); + static std::mt19937 generator(std::random_device{}()); return distribution(generator); }