From 58ffcff45924a444363caf1a395a68f92ff29cf0 Mon Sep 17 00:00:00 2001 From: Phireh Date: Sun, 22 Aug 2021 17:24:33 +0200 Subject: [PATCH] Add material support --- hittable.cpp | 16 ---------------- hittable.hpp | 17 ++--------------- main.cpp | 25 +++++++++++++++++++++---- material.hpp | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ rtweekend.hpp | 20 ++++++++++++++++++++ sphere.hpp | 5 ++++- vec3.hpp | 13 +++++++++++++ 7 files changed, 112 insertions(+), 36 deletions(-) delete mode 100644 hittable.cpp create mode 100644 material.hpp diff --git a/hittable.cpp b/hittable.cpp deleted file mode 100644 index c3b21d4..0000000 --- a/hittable.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef HITTABLE_H -#define HITTABLE_H - -#include "ray.h" - -struct hit_record { - point3 p; - vec3 normal; - double t; -}; - -struct hittable { - virtual bool hit(const ray& r, double t_min, double t_max, hit_record& rec) const = 0; -}; - -#endif diff --git a/hittable.hpp b/hittable.hpp index 42cc05e..6baef66 100644 --- a/hittable.hpp +++ b/hittable.hpp @@ -1,21 +1,8 @@ #ifndef HITTABLE_H #define HITTABLE_H -#include "ray.hpp" - -/* Representation of a hitpoint of a ray against a hittable object */ -struct hit_record { - point3 p; - vec3 normal; - double t; - bool front_face; - - inline void set_face_normal(const ray& r, const vec3& outward_normal) - { - front_face = dot(r.direction, outward_normal) < 0; - normal = front_face ? outward_normal : -outward_normal; - } -}; +#include "rtweekend.hpp" +#include "material.hpp" /* Virtual class that represents objects who could collide against a ray */ struct hittable { diff --git a/main.cpp b/main.cpp index 1c16754..4d3e218 100644 --- a/main.cpp +++ b/main.cpp @@ -21,8 +21,16 @@ color ray_color(const ray& r, const hittable& world, int32_t depth) hit_record rec; if (world.hit(r, 0.001, INFINITY, rec)) { - point3 target = rec.p + rec.normal + random_in_hemisphere(rec.normal); - return 0.5 * ray_color(ray(rec.p, target - rec.p), world, depth-1); + ray scattered; + color attenuation; + if (rec.mat_ptr->scatter(r, rec, attenuation, scattered)) + { + return attenuation * ray_color(scattered, world, depth-1); + } + else + { + return color(0,0,0); + } } vec3 unit_direction = normalize(r.direction); double t = 0.5 * (unit_direction.y + 1.0); @@ -64,8 +72,17 @@ int main() // World hittable_list world; - world.add(std::make_shared(point3(0,0,-1), 0.5)); - world.add(std::make_shared(point3(0,-100.5,-1), 100)); + + 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)); + std::shared_ptr material_right = make_shared(color(0.8, 0.6, 0.2)); + + 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)); + // Camera camera cam; diff --git a/material.hpp b/material.hpp new file mode 100644 index 0000000..2ff657e --- /dev/null +++ b/material.hpp @@ -0,0 +1,52 @@ +#ifndef MATERIAL_H +#define MATERIAL_H + +#include "rtweekend.hpp" + +struct material { + virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const = 0; +}; + +struct lambertian : material { + /* Attributes */ + color albedo; + // Constructor + lambertian(const color& c) { albedo = c; } + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-parameter" + virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override + { + vec3 scatter_direction = rec.normal + random_unit_vector(); + + /* NOTE: it is possible that the random vector we generate is exactly opposite to the normal vector, + in which case it will sum to a near-zero scatter vector and generate degenerate results. + We check for near-zero vectors here. + */ + + if (scatter_direction.near_zero()) + scatter_direction = rec.normal; + + scattered = ray(rec.p, scatter_direction); + attenuation = albedo; + return true; + } + #pragma GCC diagnostic pop +}; + +struct metal : material { + /* Attributes */ + color albedo; + // Constructor + metal(const color& c) { albedo = c; }; + + virtual bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered) const override + { + vec3 reflected = reflect(normalize(r_in.direction), rec.normal); + scattered = ray(rec.p, reflected); + attenuation = albedo; + return (dot(scattered.direction, rec.normal) > 0); + } +}; + +#endif diff --git a/rtweekend.hpp b/rtweekend.hpp index dcb562a..e996988 100644 --- a/rtweekend.hpp +++ b/rtweekend.hpp @@ -33,5 +33,25 @@ inline double clamp(double v, double min, double max) #include "ray.hpp" #include "vec3.hpp" +struct material; + +/* Common data structures */ + +struct hit_record { + point3 p; + vec3 normal; + std::shared_ptr mat_ptr; + double t; + bool front_face; + + inline void set_face_normal(const ray& r, const vec3& outward_normal) + { + front_face = dot(r.direction, outward_normal) < 0; + normal = front_face ? outward_normal : -outward_normal; + } +}; + + + #endif diff --git a/sphere.hpp b/sphere.hpp index f92d2ce..e0f5c6f 100644 --- a/sphere.hpp +++ b/sphere.hpp @@ -8,12 +8,14 @@ struct sphere : hittable { /* Attributes */ point3 center; double radius; + std::shared_ptr mat_ptr; /* Contructor */ - sphere(point3 c, double r) + sphere(point3 c, double r, std::shared_ptr m) { center = c; radius = r; + mat_ptr = m; } /* Virtual methods declaration */ @@ -46,6 +48,7 @@ bool sphere::hit(const ray& r, double t_min, double t_max, hit_record& rec) cons 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; } diff --git a/vec3.hpp b/vec3.hpp index 614938a..751257b 100644 --- a/vec3.hpp +++ b/vec3.hpp @@ -76,6 +76,13 @@ struct vec3 { { return vec3(random_double(min, max), random_double(min, max), random_double(min, max)); } + + // Check if all vector components are near zero + bool near_zero() const + { + double s = 1e-8; + return (fabs(x) < s) && (fabs(y) < s) && (fabs(z) < s); + } }; /* Type aliases */ @@ -175,4 +182,10 @@ vec3 random_in_hemisphere(const vec3& normal) return -in_unit_sphere; } +// Reflect like a metallic material +vec3 reflect(const vec3& v, const vec3 n) +{ + return v - 2*dot(v,n)*n; +} + #endif