CPP로 RayTracing 구현하기 - 2. 원 그리기

그래픽스꿀잼·2026년 4월 28일

그래픽스

목록 보기
10/20

원 그리기

원 그리기는 개쉬움 그냥 밥임
구의 방정식이라고 있음

구의 방정식

임의의 점 (x,y,z)가 있다고 가정 ㄱㄱ

x2+y2+z2=r2x^2 + y^2 + z^2 = r^2

이걸 사용하면 구 내부를 색칠놀이 할 수 있음

x2+y2+z2<r2x^2 + y^2 + z^2 < r^2 이면 x,y,z에 해당되는 점이 구 내부에 존재하는거임

x2+y2+z2=r2x^2 + y^2 + z^2 = r^2 이면 x,y,z에 해당되는 점이 구 표면에 존재하는거임

x2+y2+z2>r2x^2 + y^2 + z^2 > r^2 이면 x,y,z,에 해당되는 점이 구 외부에 존재하는거임

그러니까 구 표면 || 내부에 존재하는 점(픽셀, 좌표)만 원하는 색으로 색칠놀이 하면 되는거임


이때 구의 방정식은 구의 중앙이 원점 (0,0,0)에 있다고 가정할때의 방정식임.

즉, 임의의 점 CC에서 구의 방정식을 구하면 다음과 같음

(Cxx)2+(Cyy)2+(Czz)2=r2(Cx−x)^2+(Cy−y)^2+(Cz−z)^2=r^2

CC의 각 좌표에서 x,y,zx,y,z를 빼면 해당 방향으로의 길이 벡터가 나옴

각 길이들을 제곱했을때
=r2= r^2이면 구 표면,
<r2< r^2 이면 구 내부,
>r2> r^2이면 구 외부의 점

이 되는거지 ㅇㅇ

이때 (x,y,z)(x,y,z)를 점 PP라고 하면,

(Cxx)2+(Cyy)2+(Czz)2=(CP)(CP)(Cx−x)^2+(Cy−y)^2+(Cz−z)^2 = (C-P) \cdot (C-P)와 같음

이유는 내적 공식임
내적은 두 벡터의 각 요소를 서로 곱해서 더한 값임
두 벡터를 XX, YY라고 해보자
그럼 내적은
XxYx+XyYy+XzYzX_x * Y_x + X_y * Y_y + X_z * Y_z가 됨

(CP)(CP)=(C-P) \cdot (C-P) =
(CxPx)(CxPx)...=(C_x - P_x) * (C_x - P_x)... =
(Cxx)2+(Cyy)2+(Czz)2(Cx−x)^2+(Cy−y)^2+(Cz−z)^2


이때, (CP)(CP)(C-P) \cdot (C-P)는 다시 아래 식처럼 표현할 수 있음

핵심 아이디어는 다음과 같음

PP는 이제부터 없고, 점 PP대신 Ray P(t)P(t)가 존재함
이유는 Ray가 지나간 자리에서 원과 충돌이 생기는 지점만 색을 칠하면 되기 때문임

즉, P(t)P(t)는 광선의 시작점(QQ)에서부터 스칼라 값 tt만큼 dd방향으로 이동한 좌표임
P(t)=Q+tdP(t) = Q + td

따라서

(CP)(CP)=(C(Q+td))(CQ+td))(C-P) \cdot (C-P) = (C - (Q + td)) \cdot (C - Q + td))

처럼 표현가능

이항정리를 해보자

(C(Q+td))(CQ+td))=(C - (Q + td)) \cdot (C - Q + td)) =
(td+(CQ))(td+(CQ))(-td + (C - Q)) \cdot (-td + (C - Q))

처럼 표현가능함


(td+(CQ))(td+(CQ))(-td + (C - Q)) \cdot (-td + (C - Q))

이 식을 거시적으로 봐보센

뭐가보임?

td=A-td = A
CQ=BC - Q = B

라고 보면

(A+B)(A+B)(A + B) \cdot (A + B) 가됨

위 식을 풀어보면

(A+B)(A+B)=AA+AB+BA+BB(A + B) \cdot (A + B) = A \cdot A + A \cdot B + B \cdot A + B \cdot B 가 됨.


이때 A=tdA = -td이고, B=CQB = C - Q
내적은 교환법칙이 성립하므로 AB=BAA \cdot B = B \cdot A

