들어가며
이번 장에서는 구를 추가하여 적용해 본다.
우선, 충돌 위치보다는 충돌했는지의 여부에 대해서만 구해보는 게 이번 주 목표다!
광선과 구의 충돌
구의 반지름
우선, 구의 반지름 r에 대한 방정식은 피타고라스의 정리를 이용하여 아래와 같이 표현이 가능하다.
$ r^2 = x^2 + y^2 + z^2$
구의 표면에 있다는 것은, 구의 원점으로부터 반지름만큼 떨어져 있다는 것과 같기 때문에,
해당식이 성립되면 점(x, y, z)은 구의 표면에 있는 것이다.
또한, 이를 이용해 점(x, y, z)이 구의 내부인지, 외부인지 구분할 수 있다.
구의 내부 : $r^2\ >\ x^2 + y^2 + z^2$
구의 표면 : $ r^2\ =\ x^2 + y^2 + z^2$
구의 외부 : $r^2\ <\ x^2 + y ^2 +z ^2$
구의 중심이 임의의 위치에 있을 때
위의 방정식은 구의 원점이 (0, 0, 0)일 때를 의미하므로, 구의 원점을 $(Cx,\ Cy,\ Cz)$에 둔다면 아래와 같다.
$r^2 = (x - Cx)^2 + (y - Cy)^2 + (z - Cz)^2$
위 식은 보기 복잡하므로 점 P와 구의 중심 C를 벡터를 이용하여 아래와 같이 좀 더 간결하게 표현가능하다.
$r^2 = (P-C) \cdot (P-C)$
광선은 구에 충돌하는가?
구의 표면인지, 구의 내면인지, 구의 외부인지에 대한 판별식을 얻었으므로
해당 판별식을 이용하여 광선이 구에 충돌하였는지까지 구해본다.
우선, 이전에 광선의 한 지점은 $P(t) = A + (dir \cdot t)$로 표현이 되었다.
그러면 어떤 광선의 한 지점이 구에 닿는다면 $r^2 = (P(t)-C) \cdot (P(t)-C)$를 만족할 것이다.
해당 식을 계속 전개해 나가면 아래와 같다.
$$r^2 = (P(t) - C) \cdot (P(t) - C) \\ = (A + (dir \cdot t) - C) \cdot (A + (dir \cdot t) - C)$$
여기서 식은 단순화 하기 위해 $m =A -C$로 두고 내적의 분배 법칙을 이용하면 아래와 같다.
$$=(m + (dir \cdot t)) \cdot (m + (dir \cdot t)) \\ =m \cdot m + 2t(m \cdot dir) + t^2(dir\cdot dir)$$
이 식을 한쪽으로 정리하면 아래와 같다.
$$t^2(dir\cdot dir) + 2t(m\cdot dir) + (m\cdot m -r^2) = 0$$
해당 식은 t에 대한 이차방정식인 $at^2 + bt + c = 0$ 형태와 같아지는데,
각 미지수는 아래처럼 연결할 수 있다.
$$a= dir \cdot dir \\ b = 2(m\cdot dir) \\ c = m \cdot m - r^2$$
이차 방정식형태로 만들었으니 이차 방정식의 판별식인 $D = b^2 - 4ac$을 사용해 볼 수 있다.

- D > 0 : 두 개의 교점 (광선이 구를 관통)
- D = 0 : 한 개의 교점 (접점 생김)
- D < 0 : 교점 없음
main.cpp
위의 판별식을 코드로 옮겨, 광선이 구와 교점이 있는지에 대한 값을 반환하는 함수 HitSphere 작성
// 판별식만 확인한다.
bool HitSphere(const Point3& center, double radius, const Ray& r)
{
// m = A - C
Vec3 m = r.Origin() - center;
// 광선의 방향벡터 제곱 | a = dir * dir
auto a = Dot(r.Direction(), r.Direction());
// b = 2(m * d)
auto b = 2.0 * Dot(r.Direction(), m);
// c = (m * m) - r^2
auto c = Dot(m, m) - (radius * radius);
// 2차 방정식 판별식
auto discriminant = (b * b) - (4 * a * c);
return (discriminant >= 0);
}
그 후 해당 값을 HitSphere을 이용하여 충돌했다면, 빨간색으로 색을 저장한다.
Color RayColor(const Ray& r)
{
// 구에 적중하면 빨간색을 반환한다.
if (HitSphere(Point3(0,0,-1), 0.5, r))
return Color(1, 0, 0);
Vec3 unitDirection = UnitVector(r.Direction());
auto a = 0.5 * (unitDirection.Y() + 1.0);
return (1.0 - a) * Color(1.0, 1.0, 1.0) + a * Color (0.5, 0.7, 1.0);
}
Result

추가
벡터와 스칼라의 곱을 $\times$를 이용하여 표현하려 했는데,
벡터의 연산에서 $\times$를 사용하려면 무조건 외적일때만 사용해야 한다고 한다.
그래서 벡터와 스칼라를 표현할 때는 $vec \ t$ 처럼 곱셈기호를 생략하거나, $vec * t$를 사용한다.
'C++ > Ray Tracing' 카테고리의 다른 글
| [02]Rays & Simple Camera & Background (0) | 2026.02.26 |
|---|---|
| [01] Vector class (0) | 2026.02.24 |
| [00] Output an Image (0) | 2026.02.20 |
