Sphere
We know that for a sphere
, we have
where \(\vec C\) is the position vector
1 for the center
and \(\vec P\) is the position vector
1 where a ray
2 hits the sphere
.
Using dot product
1
Putting the parametric equation for ray
2
If we try to open it up, we get
We are trying to solve for \(t\) here.
Notice how \(b\) has a factor of \(-2h\).
Where
Using the quadratic equation
.
We can use discriminant
to find the nature of the roots,
Let \(D\) be discriminant
such that
then
For no hit
For one hit
For two hits
bool hit()
This function
depends a lot on the discriminant
and quadratic formula
, first we show the relation between the mathematical terms and the code terms.
- \(\vec A\) is
r.origin()
- \(\vec b\) is
r.direction()
- \(\vec C\) is
center
- \(\vec C - \vec A\) is therefore,
center - r.origin()
- \(h = \vec b \cdot (\vec C - \vec A)\) is
dot(r.origin(), oc)
- \(c = (\vec C - \vec A) \cdot (\vec C - \vec A) - r^2\) is
oc.length_squared() - radius * radius
Explanation for \(c\)
The discriminant
vec3 oc = center - r.origin();
auto a = r.direction().length_squared();
auto h = dot(r.direction(), oc);
auto c = oc.length_squared() - radius*radius;
auto discriminant = h*h - a*c;
Skipping processing if the ray
2 does not even hits the sphere
. i.e. the roots are imaginary
.
if (discriminant < 0)
return false;
We know that roots
are going to be
auto sqrtd = std::sqrt(discriminant);
// Find the nearest root that lies in the acceptable range.
auto root = (h - sqrtd) / a;
Then we are checking if both of the roots
are within the acceptable interval
3 or not.
if (!ray_t.surrounds(root)){
root = (h + sqrtd) / a;
if (!ray_t.surrounds(root))
return false;
}
If both roots
are not in the interval
,3 we are skipping these roots
.
Otherwise, we record how far did the ray
2 hit the sphere
.
rec.t = root;
Then also record where did the ray
2 hit in space.
rec.p = r.at(rec.t);
Then the surface normal
will be
Where \(\vec P\) is where the ray
2 hit,
being the head of resultant vector
1 and \(\vec C\) being a position vector
1 for center
, being the tail of resultant vector
.1
rec.normal = (rec.p - center);
To convert it to a unit vector
,1 we will divide by the radius
of the sphere
.
rec.normal = (rec.p - center) / radius;
Set the normal
to always face towards the camera
.
vec3 outward_normal = rec.normal;
rec.set_face_normal(r, outward_normal);
Hence the full code becomes
bool sphere::hit(const ray& r, interval ray_t, hit_record& rec) const override {
vec3 oc = center - r.origin();
auto a = r.direction().length_squared();
auto h = dot(r.direction(), oc);
auto c = oc.length_squared() - radius*radius;
auto discriminant = h*h - a*c;
if (discriminant < 0)
return false;
auto sqrtd = std::sqrt(discriminant);
// Find the nearest root that lies in the acceptable range.
auto root = (h - sqrtd) / a;
if (!ray_t.surrounds(root)){
root = (h + sqrtd) / a;
if (!ray_t.surrounds(root))
return false;
}
rec.t = root;
rec.p = r.at(rec.t);
rec.normal = (rec.p - center) / radius;
vec3 outward_normal = rec.normal;
rec.set_face_normal(r, outward_normal);
rect.mat = mat;
return true;
}