-
Notifications
You must be signed in to change notification settings - Fork 0
/
Sphere.cpp
229 lines (214 loc) · 4.81 KB
/
Sphere.cpp
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
224
225
226
227
228
229
#include <math.h>
#include "precomp.h"
#include "Sphere.h"
// Defult Constructor, gives a radius 1 grey sphere centered at the orgin
Sphere::Sphere()
: GeoPrimitive(), c(Point3D(0.0)), r(1.0), color(RGBColor(0.5))
{
r2 = r * r;
}
// Construct from a Point3D, a float radius and a RGBColor
Sphere::Sphere( const Point3D& center,
const float& radius,
RGBColor& colorInput)
: GeoPrimitive(), c(center), r(radius), color(colorInput)
{
r2 = r * r;
}
// Copy Constructor
Sphere::Sphere(const Sphere& sphereInput)
: GeoPrimitive(), c(sphereInput.c), r(sphereInput.r), color(sphereInput.color), r2(sphereInput.r2)
{
}
// Destructor
Sphere::~Sphere()
{
}
// Assignment operator
Sphere& Sphere::operator= (const Sphere& sphereInput)
{
if (this == &sphereInput)
return *this;
c = sphereInput.c;
r = sphereInput.r;
r2 = sphereInput.r2;
color = sphereInput.color;
return *this;
}
// Color setter
void Sphere::set_color(const RGBColor& colorInput)
{
color = colorInput;
}
// Get color
RGBColor Sphere::get_color()
{
return color;
}
// Get Bounding Box
AABB Sphere::get_AABB() const
{
float delta = 0.0000001;
return AABB( c.x - r - delta,
c.y - r - delta,
c.z - r - delta,
c.x + r + delta,
c.y + r + delta,
c.z + r + delta );
}
// Get normal
Normal Sphere::get_normal(const Point3D& p3d)
{
Vector3D vec = p3d - this->c;
vec.normalize();
return Normal(vec);
}
// Generate a random Point3D on surface, the only case where Point3D p3d is used
Point3D Sphere::rand_pnt(const Point3D& p3d)
{
#if 0
// The cheap way, uses the hemisphere on p3d side
Vector3D vec;
do
{
vec = random_in_unit_sphere();
} while (vec*(p3d - this->c) <= 0.0f);
vec.normalize();
#else
// The expensive way, uses the Spherical Sector that p3d can see
Vector3D c2p = p3d - this->c; float distance = c2p.length();
Normal normal(c2p); normal.normalize();
ONB ONB_helper; ONB_helper.build_onb(normal);
Vector3D vec = ONB_helper.local(random_cosine_direction(this->r / distance));
#endif
return this->c + this->r * vec;
}
// Ray-Sphere hit function
bool Sphere::hit(Ray& ray) const
{
ray.counter++;
float t;
Vector3D temp = ray.o - this->c;
float a = ray.d * ray.d;
float b = 2.0f * temp * ray.d;
float c = temp * temp - this->r2;
float delta = b * b - 4.0f * a * c;
if (delta < 0.0f)
return(false);
else
{
float e = sqrtf(delta);
float denom = 2.0f * a;
t = (-b - e) / denom; // smaller root
if (t > EPSILON && t < ray.t)
{
ray.t = t;
//ray.counter++;
return true;
}
t = (-b + e) / denom; // larger root
if (t > EPSILON && t < ray.t)
{
ray.t = t;
//ray.counter++;
return true;
}
}
return false;
}
// Ray-Sphere hit function, another hit method, not yet optimized
bool Sphere::hit2(Ray& ray) const
{
Vector3D rayToCenter(c.x - ray.o.x, c.y - ray.o.y, c.z - ray.o.z);
float distanceSquared = rayToCenter * rayToCenter;
// If the ray origin is outside the Sphere
if (distanceSquared > r2)
{
float t_project = rayToCenter * ray.d;
Vector3D vec_RayToCenter = rayToCenter - t_project * ray.d;
float closestDistance = vec_RayToCenter * vec_RayToCenter;
if (closestDistance > r2)
return false;
t_project -= sqrtf(r2 - closestDistance);
if (t_project < ray.t && t_project > 0)
{
ray.t = t_project;
return true;
}
return false;
}
// Else the ray origin is inside / on surface of the sphere
else
{
float a = ray.d * ray.d;
float b = 2.0f * ray.d * (-1.0f * rayToCenter);
float c = distanceSquared - r2;
float delta = sqrtf(b * b - 4.0f * a * c);
float t1 = float((-1.0*b - delta) / (2.0f * a));
float t2 = float((-1.0*b + delta) / (2.0f * a));
if (t1 == t2)
{
if (t1 < 0)
return false;
else if (t1 < ray.t)
{
ray.t = t1;
return true;
}
else
return false;
}
else
{
float t_max = t1 > t2 ? t1 : t2;
float t_min = t1 > t2 ? t2 : t1;
if (t_max < 0)
{
return false;
}
else if (t_min > 0 && t_min < ray.t)
{
ray.t = t_min;
return true;
}
else if (t_max < ray.t)
{
ray.t = t_max;
return true;
}
else
return false;
}
}
}
// Ray-Sphere hit function
bool Sphere::hit(Ray& ray, RGBColor& colorOut) const
{
if (this->hit(ray))
{
colorOut = this->color;
return true;
}
return false;
}
// Ray-Sphere hit function, with hit point
bool Sphere::hit(Ray& ray, HitPoint& hitPoint) const
{
bool hit = this->hit(ray);
hitPoint.ray_counter = ray.counter;
if (hit)
{
hitPoint.ray_counter++;
hitPoint.hit = true;
hitPoint.color = this->color;
hitPoint.point = ray.o + ray.t * ray.d;
hitPoint.ray_t = ray.t;
hitPoint.normal = hitPoint.point - this->c;
hitPoint.normal.normalize();
hitPoint.material = this->material;
hitPoint.u = 0.5f + invTWO_PI * atan2(hitPoint.normal.z, hitPoint.normal.x);
hitPoint.v = 0.5f - invPI * asinf(hitPoint.normal.y);
return hit;
}
return hit;
}