들어가며

이번 장에서는 구를 추가하여 적용해 본다.

우선, 충돌 위치보다는 충돌했는지의 여부에 대해서만 구해보는 게 이번 주 목표다!

 

 

광선과 구의 충돌

구의 반지름

우선, 구의 반지름 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

+ Recent posts