즉, AA+2(AB)+BBA \cdot A + 2 (A \cdot B)+ B \cdot B 임!


항을 풀어서 정리를 해보자

(td+(CQ))(td+(CQ))=(-td + (C - Q)) \cdot (-td + (C - Q)) =
(A+B)(A+B)=(A + B) \cdot (A + B) =
(td)(td)+2(td(CQ))+(CQ)(CQ)(-td) \cdot (-td) + 2(-td \cdot (C - Q)) + (C - Q) \cdot (C - Q)

가 됨


tt는 스칼라 값으로,
스칼라 값들의 내적은 스칼라 값을 그냥 곱한것과 같음

즉, 위의 식을 한번 더 풀어서 원 표면과 함께 식 정리를 하면


t2(dd)2td(CQ)+(CQ)(CQ)=r2t^2(d \cdot d) -2td \cdot(C - Q) + (C - Q ) \cdot (C - Q) = r^2

이 됨


뭔가 익숙하지않음?
이차방정식이 막 근질근질 하지 않음?

r2r^2을 좌항으로 넘기면 ax2+bx+c=0ax^2 + bx + c = 0꼴의 이차방정식이 됨

t2(dd)2td(CQ)+(CQ)(CQ)r2=0t^2(d \cdot d) -2td \cdot(C - Q) + (C - Q ) \cdot (C - Q) - r^2 = 0이 되는거고,

이걸 다시 정리하면

[dd]t2+[2d(CQ)]t+[(CQ)(CQ)r2]=0[d \cdot d] t^2 + [-2d \cdot(C - Q)]t + [(C - Q ) \cdot (C - Q) - r^2] = 0

형태인 이차방정식이 됨!! ㅁㅊㅁㅊ!!


ax2ax^2에서 a=[dd]a = [d \cdot d]가 되고
bxbx에서 b=[2d(CQ)]b = [-2d \cdot(C - Q)]가 되고
cc에서 c=[(CQ)(CQ)r2]c = [(C - Q ) \cdot (C - Q) - r^2]가 되는거임

근의 공식

b±b24ac2a\frac{-b \pm \sqrt{b^2 - 4ac}}{2a}

이라는 공식 알고있지?

여기서 판별식이 존재함
b24acb^2 - 4ac가 판별식임

이 판별식에 따라서
판별식 값이 음수 = 근이 없음 = 구 외부를 지남
판별식 값이 같음 = 근은 1개 = 구 표면(접점)을 지남
판별식 값이 양수 = 근이 2개 = 구 내부를 지남

이렇게 ray에 대하여 구의 어느부분을 지나는지 판별하게 되는거임

코드 구현

a=[dd]a = [d \cdot d]
b=[2d(CQ)]b = [-2d \cdot(C - Q)]
c=[(CQ)(CQ)r2]c = [(C - Q ) \cdot (C - Q) - r^2]

를 기억하자

bool hit_sphere(const Point3& center, double radius, const Ray& r)
{
    Vector3 oc = center - r.origin();
    double a = dot(r.direction(), r.direction());
    double b = -2.0 * dot(r.direction(), oc);
    double c = dot(oc, oc) - radius * radius;
    double discriminant = b * b - 4 * a * c;

    return discriminant >= 0;
}

Color ray_color(const Ray& r)
{
    if (hit_sphere(Point3(0.2, 0.2, -1), 0.5, r))
    {
        return Color(1, 1, 0);
    }
    
    ...
}

여기서 Point3(0.2, 0.2, -1)는 원 중심의 좌표임

판별식에 따라 0보다 크거나 같으면 원 표면 혹은 원 내부를 지난다는 의미이므로, 해당 점을 다른색으로 칠해준다.

이때 z축이 -1인 이유는 다음과 같음

오른손 좌표계를 우리는 사용중임
y는 화면 위, x는 화면 오른쪽, z는 화면에서 내 눈 방향임

즉, 우리 눈이 z축으로 양수값에 위치하게 되는거임
근데 원의 z축이 양수가 되면?
우리가 반대로 구를 보게 된다는거임!

따라서 좌표값에 반대 값으로 반전이 생긴것처럼 보이게 된다는거임

  • z = 1일때

  • z = -1일때

구가 약간 삐뚫어진 이유는
카메라에서 바라본 각도이기 때문에, 시야각에 따라 삐뚫어진거임

profile
그래픽스 공부중

0개의 댓글