-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhittable.h
140 lines (106 loc) · 4.1 KB
/
hittable.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#ifndef HITTABLE_H
#define HITTABLE_H
#include "aabb.h"
#include "interval.h"
#include "ray.h"
class material;
class hit_record {
public:
point3 p;
vec3 normal;
std::shared_ptr<material> mat;
double t;
double u;
double v;
bool front_face;
void set_face_normal(const ray& r, const vec3& outward_normal) {
// Sets the hit record normal vector.
// NOTE: the parameter `outward_normal` is assumed to have unit length.
front_face = dot(r.direction(), outward_normal) < 0;
normal = front_face ? outward_normal : -outward_normal;
}
};
class hittable {
public:
virtual ~hittable() = default;
virtual bool hit(const ray& r, interval ray_t, hit_record& rec) const = 0;
virtual aabb bounding_box() const = 0;
};
class translate : public hittable {
public:
translate(std::shared_ptr<hittable> p, const vec3& displacement) : object(p), offset(displacement) {
bbox = object->bounding_box() + offset;
}
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
// Move the ray backwards by the offset
ray offset_r(r.origin() - offset, r.direction(), r.time());
// Determine where (if any) an intersection occurs along the offset ray
if (!object->hit(offset_r, ray_t, rec)) return false;
// Move the intersection point forwards by the offset
rec.p += offset;
return true;
}
aabb bounding_box() const override { return bbox; }
private:
std::shared_ptr<hittable> object;
vec3 offset;
aabb bbox;
};
class rotate_y : public hittable {
public:
rotate_y(std::shared_ptr<hittable> p, double angle) : object(p) {
auto radians = degrees_to_radians(angle);
sin_theta = sin(radians);
cos_theta = cos(radians);
bbox = object->bounding_box();
point3 min(infinity, infinity, infinity);
point3 max(-infinity, -infinity, -infinity);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
auto x = i * bbox.x.max + (1 - i) * bbox.x.min;
auto y = j * bbox.y.max + (1 - j) * bbox.y.min;
auto z = k * bbox.z.max + (1 - k) * bbox.z.min;
auto newx = cos_theta * x + sin_theta * z;
auto newz = -sin_theta * x + cos_theta * z;
vec3 tester(newx, y, newz);
for (int c = 0; c < 3; c++) {
min[c] = fmin(min[c], tester[c]);
max[c] = fmax(max[c], tester[c]);
}
}
}
}
bbox = aabb(min, max);
}
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
// Change the ray from world space to object space
auto origin = r.origin();
auto direction = r.direction();
origin[0] = cos_theta * r.origin()[0] - sin_theta * r.origin()[2];
origin[2] = sin_theta * r.origin()[0] + cos_theta * r.origin()[2];
direction[0] = cos_theta * r.direction()[0] - sin_theta * r.direction()[2];
direction[2] = sin_theta * r.direction()[0] + cos_theta * r.direction()[2];
ray rotated_r(origin, direction, r.time());
// Determine where (if any) an intersection occurs in object space
if (!object->hit(rotated_r, ray_t, rec)) return false;
// Change the intersection point from object space to world space
auto p = rec.p;
p[0] = cos_theta * rec.p[0] + sin_theta * rec.p[2];
p[2] = -sin_theta * rec.p[0] + cos_theta * rec.p[2];
// Change the normal from object space to world space
auto normal = rec.normal;
normal[0] = cos_theta * rec.normal[0] + sin_theta * rec.normal[2];
normal[2] = -sin_theta * rec.normal[0] + cos_theta * rec.normal[2];
rec.p = p;
rec.normal = normal;
return true;
}
aabb bounding_box() const override { return bbox; }
private:
std::shared_ptr<hittable> object;
double sin_theta;
double cos_theta;
aabb bbox;
};
#endif