summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeuin <[email protected]>2022-04-15 12:32:42 +0800
committerKeuin <[email protected]>2022-04-15 12:32:42 +0800
commit4193adbbff4a5b633f59f88367fe1b74aec7789b (patch)
tree25056ac9e028e268193615724dda8c3ac12317d0
parent2ed39ad4eb9ebf64768dbf4aeefafc5ebb072ca7 (diff)
Code Refactor:
- Add material class. - Move diffuse routine into separate material classes.
-rw-r--r--CMakeLists.txt4
-rw-r--r--hitlist.h21
-rw-r--r--main_simple_scanner.cpp6
-rw-r--r--material.cpp61
-rw-r--r--material.h48
-rw-r--r--object.h12
-rw-r--r--sphere.h10
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
diff --git a/hitlist.h b/hitlist.h
index 8625cb5..8073587 100644
--- a/hitlist.h
+++ b/hitlist.h
@@ -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
diff --git a/object.h b/object.h
index b6e2bbd..26548c5 100644
--- a/object.h
+++ b/object.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
diff --git a/sphere.h b/sphere.h
index f604b74..fa181e7 100644
--- a/sphere.h
+++ b/sphere.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 &center, double radius) : center(center), radius(radius) {}
+ sphere(const vec3d &center, 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}