무엇보다 먼저, 우주가 구형이라는 점을 짚어야 한다. — 니콜라우스 코페르니쿠스(1473–1543)
공간에 좌표를 부여하고 위치를 정확히 정의하는 방법이 직교좌표계뿐인 것은 아닙니다. 그 대안 가운데 하나가 이 장의 주제인 극좌표계입니다. 극좌표에 익숙하지 않다면, 특히 삼각함수가 등장하기 때문에 난해하거나 고급 주제처럼 보여 대충 넘기고 싶을 수도 있습니다. 그러지 않기를 바랍니다. AI나 카메라 제어 같은 분야의 실용적인 문제 중에는 극좌표라는 틀 안에서 해법뿐 아니라 문제에 내재한 어려움까지 쉽게 이해할 수 있는 것이 많습니다.
이 장은 다음과 같이 구성됩니다.
•
7.1절: 2차원 극좌표를 설명합니다.
•
7.2절: 직교좌표보다 극좌표가 더 적합한 예를 살펴봅니다.
•
7.3절: 3차원 극좌표 공간의 작동 방식을 설명하고 원통좌표와 구면좌표를 소개합니다.
•
마지막으로 7.4절에서는 극좌표 공간으로 위치뿐 아니라 벡터도 표현할 수 있음을 분명히 합니다.
7.1 2차원 극좌표 공간 (2D Polar Space)
이 절에서는 준비 운동 삼아 2차원에서 극좌표의 기본 개념을 소개합니다. 7.1.1절은 극좌표로 위치를 표현하는 방법, 7.1.2절은 극좌표의 중복 표현(aliasing), 7.1.3절은 2차원 극좌표와 직교좌표 사이의 변환을 다룹니다.
7.1.1 2차원 극좌표로 점의 위치 나타내기
2차원 직교좌표 공간에는 공간의 위치를 정하는 원점과, 원점을 지나며 공간의 방향을 정하는 두 축이 있다는 사실을 떠올려 보겠습니다. 2차원 극좌표 공간에도 극(pole)이라고 부르는 원점이 있습니다. 그 기본 목적 역시 좌표 공간의 ‘중심’을 정의하는 것입니다. 다만 극좌표 공간의 축은 하나뿐입니다.
극축(polar axis)이라고도 부르며 보통 원점에서 뻗어나가는 반직선으로 그립니다. 수학 문헌에서는 극축이 그림의 오른쪽을 향하도록 그리는 것이 관례입니다. 따라서 그림 7.1처럼 직교좌표계의 +x축에 대응합니다.
그림 7.1. 2차원 극좌표 공간
그림 7.1. 2차원 극좌표 공간
7.3.3절에서 보겠지만 다른 규약을 쓰는 편이 편리할 때도 많습니다. 그 전까지는 수학 문헌의 전통적인 규약을 따릅니다.
직교좌표계에서는 부호 있는 두 거리 x, y로 2차원 점을 표현했습니다. 극좌표계에서는 거리 하나와 각도 하나를 사용합니다. 관례상 거리는 radius(반지름)의 첫 글자인 r, 각도는 θ라고 합니다. 극좌표 쌍 (r, θ)는 다음 과정으로 2차원 공간의 점을 지정합니다.
2차원 극좌표 (r, θ)가 가리키는 점 찾기
1.
원점에서 극축 방향을 바라본 상태로 시작해 θ만큼 회전합니다. 보통 양의 θ는 반시계 방향, 음의 θ는 시계 방향 회전을 뜻합니다.
2.
이제 원점에서 앞으로 r단위만큼 이동합니다. 도착한 곳이 극좌표 (r, θ)가 나타내는 점입니다.
그림 7.2는 이 과정을 보여 줍니다.
그림 7.2. 2차원 극좌표로 점의 위치 찾기
그림 7.2. 2차원 극좌표로 점의 위치 찾기
그림 7.3. 2차원 극좌표를 표시한 점의 예
그림 7.3. 2차원 극좌표를 표시한 점의 예
정리하면 r은 점과 원점 사이의 거리를, θ는 원점에서 본 점의 방향을 정합니다. 그림 7.3에는 여러 점과 그 극좌표가 나와 있습니다. 어떻게 작동하는지 확실히 알겠다는 생각이 들 때까지 그림을 살펴보겠습니다.
극좌표 공간의 그림에도 격자선이 있지만 직교좌표계의 격자선과 조금 다르다는 점을 눈치챘을 것입니다. 직교좌표계의 각 격자선은 어느 한 좌표의 값이 같은 점들로 이루어집니다. 수직선은 x좌표가 같은 점들, 수평선은 y좌표가 같은 점들로 이루어집니다. 극좌표계의 격자선도 비슷합니다.
•
‘격자 원’은 r이 일정한 선입니다. 원이란 중심에서 같은 거리에 있는 모든 점의 집합이므로 당연합니다. 이 거리를 나타내는 관례적인 변수가 r인 것도 반지름 방향의 거리(radial distance)이기 때문입니다.
•
원점을 지나는 곧은 격자선은 θ가 일정한 선입니다. 원점에서 같은 방향에 있는 점들로 이루어집니다.
각도의 측정 단위에 관해 한 가지 짚고 넘어가겠습니다. 직교좌표에서는 측정 단위 자체가 그다지 중요하지 않았습니다. 그림을 피트, 미터, 마일, 야드, 광년, 비어드초(beard-second), 파이카 중 무엇으로 해석하든 본질적으로는 상관없었습니다.(원주 7-1) 직교좌표 데이터를 다른 물리 단위로 해석하면 대상이 커지거나 작아질 뿐, 비율과 모양은 그대로입니다. 그러나 극좌표의 각도 성분에 서로 다른 각도 단위를 적용하면 결과가 크게 뒤틀릴 수 있습니다.
도를 쓰든 라디안을 쓰든, 또는 그라드, 밀, 분, 사인, 섹스턴트, 퍼먼을 쓰든, 일관되게 구분하기만 하면 상관없습니다. 이 책의 본문에서는 구체적인 각도를 거의 항상 도 단위로 제시하고 숫자 뒤에 °를 붙입니다. 저자들도 사람이기 때문입니다. 수학 교수가 아닌 대부분의 사람은 π의 분수보다 정수를 다루는 편을 쉽게 느낍니다.
실제로 360이라는 수를 택한 것도 흔한 경우에 분수를 피하려는 의도였습니다. 하지만 계산 기계(원주 7-2)는 라디안으로 표현한 각도를 선호하므로 이 책의 코드 조각은 도 대신 라디안을 사용합니다.
원주 7-1: NASA 직원 중에는 다르게 생각하는 사람도 있을 것입니다. 1억 2,500만 달러짜리 화성 기후 궤도선(Mars Climate Orbiter)은 미터법과 영국식 단위를 혼동한 버그 때문에 잘못된 길로 들어섰습니다. ‘직교좌표의 개념을 이해하는 데 구체적인 측정 단위를 알 필요는 없다’고 표현하는 편이 낫겠습니다.
원주 7-2: 수학 교수 같은 존재 말입니다.
7.1.2 중복 표현(Aliasing)
이제 극좌표의 작동 방식과 극좌표 공간의 모습에 어느 정도 감이 잡혔기를 바랍니다. 그래도 머릿속 한편에 석연치 않은 생각이 남아 있을 수 있습니다. 의식적으로든 무의식적으로든 직교좌표 공간과 극좌표 공간 사이의 근본적인 차이를 알아차렸을지도 모릅니다. 2차원 직교좌표 공간을 모든 방향으로 무한히 펼쳐진 완벽히 균일한 연속체, 이를테면 흠 없는 젤리 한 판으로 상상했을 수 있습니다.
무한히 얇게 떠낸 어느 한 숟갈도 다른 숟갈과 같습니다. 물론 원점이나 축 같은 ‘특별한’ 곳은 있습니다. 그러나 그것은 틀 바닥의 표시와 같을 뿐, 그 위의 젤리 자체는 다른 곳과 똑같습니다.
극좌표 공간의 바탕을 상상하면 어딘가 다릅니다. 극좌표 공간에는 ‘이음매’가 있고, 무언가를 ‘기워 붙인’ 듯한 불연속 지점이 있습니다. 무한히 큰 원형 젤리 틀 안에 젤리 판이 여러 겹 포개져 있는 셈입니다. 어떤 곳에 숟가락을 넣어 한 입을 뜨면 여러 입이 함께 떠지는 일이 흔합니다!
젤리 덩어리 안에는 머리카락도 하나 있습니다. 특별히 주의해야 하는 특이점입니다. 극좌표 공간을 젤리로 상상했든 다른 맛있는 디저트로 상상했든, 아마 다음 질문 중 일부를 떠올렸을 것입니다.
1.
반지름 거리 r이 음수일 수도 있을까?
2.
θ가 [−180°, +180°] 구간을 벗어날 수도 있을까?
3.
원점에서 정확히 ‘서쪽’에 있는 점, 즉 직교좌표로 x < 0, y = 0인 점의 θ는 모호합니다. 그림 7.3에 이 점들의 좌표가 적혀 있지 않다는 것을 보았을지도 모릅니다. 이 점들의 θ는 +180°일까, −180°일까?
4.
원점 자체의 극좌표도 모호합니다. r = 0임은 분명한데 θ는 어떤 값이어야 할까? 어떤 값이든 되지 않을까?
이 모든 질문에 대한 답은 ‘그렇다’입니다.(원주 7-3) 사실 극좌표 공간에 관해서는 제법 냉정한 현실을 받아들여야 합니다.
어느 점이든, 그 점을 나타낼 수 있는 극좌표 쌍은 무한히 많다.
이 현상을 중복 표현(aliasing)이라고 합니다. 수치가 서로 다르지만 공간의 같은 점을 가리키는 두 좌표 쌍을 서로의 별칭(alias)이라고 합니다. 직교좌표 공간에는 이런 중복 표현이 없습니다. 공간의 각 점에는 (x, y) 좌표 쌍이 정확히 하나씩 배정되며, 점과 좌표 쌍의 대응이 일대일입니다.
중복 표현이 일으키는 어려움을 논하기 전에, 중복 표현이 아무 문제도 일으키지 않는 작업 하나를 분명히 해 두겠습니다. 주어진 극좌표 쌍 (r, θ)를 해석하여 그 좌표가 가리키는 공간상의 점을 찾는 작업입니다. r과 θ가 어떤 값이든 합리적인 해석이 가능합니다.
r < 0이면 ‘뒤로’ 움직인다고 해석합니다. r이 양수일 때 움직일 방향의 반대 방향으로 변위가 생깁니다. θ가 [−180°, +180°] 범위를 벗어나더라도 당황할 필요는 없습니다. 최종 방향은 여전히 결정할 수 있습니다.(원주 7-4) 즉, ‘특이한’ 극좌표는 있을지 몰라도 ‘잘못된’ 극좌표는 없습니다. 하나의 공간상의 점에는 많은 좌표 쌍이 대응하지만, 하나의 좌표 쌍은 모호함 없이 정확히 하나의 점을 지정합니다.
점 (r, θ)의 별칭을 만드는 한 방법은 θ에 360°의 배수를 더하는 것입니다. 한 바퀴 이상의 완전한 회전을 더해도 θ가 정하는 최종 방향은 바뀌지 않습니다. 그러므로 정수 k에 대해 (r, θ)와 (r, θ + k360°)는 같은 점을 나타냅니다. θ에 180°를 더하고 r의 부호를 뒤집어도 별칭을 만들 수 있습니다. 반대 방향을 바라보되 반대 양만큼 이동하기 때문입니다.
일반적으로 원점이 아닌 점 (r, θ)의 모든 극좌표 별칭은 정수 k를 이용하여 다음과 같이 쓸 수 있습니다.
이처럼 중복 표현이 있어도, r과 θ가 어떤 값이든 극좌표 (r, θ)가 가리키는 점에 대해서는 모두 동의할 수 있습니다. 그 반대 문제는 어떨까요? 공간의 임의의 점 p가 주어졌을 때 p를 나타낼 극좌표 (r, θ)에 모두 동의할 수 있을까요? 방금 p의 위치를 나타내는 극좌표 쌍이 무한히 많다고 했습니다. 어느 것을 쓰면 좋을까요? 짧게 답하면 ‘맞기만 하면 무엇이든 괜찮지만, 그중 사용하기를 권장하는 것은 하나뿐’입니다.
이는 분수를 약분하는 것과 비슷합니다. 13/26이 완전히 유효한 분수라는 점과 그 값에는 누구도 이견이 없습니다. 그렇지만 13/26은 ‘특이한’ 표현입니다. 대부분은 더 간단하고 이해하기 쉬운 1/2로 표현하기를 선호합니다. 분수는 분자와 분모를 모두 나누어떨어지게 하는 1보다 큰 정수가 없을 때, 즉 기약분수일 때 ‘선호되는’ 형태입니다.
꼭 13/26을 1/2로 바꿔야 하는 것은 아니지만 관례상 보통 그렇게 합니다. 이 관례를 얼마나 열심히 따르느냐는 대체로 수학 선생님이 숙제에서 약분하지 않은 답을 몇 점씩 감점했느냐에 달려 있습니다.(원주 7-5)
극좌표에서 어떤 점을 나타내는 ‘선호되는’ 좌표를 그 점의 표준 좌표(canonical coordinates)라고 합니다. 2차원 극좌표 쌍 (r, θ)는 r이 음수가 아니고 θ가 (−180°, 180°]에 있으면 표준 집합에 속합니다. 이 구간은 반열린 구간입니다.
원점의 정확히 ‘서쪽’, 즉 x < 0, y = 0인 점에는 θ = +180°를 사용합니다. 또한 r = 0일 때, 즉 원점에서는 보통 θ = 0으로 정합니다. 이 규칙들을 모두 적용하면 2차원 공간의 어느 점이든 표준 극좌표로 나타내는 방법이 정확히 하나 존재합니다. 다음 수학 표기로 간결하게 정리할 수 있습니다.
극좌표 쌍 (r, θ)는 다음 조건을 모두 만족하면 표준 집합에 속합니다.
조건 | 뜻 |
r ≥ 0 | 거리를 ‘뒤로’ 재지 않습니다. |
−180° < θ ≤ 180° | 각도는 양·음 방향 각각 반 바퀴 범위로 제한합니다. ‘서쪽’에는 +180°를 씁니다. |
r = 0 ⇒ θ = 0 | 원점에서는 각도를 0으로 둡니다. |
극좌표 쌍 (r, θ)를 표준형으로 바꾸는 알고리즘
1.
r = 0이면 θ = 0으로 둡니다.
2.
r < 0이면 r의 부호를 바꾸고 θ에 180°를 더합니다.
3.
θ ≤ −180°이면 θ > −180°가 될 때까지 θ에 360°를 더합니다.
4.
θ > 180°이면 θ ≤ 180°가 될 때까지 θ에서 360°를 뺍니다.
목록 7.1은 이를 C로 구현한 예입니다. 7.1.1절에서 설명했듯이, 이 책의 코드는 보통 각도를 라디안으로 저장합니다.
// 반지름 거리
float r;
// 각도: 라디안 단위
float theta;
// 2*pi(360도) 상수
const float TWOPI = 2.0f * PI;
// 정확히 원점인지 확인한다.
if (r == 0.0f) {
// 원점에서는 theta를 0으로 지정한다.
theta = 0.0f;
} else {
// 음수 거리를 처리한다.
if (r < 0.0f) {
r = -r;
theta += PI;
}
// theta가 범위를 벗어났는가? 이 if 검사는 반드시 필요한 것은
// 아니지만 불필요한 부동소수점 연산을 피한다.
// 필요도 없는 부동소수점 정밀도 손실을 감수할 이유가 있을까?
if (fabs(theta) > PI) {
// PI만큼 이동시킨다.
theta += PI;
// 0...TWOPI 범위로 감싼다.
theta -= floor(theta / TWOPI) * TWOPI;
// 이동을 되돌려 각도를 -PI...PI 범위로 옮긴다.
theta -= PI;
}
}
C++
복사
목록 7.1: 극좌표를 표준형으로 변환하기.
꼼꼼한 독자는 이 코드가 θ를 닫힌 구간 [−π, +π]에 넣지만 θ = −π인 경우를 명시적으로 피하지는 않는다는 점을 알아차릴 것입니다. π는 부동소수점으로 정확히 표현할 수 없습니다. 사실 무리수이므로 부동소수점으로도, 어떤 진법의 유한한 자릿수로도 정확하게 표현할 수 없습니다!
코드의 상수 PI는 정확한 π가 아니라 float로 표현 가능한 수 중 π에 가장 가까운 수입니다. 배정밀도 연산을 쓰면 실제 값에 더 가까워지지만 여전히 정확하지는 않습니다. 원문은 이 때문에 함수가 열린 구간 (−π, +π)의 값을 반환한다고 설명합니다. 다만 부동소수점이라는 이유만으로 코드의 −PI 경계값이 제외되지는 않습니다. 아래 ‘원문 코드의 주의점과 보충 구현’에서 이 부분을 구분해 설명합니다.
원주 7-3: 세 번째 질문까지도 그렇습니다.
원주 7-4: 주의: θ가 아주 크고 그림 7.2의 1단계를 문자 그대로 따른다면 어지러울 수 있습니다.
원주 7-5: 수학 선생님과 약분 이야기가 나왔으니 말인데, 저자 한 명은 중학교 수학 선생님이 2와 3/5 같은 대분수가 대응하는 가분수 13/5보다 ‘간단한가’를 두고 열띤 논쟁을 하던 일을 기억합니다. 다행히 이 심오한 수수께끼의 답은 극좌표의 중복 표현을 이해하는 데 필요하지 않습니다.
음수 반지름과 경계 규약을 분리해서 생각하기
(-3,30°)은 먼저 30°를 바라본 뒤 뒤로 3만큼 이동한다는 뜻입니다. 그래서 (3,210°), 즉 표준각을 사용한 (3,−150°)와 같은 점입니다. 반지름에 절댓값만 취하고 각도를 그대로 두면 다른 점으로 바뀝니다. 올바른 표준화는 부호를 바꿀 때 각도도 반 바퀴 바꿉니다.
2차원 원점에서는 각도가 무엇이든 같은 점입니다. 표준형이 각도를 0으로 정하는 것은 저장·비교를 위한 약속이며, 실제 물체가 자동으로 동쪽을 바라본다는 뜻이 아닙니다. 위치 데이터의 표준화와 물체의 자세 상태는 구분해서 저장해야 합니다.
7.1.3 2차원 직교좌표와 극좌표 사이의 변환
이 절에서는 2차원 직교좌표계와 극좌표계 사이의 변환을 설명합니다. 1.4.5절에서 복습한 삼각함수를 언제 쓰나 궁금했다면, 바로 지금입니다.
그림 7.4는 2차원 극좌표와 직교좌표 사이의 변환에 관련된 기하를 보여 줍니다.
그림 7.4. 직교좌표와 극좌표 사이의 변환
그림 7.4. 직교좌표와 극좌표 사이의 변환
극좌표 (r, θ)를 대응하는 직교좌표로 바꾸는 식은 사인과 코사인의 정의에서 거의 바로 나옵니다.
중복 표현은 아무 문제도 되지 않습니다. 식 (7.1)은 r과 θ가 ‘이상한’ 값이어도 작동합니다.
어려운 쪽은 직교좌표 (x, y)에서 극좌표 (r, θ)를 구하는 것입니다. 중복 표현 때문에 정답은 하나가 아닙니다. (x, y)를 나타내는 (r, θ) 쌍이 무한히 많습니다. 대개 우리가 원하는 것은 표준 좌표입니다.
r은 피타고라스 정리로 쉽게 계산할 수 있습니다.
제곱근 함수는 항상 0 이상인 제곱근을 반환하므로, 계산한 r 때문에 극좌표가 표준 집합 밖으로 나갈 걱정은 없습니다.
r은 꽤 쉽게 구했습니다. 이제 θ를 풀어 보겠습니다.
안타깝게도 이 접근법에는 두 가지 문제가 있습니다. 첫째, x = 0이면 나눗셈이 정의되지 않습니다. 둘째, arctan 함수의 치역은 (−90°, +90°)뿐입니다. 근본적인 문제는 y/x라는 나눗셈이 유용한 정보를 버린다는 점입니다. x와 y는 각각 양수 또는 음수일 수 있으므로 네 가지 조합이 있으며, 이는 점이 놓일 수 있는 네 사분면에 대응합니다.
하지만 나눗셈 y/x의 결과는 값 하나뿐입니다. x와 y의 부호를 동시에 바꾸면 점은 평면의 다른 사분면으로 이동하지만 y/x의 비는 그대로입니다.
이 문제들 때문에 직교좌표에서 극좌표로 변환하는 완전한 식에는 각 사분면을 처리하는 ‘if문’이 필요합니다. ‘수학을 하는 사람’에게는 조금 지저분한 식입니다. 다행히 ‘컴퓨터를 하는 사람’에게는 atan2 함수가 있습니다. 성가신 원점만 제외하면 모든 x와 y에 대해 θ를 올바르게 구합니다. 이 표기법을 빌려 이 책의 수학식에서 사용할 atan2 함수를 다음처럼 정의하겠습니다.
식 (7.2)에서 두 가지를 눈여겨보겠습니다. 첫째, 대부분의 컴퓨터 언어 표준 라이브러리에 있는 atan2의 관례에 따라 인수 순서는 ‘뒤집힌’ y, x입니다. 거꾸로라고 외워도 되고, atan2(y, x)와 arctan(y/x)의 글자 모양이 비슷하다는 점을 기억해도 됩니다. 또는 tan θ = sin θ/cos θ이므로 θ = atan2(sin θ, cos θ)라고 기억하겠습니다.
둘째, 많은 소프트웨어 라이브러리는 x = y = 0인 원점에서 atan2를 정의하지 않습니다. 이 책의 본문 수식에 쓰려고 정의한 atan2는 atan2(0, 0) = 0입니다. 코드에서는 라이브러리 함수 atan2를 쓰고 원점을 특수한 경우로 명시적으로 처리합니다. 수식에서는 원점에서도 정의된 추상 함수 atan2를 사용합니다. 원문에서는 두 경우의 서체가 다르다는 점에도 주의하겠습니다.
이제 2차원 직교좌표에서 극각 θ를 구하는 원래 작업으로 돌아가겠습니다. atan2 함수만 있으면 변환은 간단합니다.
목록 7.2의 C 코드는 직교좌표 쌍 (x, y)를 대응하는 표준 극좌표 (r, θ)로 변환합니다.
// 입력: 직교좌표
float x, y;
// 출력: 극좌표의 반지름 거리와 라디안 단위의 각도
float r, theta;
// 원점인지 확인한다.
if (x == 0.0f && y == 0.0f) {
// 원점에서는 두 극좌표를 모두 0으로 지정한다.
r = 0.0f;
theta = 0.0f;
} else {
// 값을 계산한다. atan2는 참 편리하지 않은가?
r = sqrt(x*x + y*y);
theta = atan2(y, x);
}
C++
복사
목록 7.2: 2차원 직교좌표를 극좌표로 변환하기.
극좌표를 눈으로 확인하기
추가 도해 — 극좌표의 직교 투영, 음수 반지름, 각도 경계
왼쪽의 파란 선은 반지름 r=3이며, 주황색과 초록색 선은 각각 x축과 y축으로의 투영입니다. 점이 한 바퀴 도는 동안 좌표는 연속적으로 변합니다. 하지만 표준각은 +180°를 지난 순간 −180° 근처로 바뀝니다. 각도라는 표기의 이음매가 생긴 것이지, 점이 순간이동한 것이 아닙니다. 오른쪽은 음수 반지름을 해석하는 별도 예입니다.
목표를 향해 천천히 도는 포탑
포탑에서 목표로 향하는 변위가 (dx,dy)이면 목표 각도는 atan2(dy,dx)입니다. 현재 방향이 179°, 목표가 −179°일 때 두 수를 그냥 빼면 −358°가 됩니다. 그러나 짧게 돌려면 +2°만 움직이면 됩니다. 각도 차이를 한 바퀴의 대표 구간으로 감싼 뒤 회전량을 제한합니다.
회전 속도 제한이 초당 90°이고 Δt=1/60초이면 한 프레임의 최대 회전은 1.5°입니다. 위 예에서는 179°에서 180.5°로 이동하며, 같은 방향을 표준각으로 쓰면 −179.5°입니다. 목표와 포탑이 같은 위치에 있을 때는 방향 자체가 정의되지 않으므로, 조준을 유지할지 기본 방향으로 돌릴지 게임 규칙으로 정해야 합니다. 정확히 180° 차이인 경우에도 어느 쪽으로 돌지 일관된 규칙이 필요합니다.
7.2 굳이 극좌표를 쓰는 이유는 무엇일까? (Why Polar Coordinates?)
직교좌표는 젤리 속 머리카락 같은 골칫거리 없이 잘 작동합니다. 그런데 중복 표현, 도와 라디안, 삼각함수 같은 복잡함을 감수하며 왜 극좌표를 쓸까요? 사실 우리는 직교좌표보다 극좌표를 더 자주 쓸지도 모릅니다. 일상 대화에도 흔히 나타납니다.
예를 들어 저자 한 명은 텍사스주 앨버라도 출신입니다. 사람들이 앨버라도가 어디냐고 물으면 그는 ‘벌리슨에서 남동쪽으로 약 15마일 떨어진 곳’이라고 말합니다. 원점(벌리슨), 거리(15마일), 각도(남동쪽)를 지정하는 극좌표로 앨버라도의 위치를 설명하는 셈입니다. 물론 텍사스 밖에 사는 대부분의 사람은, 그리고 텍사스 사람 중 상당수도 벌리슨이 어디인지 모릅니다.
그러면 다른 극좌표계로 바꿔 ‘댈러스에서 남서쪽으로 약 50마일 떨어진 곳’이라고 말하는 편이 자연스럽습니다. 다행히 미국 밖의 사람들도 대체로 댈러스가 어디인지는 압니다.(원주 7-6) 덧붙이면, 텍사스 사람이 모두 카우보이모자와 부츠를 착용하는 것은 아닙니다. 다만 ‘y’all’이나 ‘fixin’’ 같은 말은 씁니다.(원주 7-7)
극좌표가 자주 등장하는 이유는 사람들이 위치를 거리와 방향으로 생각하는 것이 자연스럽기 때문입니다. 물론 극좌표를 쓸 때도 아주 정확하지는 않습니다. 정확성이 뇌의 특기 중 하나는 아니기 때문입니다. 직교좌표는 사람의 모국어가 아닙니다. 컴퓨터는 그 반대입니다. 컴퓨터로 기하 문제를 풀 때는 대개 극좌표보다 직교좌표를 쓰는 편이 쉽습니다. 8장에서 3차원 방향을 나타내는 여러 방법을 비교할 때, 사람과 컴퓨터의 이 차이를 다시 이야기합니다.
우리가 극좌표에 친숙한 이유는 각 좌표가 그 자체로 구체적인 의미를 가지기 때문일지도 모릅니다. 한 전투기 조종사가 다른 조종사에게 ‘미확인기, 6시 방향!’이라고 말할 수 있습니다.(원주 7-8) 치열한 공중전 중인 용감한 조종사들도 사실 극좌표를 쓰고 있습니다. ‘6시 방향’은 ‘당신 뒤’를 뜻하며, 본질적으로 지금까지 살펴본 각도 θ입니다.
조종사가 거리를 명시할 필요는 없었다는 점에 주목하겠습니다. 아마 다른 조종사가 고개를 돌려 직접 보는 편이 상대가 거리를 말해 주는 것보다 빨랐을 것입니다. 이처럼 극좌표 성분 하나, 여기서는 방향만으로도 유용한 정보가 됩니다. 다른 극좌표 성분인 거리 r에 대해서도 비슷한 예를 들 수 있습니다.
직교좌표 성분 하나의 유용성과 비교해 보겠습니다. 조종사가 ‘미확인기, x = 1000피트!’라고 말하는 모습을 상상해 보세요. 이 정보는 처리하기 더 어렵고 그다지 유용하지 않습니다.
게임에서 극좌표가 특히 자주 등장하는 경우는 카메라나 무기 등을 어떤 목표를 향하게 할 때입니다. 보통 필요한 값이 각도이므로 직교좌표에서 극좌표로 변환하면 쉽게 처리할 수 있습니다. 방향을 행렬로 표현할 때처럼 벡터 연산만으로 해결하여 각도 데이터를 피할 수 있더라도 극좌표는 유용합니다.
대개 카메라, 포탑, 암살자의 팔은 아무리 실력이 좋은 암살자라도 순간적으로 움직일 수 없지만 목표는 움직입니다. 이런 상황에서는 어떤 방식으로든 목표를 ‘쫓아갑니다’. 단순한 속도 제한, 지연, 2차 시스템 중 어느 제어 방식을 쓰든, 이런 추적은 3차원 공간의 목표 위치를 보간하는 것보다 극좌표 공간에서 처리하는 편이 대개 더 좋습니다.
기본 측정값을 거리와 방향 형태로 제공하는 물리적 데이터 수집 시스템에서도 극좌표를 자주 만납니다.
직교좌표보다 극좌표가 자연스러운 마지막 예로 구의 표면을 따라 움직이는 경우를 들 수 있습니다. 언제 그런 일을 하냐고요? 아마 지금도 하고 있을 것입니다. 지리적 위치를 정확하게 표현하는 위도·경도는 사실 직교좌표가 아니라 극좌표입니다. 더 정확히는 7.3.2절에서 살펴볼 3차원 극좌표의 일종인 구면좌표입니다.
물론 행성 크기에 비해 작은 영역을 다루고 적도에서 너무 멀지 않다면, 위도와 경도를 직교좌표처럼 사용해도 큰 문제는 없습니다. 댈러스에서는 늘 그렇게 합니다.
원주 7-6: 댈러스가 유명한 데에는 다소 안타까운 두 가지 이유가 있습니다. 케네디 대통령 암살과, 이유를 알 수 없지만 국제적인 인기를 얻은 그 도시 이름의 연속극입니다.
원주 7-7: 이 두 사실은 수학과는 아무 관계가 없지만 오해를 바로잡는 일과는 관계가 깊습니다.
원주 7-8: 저자들이 실제로 이런 말을 직접 들어 본 적은 없습니다. 다만 영화에서는 보았습니다.
플레이어 주위를 도는 위성이나 탄환
중심이 이고 궤도 반지름이 r라면, 삼각함수는 반지름 벡터의 수평·수직 성분을 돌려줍니다. 단위원의 좌표가 (cos θ, sin θ)이므로 길이 r을 곱하고 중심을 더합니다.
중심 (0,0), r=10, θ=30°이면 위치는 약 (8.660,5.000)입니다. 각속도가 초당 90°라면 시간 간격 Δt초마다 각도를 90°×Δt만큼 늘립니다. 실제 sin과 cos에 넣을 때는 θ를 라디안으로 바꾸어야 합니다. 충돌이나 다른 물체와의 거리 계산은 변환한 직교좌표로 수행하면 편합니다. 화면의 y축이 아래로 증가한다면 화면 출력에서 y 부호를 뒤집거나, 처음부터 화면 좌표 관례에 맞는 회전 방향을 정합니다.
7.3 3차원 극좌표 공간 (3D Polar Space)
극좌표는 2차원뿐 아니라 3차원에서도 사용할 수 있습니다. 이미 예상했겠지만 3차원 극좌표에는 값이 세 개 있습니다. 그런데 세 번째 좌표는 r 같은 직선 거리일까요, θ 같은 각도일까요? 둘 중 어느 쪽을 택해도 됩니다. 3차원 극좌표에는 두 종류가 있기 때문입니다. 직선 거리를 하나 더하면 다음 절에서 다룰 원통좌표(cylindrical coordinates)가 됩니다.
그 대신 각도를 하나 더하면 뒤에서 다룰 구면좌표(spherical coordinates)가 됩니다. 원통좌표는 구면좌표보다 덜 쓰이지만 이해하기 쉬우므로 먼저 설명합니다.
7.3.1절은 3차원 극좌표의 한 종류인 원통좌표를, 7.3.2절은 다른 종류인 구면좌표를 다룹니다. 7.3.3절에서는 게임 코드에 사용하기에 더 간결한 대체 극좌표 규약을 제시합니다. 7.3.4절은 구면좌표 공간에서 발생할 수 있는 특별한 중복 표현을, 7.3.5절은 구면좌표와 3차원 직교좌표 사이의 변환을 설명합니다.
7.3.1 원통좌표
직교좌표를 3차원으로 확장할 때는 평면을 다루는 2차원 체계에서 출발하여, 그 평면에 수직인 세 번째 축을 더합니다. 원통좌표가 극좌표를 3차원으로 확장하는 방식도 기본적으로 같습니다. 직교좌표에서처럼 세 번째 축을 z축이라고 하겠습니다. 원통좌표 (r, θ, z)가 나타내는 점을 찾으려면, 먼저 2차원 극좌표에서처럼 r과 θ를 처리한 다음 z좌표에 따라 ‘위’ 또는 ‘아래’로 움직입니다.
그림 7.5는 원통좌표로 점 (r, θ, z)를 찾는 방법을 보여 줍니다.
그림 7.5. 원통좌표
그림 7.5. 원통좌표
3차원 직교좌표와 원통좌표 사이의 변환은 간단합니다. z좌표는 양쪽 표현에서 같고, (x, y)와 (r, θ)는 7.1.3절의 2차원 기법으로 변환합니다.
이 책에서는 원통좌표를 많이 사용하지 않습니다. 하지만 원통 모양의 환경에서 작업하거나 원통 모양의 물체를 표현할 때 유용합니다. 사람들이 자신도 모르게 극좌표를 쓰듯이(7.2절 참조), ‘원통좌표’라는 용어를 모르는 사람도 이를 사용할 수 있습니다. 자신이 원통좌표를 쓰고 있다는 사실을 아는 사람끼리도 표기와 규약은 크게 다를 수 있으니 주의하겠습니다.
예를 들어 어떤 사람은 (ρ, φ, z)라고 씁니다. 축의 방향과 양의 회전도 그 상황에서 가장 편리한 방식으로 정합니다.
7.3.2 구면좌표
더 흔히 쓰이는 3차원 극좌표계는 구면좌표계입니다. 원통좌표는 거리 둘과 각도 하나로 이루어지지만 구면좌표는 각도 둘과 거리 하나로 이루어집니다.
2차원 극좌표의 핵심을 되짚어 보겠습니다. 점은 방향 θ와 거리 r로 지정합니다. 구면좌표도 방향과 거리를 정한다는 점은 같습니다. 유일한 차이는 3차원에서는 방향을 정의하는 데 각도가 두 개 필요하다는 것입니다. 3차원 구면좌표 공간에는 극축도 두 개 있습니다. 첫째 축은 ‘수평’이며 2차원 극좌표의 극축, 또는 이 책의 3차원 직교좌표 규약에서 +x에 대응합니다. 다른 축은 수직이며, 이 책의 3차원 직교좌표 규약에서 +y에 대응합니다.
구면좌표의 규약과 표기는 사람마다 다르지만, 수학을 하는 사람들 대부분은 두 각도의 이름을 θ와 φ로 정하는 데 동의합니다.(원주 7-9) 이 두 각도를 해석하여 방향을 정하는 방식도 대체로 같습니다. 전체 과정은 다음과 같습니다.
극좌표로 3차원 점 찾기
1.
원점에 서서 수평 극축 방향을 바라봅니다. 수직축은 발에서 머리 쪽을 향합니다. 오른팔(원주 7-10)을 수직 극축 방향으로 곧게 위로 뻗습니다.
2.
2차원 극좌표에서 했던 것처럼 반시계 방향으로 θ만큼 회전합니다.
3.
팔을 φ만큼 아래로 내립니다. 이제 팔은 극각 θ와 φ가 정하는 방향을 가리킵니다.
4.
원점에서 그 방향으로 거리 r만큼 이동합니다. 도착한 점이 구면좌표 (r, θ, φ)가 나타내는 점입니다.
그림 7.6은 이 과정을 보여 줍니다.
그림 7.6. 수학에서 사용하는 구면좌표
그림 7.6. 수학에서 사용하는 구면좌표
다른 표기를 쓰는 사람들도 있습니다. 특히 물리학에서는 θ와 φ 기호를 서로 바꾸는 규약을 자주 씁니다. 또 어떤 저자들은 로마자를 모두 그리스 문자로 바꾸려는 듯 반지름 거리의 이름으로 r 대신 ρ를 씁니다. 게임 제작에 더 실용적인 규약은 7.3.3절에서 제시합니다.
수평각 θ는 방위각(azimuth), φ는 천정각(zenith)이라고 합니다. 경도(longitude)와 위도(latitude)라는 용어도 들어 보았을 것입니다. 경도는 기본적으로 θ와 같고 위도는 경사각, 즉 90° − φ입니다. 이처럼 지구의 위치를 나타내는 위도·경도 체계는 사실 구면좌표계의 일종입니다.
우리는 행성 표면의 점만 표현하려는 경우가 많으므로, 지구 중심까지의 거리를 재는 반지름 거리 r이 필요하지 않습니다. r은 대략 고도와 같은 것이라 생각할 수 있습니다. 다만 ‘고도’가 정확히 무엇을 뜻하느냐에 따라 지면이나 해수면이 0이 되도록 지구 반지름(원주 7-11)만큼 값을 옮겨 놓습니다.
원주 7-9: φ는 그리스 문자 파입니다. 대부분 영어로 ‘fee’처럼 발음하지만 ‘fly’와 운이 맞게 발음하는 사람도 있습니다.
원주 7-10: 왼손잡이 독자를 차별하려는 뜻은 없습니다. 원한다면 왼팔을 쓴다고 상상해도 됩니다. 다만 이것은 오른손 좌표계이므로 상상 속에서도 오른팔을 쓰는 편이 더 격식에 맞는 기분이 들 수 있습니다. 왼팔은 나중에 왼손 규약을 다룰 때를 위해 남겨 두겠습니다.
원주 7-11: 지구의 평균 반지름은 약 6,371km(3,959마일)입니다.
7.3.3 3차원 가상 세계에 유용한 극좌표 규약
앞 절의 구면좌표계는 수학에서 사용하는 전통적인 오른손 체계입니다. 이런 전제하에서는 직교좌표와 구면좌표 사이의 변환식이 제법 우아합니다. 하지만 게임 업계의 대부분에게 그 우아함은 다음과 같은 성가신 단점과 비교하면 작은 장점일 뿐입니다.
•
θ = 0의 기본 수평 방향이 +x를 향합니다. 이 책에서 +x는 ‘오른쪽’ 또는 ‘동쪽’이므로 아쉽습니다. 대부분의 사람에게 어느 쪽도 ‘기본’ 방향은 아닙니다. 시계의 숫자가 위에서 시작하듯 수평 극축이 ‘앞쪽’ 또는 ‘북쪽’인 +z를 향하면 더 좋겠습니다.
•
φ 규약은 여러모로 아쉽습니다. 직교좌표를 2차원에서 3차원으로 확장할 때처럼 2차원 극좌표 (r, θ)에 값이 0인 세 번째 좌표만 더하면 3차원이 되면 좋겠습니다. 그러나 구면좌표 (r, θ, 0)는 기대한 대로 2차원 극좌표 (r, θ)에 대응하지 않습니다. 오히려 φ = 0이면 7.3.4절에서 설명할 특이점인 짐벌 락(Gimbal lock)이라는 곤란한 상황이 됩니다. 2차원 평면의 점들은 (r, θ, 90°)로 나타내야 합니다. 천정각 대신 위도를 재는 편이 더 직관적일 수 있습니다. 대부분 ‘수평’을 기본으로, ‘위’를 극단적인 경우로 생각합니다.
•
그리스 사람들에게 악의는 없지만 θ와 φ에는 익숙해지는 데 시간이 조금 걸립니다. r은 적어도 radial distance 또는 radius라는 의미 있는 말의 약자이므로 나쁘지 않습니다. 각도를 나타내는 기호도 임의의 그리스 문자 대신 영어 단어의 약자라면 좋지 않을까요?
•
구면좌표의 두 각도가 3차원 자세를 표현하는 오일러 각(Euler angles)(원주 7-12)의 첫 두 각도와 같으면 좋겠습니다. 오일러 각은 8.3절까지 다루지 않으므로, 지금은 직교좌표 대신 극좌표를 택하는 데 더해, 극좌표 규약까지 바꾸면서 ‘저자들이 그렇다고 하니까 좋을 것이다’라고 해 두겠습니다.(원주 7-13)
•
전통적인 체계는 오른손 체계이지만 우리는, 적어도 이 책에서는 왼손 체계를 씁니다.
우리 목적에 더 알맞은 구면좌표 규약을 정해 보겠습니다. 반지름 거리 r의 표준 규약에는 불만이 없으므로 이름과 의미를 모두 유지합니다. 불만은 주로 두 각도에 있으니, 두 각도의 이름과 역할을 모두 바꿉니다.
그림 7.7. 이 책에서 사용하는 헤딩과 피치 각
그림 7.7. 이 책에서 사용하는 헤딩과 피치 각
수평각 θ의 이름을 헤딩(heading)의 첫 글자인 h로 바꿉니다. 이는 나침반 방위와 비슷합니다. 헤딩이 0이면 문맥에 따라 ‘앞쪽’ 또는 ‘북쪽’을 뜻하며 표준 항공 규약과도 일치합니다. 1.3.4절의 3차원 직교좌표 규약을 따르면 헤딩 0, 즉 주 극축은 +z에 대응합니다. 또한 우리는 왼손 좌표계를 선호하므로 양의 회전은 위에서 내려다볼 때 시계 방향입니다.
수직각 φ는 피치(pitch)의 첫 글자인 p로 바꿉니다. 얼마나 위나 아래를 보는지 측정하는 값입니다. 기본값인 피치 0은 우리 대부분이 직관적으로 기대하듯 수평 방향을 가리킵니다. 다소 덜 직관적인 점은 양의 피치가 아래로 회전한다는 것입니다. 따라서 피치는 실제로 아래로 기운 각도, 즉 부각(angle of declination)을 측정합니다.
좋지 않은 선택처럼 보일 수도 있지만 왼손 법칙과 일치합니다(그림 1.14 참조). 뒤에서는 이 일관성이 작은 직관적 불편을 감수할 만한 성과를 내는 것을 보게 됩니다.
그림 7.7은 헤딩과 피치가 함께 방향을 정의하는 방법을 보여 줍니다.
원주 7-12: Euler라는 이름은 한 단어짜리 수학 시험이라는 말이 있습니다. 발음할 줄 안다면 수학을 조금 배웠다는 뜻입니다. ‘율러(yooler)’가 아니라 ‘오일러(oiler)’로 발음해 시험에 통과하여 저자들을 자랑스럽게 해 주세요.
원주 7-13: 1장의 첫 부분을 읽으셨지요?
7.3.4 구면좌표의 중복 표현
7.1.2절에서는 성가신 2차원 극좌표의 중복 표현을 살펴보았습니다. 수치가 다른 좌표 쌍이 공간의 같은 점을 가리키면 서로의 별칭입니다. 거기서 제시한 세 가지 기본 유형은 3차원 구면좌표계에도 나타나므로 다시 살펴보겠습니다.
별칭을 만드는 첫 번째 확실한 방법은 어느 각도든 360°의 배수를 더하는 것입니다. 각도 측정의 주기성에서 오는 가장 단순한 중복 표현입니다.
나머지 두 형태는 좌표 간 의존성에서 발생하므로 조금 더 흥미롭습니다. 한 좌표 r의 의미가 다른 좌표, 즉 각도들의 값에 따라 달라지는 것입니다. 이 의존성은 한 종류의 중복 표현과 한 특이점을 만듭니다.
•
2차원 극좌표에서는 반지름 거리 r의 부호를 바꾸고 각도가 반대 방향을 가리키도록 조정하면 별칭이 됩니다. 구면좌표에서도 같습니다. 7.3.3절의 헤딩·피치 규약에서는 헤딩에 180°의 홀수 배를 더해 방향을 뒤집고 피치의 부호를 바꾸면 됩니다.
•
2차원 극좌표의 특이점은 원점에서 발생합니다. r = 0이면 각도 좌표가 무의미하기 때문입니다. 구면좌표에서는 원점에서 두 각도 모두 무의미합니다.
구면좌표에서도 r의 의미가 각도에 따라 바뀌기 때문에 비슷한 중복 표현이 나타납니다. 하지만 구면좌표에는 추가적인 중복 표현도 있습니다. 피치가 회전하는 축 자체가 헤딩에 따라 바뀌기 때문입니다. 이 의존성은 r이 방향에 의존할 때와 유사하게 또 다른 중복 표현과 또 다른 특이점을 만듭니다.
•
각 각도 자체의 단순한 주기적 중복을 제외해도, 서로 다른 헤딩과 피치가 같은 방향을 가리킬 수 있습니다. (h, p)의 별칭은 (h ± 180°, 180° − p)로 만들 수 있습니다. 예를 들어 오른쪽으로 90° 돌아 ‘동쪽’을 향한 뒤 45° 아래를 보는 대신, 왼쪽으로 90° 돌아 ‘서쪽’을 향한 뒤 아래로 135° 회전할 수 있습니다. 몸은 뒤집혀 있겠지만 여전히 같은 방향을 바라봅니다.
•
피치가 ±90° 또는 그 별칭일 때 특이점이 생깁니다. 짐벌 락이라는 이 상황에서는 가리키는 방향이 완전히 수직, 즉 정확히 위나 아래이므로 헤딩은 무의미합니다. 8.3절에서 오일러 각을 설명할 때 짐벌 락을 훨씬 더 자세히 다룹니다.
2차원에서처럼 표준 구면좌표 집합을 정의하면, 3차원 공간의 각 점이 이 집합 안의 정확히 한 좌표 삼중항에 모호함 없이 대응하도록 만들 수 있습니다. r과 h에는 2차원 극좌표에서와 비슷한 제한을 둡니다. 피치와 관련한 제약도 두 개 더합니다. 첫째, 피치를 [−90°, +90°]로 제한합니다.
둘째, 짐벌 락으로 피치가 극단값에 도달하면 헤딩이 무의미해지므로 h = 0으로 강제합니다. 표준 집합에 속하는 점이 만족하는 조건을 정리하면 다음과 같습니다. 여기서는 전통적인 θ·φ 규약이 아니라 이 책의 헤딩·피치 규약을 전제로 합니다.
표준 구면좌표의 조건 | 뜻 |
r ≥ 0 | 거리를 ‘뒤로’ 재지 않습니다. |
−180° < h ≤ 180° | 헤딩은 양·음 방향 각각 반 바퀴 범위입니다. ‘남쪽’에는 +180°를 씁니다. |
−90° ≤ p ≤ 90° | 피치의 한계는 정확히 위와 아래입니다. 뒤로 넘어가도록 피치할 수 없습니다. |
r = 0 ⇒ h = p = 0 | 원점에서는 두 각도를 0으로 둡니다. |
절댓값(p) = 90° ⇒ h = 0 | 정확히 위나 아래를 바라볼 때 헤딩을 0으로 둡니다. |
구면좌표 삼중항 (r, h, p)를 표준형으로 바꾸기
1.
r = 0이면 h = p = 0으로 둡니다.
2.
r < 0이면 r의 부호를 바꾸고 h에 180°를 더하며 p의 부호를 바꿉니다.
3.
p < −90°이면 p ≥ −90°가 될 때까지 p에 360°를 더합니다.
4.
p > 270°이면 p ≤ 270°가 될 때까지 p에서 360°를 뺍니다.
5.
p > 90°이면 h에 180°를 더하고 p = 180° − p로 둡니다.
6.
h ≤ −180°이면 h > −180°가 될 때까지 h에 360°를 더합니다.
7.
h > 180°이면 h ≤ 180°가 될 때까지 h에서 360°를 뺍니다.
목록 7.3은 C 구현입니다. 컴퓨터는 라디안을 좋아한다는 사실을 기억하겠습니다.
// 반지름 거리
float r;
// 라디안 단위의 각도
float heading, pitch;
// 필요한 상수들
const float TWOPI = 2.0f * PI; // 360도
const float PIOVERTWO = PI / 2.0f; // 90도
// 정확히 원점인지 확인한다.
if (r == 0.0f) {
// 원점에서는 각도를 모두 0으로 지정한다.
heading = pitch = 0.0f;
} else {
// 음수 거리를 처리한다.
if (r < 0.0f) {
r = -r;
heading += PI;
pitch = -pitch;
}
// 피치가 범위를 벗어났는가?
if (fabs(pitch) > PIOVERTWO) {
// 90도만큼 이동시킨다.
pitch += PIOVERTWO;
// 0...TWOPI 범위로 감싼다.
pitch -= floor(pitch / TWOPI) * TWOPI;
// 범위를 벗어났는가?
if (pitch > PI) {
// 헤딩을 뒤집는다.
heading += PI;
// 이동을 되돌리면서 pitch = 180 - pitch도 적용한다.
pitch = 3.0f * PI / 2.0f - pitch; // p = 270도 - p
} else {
// 이동을 되돌려 피치를 -90도...+90도로 옮긴다.
pitch -= PIOVERTWO;
}
}
// 짐벌 락인가? 단정밀도의 한계에 가까운,
// 비교적 작은 허용 오차로 검사한다.
if (fabs(pitch) >= PIOVERTWO * 0.9999) {
heading = 0.0f;
} else {
// 정밀도를 보존하기 위해 가능하면 연산을 피하며 헤딩을 감싼다.
if (fabs(heading) > PI) {
// PI만큼 이동시킨다.
heading += PI;
// 0...TWOPI 범위로 감싼다.
heading -= floor(heading / TWOPI) * TWOPI;
// 이동을 되돌려 -PI...PI 범위로 옮긴다.
heading -= PI;
}
}
}
C++
복사
목록 7.3: 구면좌표를 표준형으로 변환하기.
7.3.5 구면좌표와 직교좌표 사이의 변환
구면좌표를 3차원 직교좌표로 바꿔 보겠습니다. 그림 7.8에는 두 좌표가 함께 나와 있습니다. 먼저 직교좌표 공간과 구면좌표 공간 모두 전통적인 오른손 규약을 따른다는 전제로 변환식을 구한 뒤, 이 책의 왼손 규약에 맞는 식을 제시합니다.
그림 7.8. 수학에서 사용하는 구면좌표와 직교좌표
그림 7.8. 수학에서 사용하는 구면좌표와 직교좌표
그림 7.8에는 점과 수직축 사이의 수평 거리인 새 변수 d가 등장합니다. 빗변 r, 나머지 변 d와 z인 직각삼각형에서 다음을 얻습니다.
이제 x와 y만 계산하면 됩니다. φ = 90°라면 본질적으로 2차원 극좌표입니다. φ = 90°일 때의 x, y좌표를 각각 x′, y′라고 하겠습니다. 7.1.3절에 따르면
φ = 90°일 때 d = r이라는 점을 보겠습니다. φ가 줄어들면 d도 줄어듭니다. 닮은삼각형의 성질에 따라 x/x′ = y/y′ = d/r입니다. 삼각형 △drz를 다시 보면 d/r = sin φ임을 알 수 있습니다. 모두 결합하면 다음과 같습니다.
수학의 구면좌표를 3차원 직교좌표로 변환하기
이 식은 오른손 규약을 쓰는 수학자들에게 맞습니다. 직교좌표(1.3.4절)와 구면좌표(7.3.3절)에 이 책의 규약을 적용하면 다음 식을 사용해야 합니다.
직교좌표를 구면좌표로 바꾸는 것은 중복 표현 때문에 더 복잡합니다. 하나의 3차원 위치에 여러 구면좌표가 대응한다는 사실을 알고 있으며, 우리가 원하는 것은 표준 좌표입니다. 다음 유도에서는 게임에서 가장 흔히 쓰는 규약이므로, 항공 분야에서 가져온 식 (7.3)의 규약을 사용합니다.
2차원 극좌표에서처럼 r은 거리 공식을 바로 적용하면 됩니다.
이전처럼 r = 0인 원점의 특이점은 따로 처리합니다.
헤딩은 이 책의 atan2 함수를 사용하면 놀랄 만큼 간단하게 구합니다.
이 방법이 통하는 이유는 atan2가 인수의 비와 부호만 사용하기 때문입니다. 식 (7.3)을 살펴보면 x와 z에는 r cos p라는 공통 배율이 있습니다. 또한 표준 좌표를 사용하므로 r > 0, −90° ≤ p ≤ 90°라고 가정합니다. 따라서 cos p ≥ 0이며 공통 배율은 언제나 음수가 아닙니다. 짐벌 락은 이 책에서 정의한 atan2가 처리합니다.
마지막으로 r을 알면 y로부터 p를 구할 수 있습니다.
arcsin의 치역은 [−90°, 90°]이며, 다행히 표준 집합의 p 범위와 일치합니다. 목록 7.4는 전체 절차를 보여 줍니다.
// 입력 직교좌표
float x, y, z;
// 출력 반지름 거리
float r;
// 라디안 단위의 출력 각도
float heading, pitch;
// 필요한 상수들
const float TWOPI = 2.0f * PI; // 360도
const float PIOVERTWO = PI / 2.0f; // 90도
// 반지름 거리를 계산한다.
r = sqrt(x*x + y*y + z*z);
// 정확히 원점인지 확인한다.
if (r > 0.0f) {
// 피치를 계산한다.
pitch = asin(-y / r);
// 라이브러리 atan2는 2차원 원점에서 정의되지 않으므로
// 짐벌 락인지 검사한다.
if (fabs(pitch) >= PIOVERTWO * 0.9999) {
heading = 0.0f;
} else {
heading = atan2(x, z);
}
} else {
// 원점에서는 각도를 모두 0으로 지정한다.
heading = pitch = 0.0f;
}
C++
복사
목록 7.4: 직교좌표를 구면좌표로 변환하기.
목표 주위를 도는 3차원 카메라
이 책의 규약은 +y가 위, 헤딩 h=0이 +z, 양의 피치 p가 아래쪽입니다. 먼저 수평 투영 길이를 d=r cos p로 구합니다. 이 수평 길이를 헤딩에 따라 x축과 z축으로 분해하면 x와 z가 되고, 수직 성분은 −r sin p입니다.
목표 중심이 (0,1,0), r=5, h=60°, p=−30°이면 카메라 위치는 약 (3.750,3.500,2.165)입니다. 이 각도는 중심에서 카메라 위치로 향하는 방향입니다. 카메라가 바라볼 방향은 중심에서 카메라를 뺀 로 별도로 구합니다. 위치용 구면좌표를 카메라의 시선 각도로 그대로 복사하면 반대쪽을 볼 수 있습니다.
수직 방향 근처에서는 헤딩이 불안정해지므로, 카메라 조작에서는 보통 피치 범위를 수직보다 조금 안쪽으로 제한하거나 이전 헤딩을 유지하는 정책을 둡니다.
원문 코드의 주의점과 보충 구현
목록 7.1은 본문이 정한 (−π,π] 경계를 엄격히 강제하지 않습니다. fabs(theta) > PI 조건은 입력 −PI를 그대로 두며, 범위를 감싸는 계산도 −PI를 만들 수 있습니다. 부동소수점으로 π를 정확히 표현할 수 없다는 사실만으로 경계값이 절대 반환되지 않는 것은 아닙니다. 코드에서 쓰는 PI 상수와 정확히 같은 부동소수점 값은 충분히 나올 수 있습니다. 다음은 유한한 입력에 대해 −π 경계를 +π로 통일하는 별도로 작성한 Python 예제입니다.
import math
def wrap_to_positive_pi(theta):
if not math.isfinite(theta):
raise ValueError("각도는 유한한 수여야 합니다.")
wrapped = (theta + math.pi) % math.tau - math.pi
if wrapped == -math.pi:
wrapped = math.pi
return 0.0 if wrapped == 0.0 else wrapped
def canonical_polar(r, theta):
if not math.isfinite(r) or not math.isfinite(theta):
raise ValueError("반지름과 각도는 유한한 수여야 합니다.")
if r == 0.0:
return 0.0, 0.0
if r < 0.0:
r = -r
theta += math.pi
return r, wrap_to_positive_pi(theta)
def cartesian_to_book_spherical(x, y, z):
if not all(math.isfinite(v) for v in (x, y, z)):
raise ValueError("좌표는 유한한 수여야 합니다.")
r = math.hypot(x, y, z)
if r == 0.0:
return 0.0, 0.0, 0.0
horizontal = math.hypot(x, z)
heading = 0.0 if horizontal == 0.0 else math.atan2(x, z)
pitch = math.atan2(-y, horizontal)
return r, wrap_to_positive_pi(heading), pitch
Python
복사
매우 큰 각도는 입력값을 저장할 때 이미 작은 각도 차이를 잃었을 수 있습니다. 나중에 나머지 연산을 한다고 그 정보가 복원되지는 않으므로 장시간 누적값을 무한정 크게 만들지 않는 편이 좋습니다. 또한 각도를 감싼 뒤 두 각도의 산술평균을 내면 179°와 −179°의 평균이 0°가 되는 문제가 생깁니다. 짧은 각도 차이를 이용해 보간하거나 단위원 벡터로 처리해야 합니다.
목록 7.3과 7.4의 0.9999 검사는 정확한 극점보다 넓은 영역에서 헤딩을 0으로 만듭니다. 이는 수치 계산의 정책이며 엄밀히 같은 점을 보존하는 변환은 아닙니다. 원래 피치가 정확히 ±90°가 아니라면 헤딩을 바꿀 때 작은 수평 변위도 바뀝니다. 이 오차는 반지름이 클수록 커질 수 있습니다. 보충 코드에서는 정확히 수직인 경우만 헤딩을 0으로 정했습니다. 실제 카메라 제어에서 더 넓은 허용 범위를 쓰려면 의도한 동작으로 명시해야 합니다.
목록 7.4의 asin(-y/r)은 반올림으로 입력이 [−1,1]을 조금 벗어나면 문제가 될 수 있습니다. 범위를 제한한 뒤 asin을 사용하거나, 위 보충 예제처럼 atan2(-y, hypot(x,z))를 사용할 수 있습니다. hypot은 제곱을 직접 더하는 표현보다 아주 큰 값이나 작은 값을 다루기에도 유리합니다. NaN·무한대·원점·정확한 극점은 일반 방향 계산과 따로 처리합니다.
7.4 극좌표로 벡터 표현하기 (Vectors in Polar Space)
극좌표로 점을 나타내는 방법과 직교좌표로 벡터를 나타내는 방법을 보았습니다. 극좌표 형식으로 벡터를 표현할 수도 있습니다. 사실 ‘수도 있다’는 말은 컴퓨터를 키보드로 조작하지만 마우스로 조작할 ‘수도 있다’고 말하는 것과 비슷합니다. 극좌표는 벡터의 핵심 속성인 방향과 길이를 직접 나타냅니다.
직교좌표 형식에서는 이 값들이 간접적으로 저장되며, 본질적으로 극좌표 변환에 해당하는 계산을 거쳐야만 얻을 수 있습니다. 이것이 7.2절에서 설명했듯 극좌표가 일상 대화의 자연스러운 표현 수단인 이유입니다.
극좌표 형식을 선호하는 것은 일반인만이 아닙니다. 대부분의 물리학 교과서가 벡터를 간략히 소개하면서 극좌표의 틀로 설명한다는 점은 흥미롭습니다. 그렇게 하면 수학이 훨씬 복잡해지는데도 말입니다.
극좌표 벡터가 구체적으로 어떻게 작동하는지는 사실 이미 다뤘습니다. 인쇄본 192쪽에 있는 2차원 극좌표로 점을 찾는 ‘알고리즘’을 생각해 보겠습니다. ‘원점에서 시작한다’라는 문구만 빼고 나머지를 그대로 두면, 주어진 극좌표가 나타내는 변위, 즉 벡터를 시각화하는 지침이 됩니다. 이는 2.4절과 같은 생각입니다. 벡터는 원점에서 같은 좌표의 점까지 이동하는 변위를 나타내므로 그 점과 관계가 있습니다.
벡터를 직교좌표와 극좌표 사이에서 변환하는 수학도 이미 배웠습니다. 7.1.3절의 방법은 점의 관점으로 제시했지만 벡터에도 똑같이 유효합니다.
7.5 연습문제 (Exercises)
정답은 인쇄본 767쪽에 있습니다.
1.
다음 극좌표의 점들을 그리고 이름을 표시해 보세요.
a = (2, 60°), b = (5, 195°), c = (3, −45°), d = (−2.75, 300°), e = (4, π/6 rad), f = (1, 4π/3 rad), g = (−5/2, −π/2 rad).
2.
다음 2차원 극좌표를 표준형으로 변환해 보세요.
(a) (4, 207°), (b) (−5, −720°), (c) (0, 45.2°), (d) (12.6, 11π/4 rad).
3.
다음 2차원 극좌표를 직교좌표로 변환해 보세요.
(a) (1, 45°), (b) (3, 0°), (c) (4, 90°), (d) (10, −30°), (e) (5.5, π rad).
4.
문제 2의 극좌표를 직교좌표로 변환해 보세요.
5.
다음 2차원 직교좌표를 표준 극좌표로 변환해 보세요.
(a) (10, 20), (b) (−12, −5), (c) (0, 4.5), (d) (−3, 4), (e) (0, 0), (f) (−5280, 0).
6.
다음 원통좌표를 직교좌표로 변환해 보세요.
(a) (4, 120°, 5), (b) (2, 45°, −1), (c) (6, −π/6, −3), (d) (3, 3π, 1).
7.
다음 3차원 직교좌표를 표준 원통좌표로 변환해 보세요.
(a) (1, 1, 1), (b) (0, −5, 2), (c) (−3, 4, −7), (d) (0, 0, −3).
8.
다음 구면좌표 (r, θ, φ)를 표준 수학 규약에 따라 직교좌표로 변환해 보세요.
(a) (4, π/3, 3π/4), (b) (5, −5π/6, π/3), (c) (2, −π/6, π), (d) (8, 9π/4, π/6).
9.
앞 문제의 구면좌표 (a)~(d)를 (r, h, p) 삼중항으로 해석하여 이 책의 게임 규약으로 바꾸어 생각해 보세요. 먼저 표준 (r, h, p) 좌표로 변환하고, 그 표준 좌표를 사용해 게임 규약의 직교좌표로 변환해 보세요.
10.
다음 3차원 직교좌표를 이 책의 수정된 규약에 따라 표준 구면좌표로 변환해 보세요.
(a) (√2, 2√3, −√2), (b) (2√3, 6, −4), (c) (−1, −1, −1), (d) (2, −2√3, 4), (e) (−√3, −√3, 2√2), (f) (3, 4, 12).
11.
구면좌표 공간의 ‘격자선’은 어떤 모습일까요? 이 책의 구면좌표 규약을 가정하고 다음 조건을 만족하는 모든 점의 집합이 이루는 모양을 설명해 보세요. 좌표를 표준 집합으로 제한하지 마세요.
(a) 반지름 r = r₀은 고정하고 h와 p는 임의의 값입니다. (b) 헤딩 h = h₀은 고정하고 r과 p는 임의의 값입니다. (c) 피치 p = p₀은 고정하고 r과 h는 임의의 값입니다.
12.
마감 작업이 한창이던 어느 저녁, 한 게임 개발자가 바람을 쐬며 산책하기로 했습니다. 스튜디오를 나와 남쪽으로 5km를 걸었습니다. 이어 동쪽으로 돌아 5km를 더 걸었습니다. 맑은 공기를 너무 많이 마셔 어지럽다는 생각이 들어 스튜디오로 돌아가기로 했습니다. 북쪽으로 돌아 5km를 걸으니 스튜디오에 도착했습니다. 이제 목록에 남아 있는 몇 안 되는 프로그래밍 버그를 잡을 준비가 되었습니다. 하지만 불행하게도 문 앞에는 배고픈 곰이 기다리고 있었고, 그녀는 산 채로 잡아먹혔습니다.(원주 7-14) 곰은 무슨 색이었을까요?
인도 제도로 항해하는 동안 나는 지식도, 수학도, 지도도 사용하지 않았다. — 크리스토퍼 콜럼버스(1451–1506)
원주 7-14: 우리도 이 상황이 완전히 불가능하다는 것을 압니다. 아니, 게임 개발자가 마감 작업 중에 산책을 하다니?!
직접 확인하기
반지름과 각도를 바꾸면 직교좌표와 점의 위치가 어떻게 달라지는지 확인해 보세요.
Fletcher Dunn · Ian Parberry, 《3D Math Primer for Graphics and Game Development, 2nd Edition》, 7장. 원서 그림과 자료.














