-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsphere.h
223 lines (193 loc) · 7.86 KB
/
sphere.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#pragma once
#include <cmath>
#include <memory>
#include "rt.h"
class sphere : public hittable
{
private:
double rad;
vec3 cent;
std::shared_ptr<material> mat;
bool is_moving;
vec3 moving_vec;
AABB bbox;
void get_sphere_uv(const point3&p, double& u, double& v) const
{
// p : a given point on the sphere of r : 1, center at the (0, 0, 0)
// u : returned value [0, 1] of angle around the Y axis from x = -1
// v : returned value [0, 1] of angle from Y = -1 to Y = 1
auto theta = std::acos(-p.y());
auto phi = std::atan2(-p.z(), p.x()) + pi;
u = phi / (2 * pi);
v = theta / pi;
}
public:
sphere() : rad(1.0), cent(1.0) {
mat = std::make_shared<lambertian>(color(0.4f, 0.4f, 0.4f));
}
sphere(double r, const vec3& vec) : rad(
std::fmax(0, r)), cent(vec) {
// TODO : Initialize the material pointer "mat"
// To indicate the specific material the sphere is
// The Default material is lambertian
mat = std::make_shared<lambertian>(color(0.4f, 0.4f, 0.4f));
}
// Stationary sphere
template<typename material_type>
sphere(double r, const vec3& vec, material_type&& mat) :
rad(std::fmax(0, r)), cent(vec), mat(std::forward<material_type>(mat)), is_moving(false)
{
// Construct the AABB of the stationary sphere
vec3 rvec = vec3(rad, rad, rad);
bbox = AABB(cent - rvec, cent + rvec);
}
// Moving sphere
template<typename material_type>
sphere(double r, const vec3& center1, const vec3& center2, material_type&& mat) :
rad(std::fmax(0, r)), cent(center1), mat(std::forward<material_type>(mat)), is_moving(true),
moving_vec(center2 - center1)
{
// Construct the AABB of the moving sphere
auto rvec = vec3(rad, rad, rad);
AABB bbox1(center1 - rvec, center1 + rvec);
AABB bbox2(center2 - rvec, center2 + rvec);
bbox = merge_two_AABB(bbox1, bbox2);
}
double radius() const noexcept
{
return rad;
}
const vec3& center() const noexcept
{
return cent;
}
double area() const noexcept
{
return pi * rad * rad;
}
bool hit_sphere(const ray& r) const noexcept
{
vec3 oc = cent - r.origin();
auto a = dot(r.direction(), r.direction());
// never use variable b will be optimized out
auto b = -2.0f * dot(r.direction(), oc);
auto h = dot(r.direction(), oc);
auto c = dot(oc, oc) - rad * rad;
// auto discrim = b * b - 4.0f * a * c;
auto discrim = h * h - a * c;
return discrim >= 0;
}
double hit_sphere_return_hit_point(const ray& r)
{
auto center = cent;
auto radius = rad;
vec3 oc = center - r.origin();
auto a = dot(r.direction(), r.direction());
// never use variable b will be optimized out
auto b = -2.0f * dot(r.direction(), oc);
auto h = dot(r.direction(), oc);
auto c = dot(oc, oc) - radius * radius;
// auto discrim = b * b - 4.0f * a * c;
auto discrim = h * h - a * c;
if(discrim < 0)
{
// the hit point is behind the camera
return -100.0f;
}
else
{
// else there is a hit point - return the nearest hit point
// return (- b - std::sqrt(discrim)) / (2.0f * a);
return (h - std::sqrt(discrim)) / a;
}
}
bool hit(const ray& r, double ray_tmin, double ray_tmax, hit_record& record) const override
{
// update the sphere center if it is moving
// what time the ray is generated, that time is the ray hit the sphere
vec3 sphere_center = is_moving ? this->sphere_center(r.get_time()) : cent;
// vec3 oc = cent - r.origin();
vec3 oc = sphere_center - r.origin();
auto a = r.direction().length_squared();
auto h = dot(r.direction(), oc);
auto c = oc.length_squared() - rad * rad;
auto discriminator = h * h - a * c;
if(discriminator < 0) return false;
auto sqrt_discriminator = std::sqrt(discriminator);
// Find the nearest root that lies in the acceptable range
auto root = (h - sqrt_discriminator) / a;
if(root <= ray_tmin || ray_tmax <= root)
{
root = (h + sqrt_discriminator) / a;
if(root <= ray_tmin || ray_tmax <= root)
{
return false;
}
}
// For Sphere the t_min hit_point_coord hit_point_normal can be calculated as
record.t = root;
record.p = r.at(record.t);
auto outside_normal = normalize(record.p - cent);
// auto outside_normal = (record.p - cent) / rad;
record.set_face_normal(r, outside_normal);
// when hit -> update the hit point texture coord u and v
get_sphere_uv(outside_normal, record.u, record.v);
record.mat = mat;
//record.normal = outside_normal;
return true;
}
bool hit(const ray& r, interval inter, hit_record& record) const override
{
// update the sphere center if it is moving
// what time the ray is generated, that time is the ray hit the sphere
vec3 sphere_center = is_moving ? this->sphere_center(r.get_time()) : cent;
// vec3 oc = cent - r.origin();
vec3 oc = sphere_center - r.origin();
auto a = r.direction().length_squared();
auto h = dot(r.direction(), oc);
auto c = oc.length_squared() - rad * rad;
auto discriminator = h * h - a * c;
if(discriminator < 0) return false;
auto sqrt_discriminator = std::sqrt(discriminator);
// Find the nearest root that lies in the acceptable range
auto root = (h - sqrt_discriminator) / a;
// inter.surround_noequ(root) => root < tmax && root > tmin
// !inter.surround_noequ(root) => root >= tmax && root <= tmin
if(! inter.surround_noequ(root))
{
root = (h + sqrt_discriminator) / a;
if(! inter.surround_noequ(root))
{
return false;
}
}
// For Sphere the t_min hit_point_coord hit_point_normal can be calculated as
record.t = root;
record.p = r.at(record.t);
auto outside_normal = normalize(record.p - cent);
// auto outside_normal = (record.p - cent) / rad;
record.set_face_normal(r, outside_normal);
// When Hit -> Update the hit point texture coord u and v
get_sphere_uv(outside_normal, record.u, record.v);
record.mat = mat;
//record.normal = outside_normal;
return true;
}
point3 sphere_center(double t) const
{
// the sphere is moving with a constant velocity
// Linearly interpolate the sphere center between the start and end points
if(is_moving)
{
return cent + t * moving_vec;
}
else
{
return cent;
}
}
AABB bounding_box() const override
{
return bbox;
}
};