diff options
author | Keuin <[email protected]> | 2022-04-15 12:32:42 +0800 |
---|---|---|
committer | Keuin <[email protected]> | 2022-04-15 12:32:42 +0800 |
commit | 4193adbbff4a5b633f59f88367fe1b74aec7789b (patch) | |
tree | 25056ac9e028e268193615724dda8c3ac12317d0 | |
parent | 2ed39ad4eb9ebf64768dbf4aeefafc5ebb072ca7 (diff) |
Code Refactor:
- Add material class.
- Move diffuse routine into separate material classes.
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | hitlist.h | 21 | ||||
-rw-r--r-- | main_simple_scanner.cpp | 6 | ||||
-rw-r--r-- | material.cpp | 61 | ||||
-rw-r--r-- | material.h | 48 | ||||
-rw-r--r-- | object.h | 12 | ||||
-rw-r--r-- | sphere.h | 10 |
7 files changed, 138 insertions, 24 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 91b234c..7ff949a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,9 +12,9 @@ set(CMAKE_VERBOSE_MAKEFILE on) # main executable -add_executable(rt main.cpp vec.h bitmap.h ray.h bitfont.h hitlist.h object.h sphere.h viewport.h aa.h) +add_executable(rt main.cpp vec.h bitmap.h ray.h bitfont.h hitlist.h object.h sphere.h viewport.h aa.h material.h material.cpp) add_executable(image_output main_image_output.cpp vec.h bitmap.h bitfont.h hitlist.h object.h sphere.h viewport.h) -add_executable(simple_scanner main_simple_scanner.cpp vec.h bitmap.h ray.h timer.h bitfont.h hitlist.h object.h sphere.h viewport.h aa.h) +add_executable(simple_scanner main_simple_scanner.cpp vec.h bitmap.h ray.h timer.h bitfont.h hitlist.h object.h sphere.h viewport.h aa.h material.h material.cpp) # googletest @@ -78,21 +78,12 @@ public: return pixel<T>::from_normalized(nv); #endif #ifdef T_DIFFUSE - const auto hit_point = r.at(hit_t); // hit point, on the surface - auto nv = hit_obj->normal_vector(hit_point); - if (dot(nv, r.direction()) > 0) return pixel<T>::black(); // discard rays from inner (or invert nv) -#ifdef DIFFUSE_SIMPLE - vec3d diffuse_target = hit_point + nv + ruvg.range01(); -#endif -#ifdef DIFFUSE_LR - vec3d diffuse_target = hit_point + nv + ruvg.normalized(); -#endif -#ifdef DIFFUSE_HEMI - vec3d diffuse_target = hit_point + ruvg.hemisphere(nv); -#endif - r.decay(0.5); // lose 50% light when diffused - r.source(hit_point); r.direction((diffuse_target - hit_point).unit_vec()); // the new diffused ray we trace on - continue; + const auto &materi = hit_obj->material(); + if (materi.scatter(r, *hit_obj, hit_t, ruvg)) { + continue; // The ray is scatted by an object. Continue processing the scattered ray. + } else { + return pixel<T>::black(); // The ray is absorbed by an object completely. Return black. + } #endif } diff --git a/main_simple_scanner.cpp b/main_simple_scanner.cpp index 806fcfe..f0a2099 100644 --- a/main_simple_scanner.cpp +++ b/main_simple_scanner.cpp @@ -14,6 +14,7 @@ #include "hitlist.h" #include "sphere.h" #include "aa.h" +#include "material.h" #define DEMO_BALL @@ -35,11 +36,12 @@ void generate_image(uint16_t image_width, uint16_t image_height, double viewport } else { vp = new aa_viewport<T>{viewport_width, viewport_width / r, vec3d{0, 0, -focal_length}, samples}; } + material_diffuse_lambertian materi{0.5}; hitlist world; world.add_object(std::make_shared<sphere>( vec3d{0, -100.5, -1}, - 100)); // the earth - world.add_object(std::make_shared<sphere>(vec3d{0, 0, sphere_z}, sphere_r)); + 100, materi)); // the earth + world.add_object(std::make_shared<sphere>(vec3d{0, 0, sphere_z}, sphere_r, materi)); timer tm; std::cerr << "Rendering..." << std::endl; tm.start_measure(); diff --git a/material.cpp b/material.cpp new file mode 100644 index 0000000..f92b02c --- /dev/null +++ b/material.cpp @@ -0,0 +1,61 @@ +// +// Created by Keuin on 2022/4/15. +// + +#include "material.h" + + +bool material_diffuse_lambertian::scatter(ray3d &r, const object &hit_obj, double hit_t, random_uv_gen_3d &ruvg) const { + const auto hit_point = r.at(hit_t); // hit point, on the surface + auto nv = hit_obj.normal_vector(hit_point); + if (dot(nv, r.direction()) > 0) return false; // discard rays from inner (or invert nv) + auto d = nv + ruvg.normalized(); + if (d.is_zero()) d = nv; + vec3d diffuse_target = hit_point + d; + r.decay(albedo); // lose some light when diffused + r.source(hit_point); + r.direction((diffuse_target - hit_point).unit_vec()); // the new diffused ray we trace on + return true; +} + +material_diffuse_lambertian::material_diffuse_lambertian(double albedo) : albedo(albedo) { + assert(albedo >= 0); + assert(albedo <= 1); +} + +material_diffuse_simple::material_diffuse_simple(double albedo) : albedo(albedo) { + assert(albedo >= 0); + assert(albedo <= 1); +} + +bool material_diffuse_simple::scatter(ray3d &r, const object &hit_obj, double hit_t, random_uv_gen_3d &ruvg) const { + const auto hit_point = r.at(hit_t); // hit point, on the surface + auto nv = hit_obj.normal_vector(hit_point); + if (dot(nv, r.direction()) > 0) return false; // discard rays from inner (or invert nv) + auto d = nv + ruvg.normalized(); + if (d.is_zero()) d = nv; + vec3d diffuse_target = hit_point + nv + ruvg.range01(); + r.decay(albedo); // lose some light when diffused + r.source(hit_point); + r.direction((diffuse_target - hit_point).unit_vec()); // the new diffused ray we trace on + return true; +} + +material_diffuse_hemispherical::material_diffuse_hemispherical(double albedo) : albedo(albedo) { + assert(albedo >= 0); + assert(albedo <= 1); +} + +bool +material_diffuse_hemispherical::scatter(ray3d &r, const object &hit_obj, double hit_t, random_uv_gen_3d &ruvg) const { + const auto hit_point = r.at(hit_t); // hit point, on the surface + auto nv = hit_obj.normal_vector(hit_point); + if (dot(nv, r.direction()) > 0) return false; // discard rays from inner (or invert nv) + auto d = nv + ruvg.normalized(); + if (d.is_zero()) d = nv; + vec3d diffuse_target = hit_point + ruvg.hemisphere(nv); + r.decay(albedo); // lose some light when diffused + r.source(hit_point); + r.direction((diffuse_target - hit_point).unit_vec()); // the new diffused ray we trace on + return true; +} diff --git a/material.h b/material.h new file mode 100644 index 0000000..a1d03db --- /dev/null +++ b/material.h @@ -0,0 +1,48 @@ +// +// Created by Keuin on 2022/4/15. +// + +#ifndef RT_MATERIAL_H +#define RT_MATERIAL_H + +#include "object.h" + +class object; + +// A material is of some characteristics that manipulates the lighted ray in some certain ways. +// An object can have one certain subclass of material, making it seemed to be different with other objects. +// A material instance can be configured when constructing. It should be immutable and thread-safe. +// It is suggested not to have mutable states in its internal implementation. +// User should have a global table containing references to all the materials the scene is using. +class material { +public: + // If this matter scatters, manipulate r and return true. Otherwise return false (the light is absorbed entirely). + // To make it thread-safe without locking mechanism, private states are passed-in as parameters. + virtual bool scatter(ray3d &r, const object &hit_obj, double hit_t, random_uv_gen_3d &ruvg) const = 0; +}; + +class material_diffuse_lambertian : public material { + double albedo; +public: + explicit material_diffuse_lambertian(double albedo); + + bool scatter(ray3d &r, const object &hit_obj, double hit_t, random_uv_gen_3d &ruvg) const override; +}; + +class material_diffuse_simple : public material { + double albedo; +public: + explicit material_diffuse_simple(double albedo); + + bool scatter(ray3d &r, const object &hit_obj, double hit_t, random_uv_gen_3d &ruvg) const override; +}; + +class material_diffuse_hemispherical : public material { + double albedo; +public: + explicit material_diffuse_hemispherical(double albedo); + + bool scatter(ray3d &r, const object &hit_obj, double hit_t, random_uv_gen_3d &ruvg) const override; +}; + +#endif //RT_MATERIAL_H @@ -5,12 +5,10 @@ #ifndef RT_OBJECT_H #define RT_OBJECT_H -#include "hitlist.h" -#include "viewport.h" -#include "timer.h" -#include "bitmap.h" +#include "material.h" #include "ray.h" #include "vec.h" +#include "bitmap.h" #include <cstdlib> #include <memory> #include <limits> @@ -18,6 +16,8 @@ #include <iostream> #include <cstdint> +class material; + class object { public: // Will the given ray hit. Returns time t if hits in range [t1, t2]. @@ -36,6 +36,10 @@ public: // subclasses must have virtual destructors virtual ~object() = default; + + // Get this object's material. + virtual material &material() const = 0; }; + #endif //RT_OBJECT_H @@ -10,6 +10,7 @@ #include "viewport.h" #include "timer.h" #include "bitmap.h" +#include "material.h" #include "ray.h" #include "vec.h" #include <cstdlib> @@ -19,14 +20,17 @@ #include <iostream> #include <cstdint> +class material; + class sphere : public object { vec3d center; double radius; + class material &materi; public: sphere() = delete; - sphere(const vec3d ¢er, double radius) : center(center), radius(radius) {} + sphere(const vec3d ¢er, double radius, class material &materi) : center(center), radius(radius), materi(materi) {} ~sphere() override = default; @@ -35,6 +39,10 @@ public: return (where - center) / radius; } + class material &material() const override { + return materi; + } + bool hit(const ray3d &r, double &t, double t1, double t2) const override { // Ray: {Source, Direction, time} // Sphere: {Center, radius} |