참고자료:
https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
- 구의 식과 직선의 식을 d에 대해서 풀면 구와 직선의 intersection에 대해 구할 수 있다.
- 두 식을 연립방정식의 해를 구하듯이 구할 수 있다.
- 식을 정리하면 2차 방정식같은 형태가 나오고, 이를 근의 공식으로 구한다.
- ∇나블라(nabla) 를 구한다음 이를 이용해, d를 구한다.
- ∇>0: 만나는 점이 2개, +- 에 대해 모두 각각 따로 구해야 한다.
- ∇==0: 만나는 점이 1개
- ∇<0: 만나지 않는다
- d 값이 음수면 충돌하지 않은 것
위 방식을 그래픽스의 3차원 구 그리기에 활용할 수 있다.
이때는
u : direction of line (a non-zero vector) 의 값을 유닛 벡터로 사용한다.
따라서 ||u||^2 의 값은 1로 단순화 할 수 있다.
이 방식으로 ray를 쏴서 구에 hit 하는 지점까지의 거리를 구할 수 있다.
-> d값이 2개라면 작은 값을 선택하면 된다(가까운 쪽에 먼저 닿을거고, 먼쪽은 가까운쪽을 뚫고 나올때 부딪히는 부분일테니까)
class Sphere
{
public:
glm::vec3 center;
float radius;
glm::vec3 color; // 뒤에서 '재질(material)'로 확장
Sphere(const glm::vec3 ¢er, const float radius, const glm::vec3 &color)
: center(center), color(color), radius(radius)
{
}
// Wikipedia Line–sphere intersection
// https://en.wikipedia.org/wiki/Line-sphere_intersection
Hit IntersectRayCollision(Ray &ray)
{
Hit hit = Hit{-1.0f, vec3(0.0f), vec3(0.0f)}; // d가 음수이면 충돌을 안한 것으로 가정
/*
* hit.d = ... // 광선의 시작점으로부터 충돌지점까지의 거리 (float)
* hit.point = ... // 광선과 구가 충돌한 지점의 위치 (vec3)
* hit.normal = .. // 충돌 지점에서 구의 단위 법선 벡터(unit normal vector)
*/
// Nabla = (origin - center)^2 - (||origin - center||^2 + radius^2)
const float nabla = pow(glm::dot(ray.dir, ray.start - this->center),2) - (pow(glm::length(ray.start - this->center), 2) - pow(this->radius, 2));
// d = -direction * (origin - center) +- sqrt(nabla)
if (nabla < 0.0f)
{
return hit; // 광선이 구와 충돌하지 않음
}
else if (nabla == 0.0f)
{
hit.d = -glm::dot(ray.dir, ray.start - this->center);
hit.point = ray.start + ray.dir * hit.d;
hit.normal = glm::normalize(hit.point - center);
}
else
{
// 두 값중 작은 값이 더 가까운 충돌 지점
const float d1 = -glm::dot(ray.dir, ray.start - this->center) + sqrt(nabla);
const float d2 = -glm::dot(ray.dir, ray.start - this->center) - sqrt(nabla);
hit.d = glm::min(d1, d2);
hit.point = ray.start + ray.dir * hit.d;
hit.normal = glm::normalize(hit.point - this->center);
}
return hit;
}
};