#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; double fuzz; // Constructor metal(const color& c, double f) { albedo = c; fuzz = f; }; 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 + fuzz*random_in_unit_sphere()); attenuation = albedo; return (dot(scattered.direction, rec.normal) > 0); } }; 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