3D 카메라 코드를 보다 보면 다음과 같이 위치와 바라볼 점, 위쪽 방향을 저장하는 구조를 자주 만난다.
Vector3 Position;
Vector3 Target;
Vector3 Up;
Position은 카메라가 있는 곳이고, Target은 바라볼 점이라고 한다. 그런데 카메라의 위치와 회전을 알고 있다면 시선 방향도 알 수 있다. 그렇다면 Target을 따로 저장할 이유가 있을까?
컴퓨터 그래픽스에서 카메라 정보는 크게 두 방식으로 표현할 수 있다. Position + Orientation은 위치와 자세를 직접 저장한다. Position + Target + Up은 위치와 관심점, 기준 위쪽 방향을 저장하고 이 값들로 카메라의 자세를 계산한다. 둘 다 카메라를 표현하지만, 무엇을 상태로 저장하느냐가 다르다.
먼저 결론부터 말하면, 카메라 자체를 표현하는 데 Target은 필수가 아니다. 카메라의 위치와 자세만 있으면 충분하다. Target은 카메라의 방향을 만드는 한 가지 입력이면서, Orbit Camera처럼 카메라가 관심점을 중심으로 움직일 때 그 관심점과 거리를 표현하는 데 유용하다.
카메라의 기본 상태는 Position과 Orientation
카메라를 완전히 설명하려면 두 가지가 필요하다.
- 카메라가 어디에 있는가?
- 카메라가 어떤 자세를 취하고 있는가?
즉 카메라의 기본 상태는 Position + Orientation이다. 회전은 Quaternion으로 저장할 수도 있고, 카메라의 축인 Forward, Right, Up으로 나타낼 수도 있다.
Vector3 Position;
Quaternion Rotation;
예를 들어 Free Camera나 FPS Camera는 특정 대상을 바라볼 필요가 없다. 위치와 회전을 저장하고, 회전에서 시선 방향을 계산하면 된다.
Vector3 Forward = Rotation * LocalForward;
이런 카메라는 Forward 방향으로 이동할 수 있다.
Position += Forward * Speed * DeltaTime;
카메라가 어디를 보고 있는지는 알지만, 특정한 목적지나 회전 중심은 정해져 있지 않다.
Target은 방향이 아니라 점이다
Target은 방향 벡터가 아니라 공간상의 한 점이다. 카메라 위치를 P, Target을 T라고 하면, 카메라에서 Target으로 향하는 벡터는 다음과 같다.
$$ \mathbf{d} = \mathbf{T} - \mathbf{P} $$
이 벡터를 정규화하면 카메라의 시선 방향을 얻는다.
$$ \mathbf{Forward} = \frac{\mathbf{T} - \mathbf{P}}{|\mathbf{T} - \mathbf{P}|} $$
예를 들어 Position = (0, 0, 0), Target = (10, 0, 0)이라면 카메라는 양의 X 방향을 바라본다. 이때 시선 방향은 (1, 0, 0)이다.
Position ───────────────▶ Target
Forward
따라서 Target은 시선 방향을 계산하기 위한 입력이다. Target 자체는 점이지만, 카메라의 Position과 함께 두면 무엇을 바라보는지와 어느 방향으로 향하는지를 함께 표현한다. 관심점은 Target으로 직접 알 수 있고, 방향은 Target - Position을 정규화해 구한다. Forward만 저장하면 방향은 알 수 있지만, 그 방향 위의 어느 지점을 관심점으로 삼는지는 알 수 없다.
LookAt은 Target으로 카메라 좌표축을 만든다
LookAt 방식은 카메라 위치, 바라볼 점, 기준이 되는 위쪽 방향을 이용해 카메라의 좌표축을 만든다. 개념적인 계산은 다음과 같다.
Forward = Normalize(Target - Position);
Right = Normalize(Cross(Forward, WorldUp));
Up = Cross(Right, Forward);
Forward는 카메라에서 Target으로 향한다. WorldUp은 세상에서 위쪽이라고 정한 방향이다. Forward와 WorldUp의 외적으로 좌우 방향을 구하고, 두 축으로부터 카메라의 Up을 다시 계산한다.
그 결과 만들어진 카메라 좌표축과 위치를 사용해 View Matrix를 구성한다. 행렬을 어느 쪽에서 곱하는지, 카메라가 로컬 +Z와 -Z 중 어느 쪽을 바라보는지에 따라 외적의 순서와 부호는 달라질 수 있다. 위 코드는 축의 관계를 보여주는 한 가지 컨벤션이다.
Position과 Target만으로는 카메라 자세가 완전히 정해지지 않는 점도 중요하다. 두 값은 바라보는 방향을 정하지만, 그 방향을 축으로 카메라가 얼마나 기울어졌는지, 즉 Roll은 정하지 못한다. 같은 점을 바라보면서 카메라를 시선 축 주위로 돌릴 수 있기 때문이다. 그래서 LookAt에는 보통 Up 방향도 함께 전달한다.
단, WorldUp과 Forward가 평행하거나 거의 평행하면 외적의 길이가 0에 가까워져 좌우 축을 안정적으로 만들 수 없다. 실제 구현에서는 이런 입력을 피하거나 별도 처리를 해야 한다.
Orbit Camera에서 Target은 회전 중심이다
에디터에서 물체를 선택한 뒤 그 물체 주위로 카메라를 돌리는 장면을 생각해보자. 이 카메라는 단순히 바라보는 방향만 바꾸는 것이 아니다. Target을 중심으로 공전한다.
이때 카메라 상태를 다음처럼 둘 수 있다.
Vector3 Target;
float Distance;
float Yaw;
float Pitch;
Yaw와 Pitch로 Target에서 카메라까지의 오프셋을 만들고, 그 오프셋을 Target에 더해 카메라 위치를 구한다.
Position = Target + OrbitOffset(Yaw, Pitch, Distance);
이 표현에서는 Target이 단순히 시선 방향을 계산하는 임시 입력이 아니다. 사용자가 선택한 물체나 회전할 중심점처럼, 카메라가 계속 기준으로 삼을 상태다.
반대로 Position과 Forward, Right, Up만 갖고 있다면 카메라가 향하는 방향은 알 수 있어도 무엇을 중심으로 회전해야 하는지는 알 수 없다. Orbit Camera를 만들려면 회전 중심을 나타내는 값이 추가로 필요하다. 그 값이 바로 Target, 즉 관심점이다.
Target은 거리의 기준도 제공한다
카메라와 Target 사이의 거리는 다음과 같다.
$$ Distance = |\mathbf{Target} - \mathbf{Position}| $$
Orbit Camera에서 마우스 휠로 가까이 가거나 멀어지는 동작은 흔히 이 거리를 바꾸는 방식으로 구현한다. 거리에 최소·최대 범위를 둘 수도 있다.
Distance = Clamp(Distance - ScrollDelta, MinDistance, MaxDistance);
Position = Target + OrbitOffset(Yaw, Pitch, Distance);
다만 여기서 말하는 Zoom은 카메라를 관심점에 가까이 또는 멀리 움직이는 동작이다. 렌즈의 초점 거리나 시야각(Field of View)을 바꾸는 광학적 Zoom과는 다르다. 일반적인 카메라 이동은 시야각을 유지한 채 위치를 바꾸고, 렌즈 Zoom은 위치를 유지한 채 시야각을 바꾼다.
세 축을 직접 저장하는 방식과의 비교
Position + Orientation에서 Orientation을 표현하는 방법으로는 Quaternion을 저장하는 대신 Forward, Right, Up 세 축을 직접 저장하는 방법도 있다. 카메라 좌표계를 바로 알 수 있어 편리해 보이지만, 이 세 벡터는 독립적인 값이 아니다. 각 벡터의 길이가 1이어야 하고, 서로 직교해야 한다.
|Forward| = |Right| = |Up| = 1
Forward ⟂ Right ⟂ Up
세 벡터를 따로 갱신하면 부동소수점 오차가 쌓여 길이나 직교 관계가 조금씩 틀어질 수 있다. 그래서 Free Camera에서는 보통 Position과 Quaternion Rotation을 기본 상태로 저장하고, 필요할 때 세 축을 계산한다. 반면 Orbit Camera에서는 회전 중심과 거리처럼 그 카메라 동작에 필요한 값을 상태로 둔다.
카메라 종류에 따라 필요한 상태가 다르다
| 카메라 동작 | 자연스러운 상태 표현 |
|---|---|
| Free Camera, FPS Camera | Position + Rotation |
| Roll을 포함해 자유롭게 회전하는 카메라 | Position + Quaternion Rotation |
| Orbit Camera, 에디터 카메라 | Target + Distance + 각도 또는 이에 해당하는 상태 |
| 대상을 계속 따라가는 카메라 | 대상 위치나 관심점 + 카메라 오프셋 |
모든 카메라가 같은 상태를 가질 필요는 없다. 어떤 카메라는 자신의 위치와 자세가 핵심이고, 어떤 카메라는 따라갈 대상이나 공전 중심이 핵심이다.
정리
카메라의 기본적인 상태는 Position + Orientation이며, Target이 반드시 필요한 것은 아니다. Free Camera라면 위치와 회전만으로 카메라를 표현할 수 있다.
Target을 두면 카메라가 바라보는 점을 명시할 수 있다. 그리고 Position과 Target의 차이를 계산하면 그 점을 향하는 방향과 거리를 얻는다. 즉 두 값을 함께 사용하면 관심점과 그곳을 향하는 방향이라는 정보를 모두 표현할 수 있다. 특히 Orbit Camera에서는 Target이 회전 중심이자 거리의 기준이 된다. Target은 카메라의 회전을 직접 저장하지는 않지만, Position과 함께 카메라의 관심점과 시선 방향을 정의한다.
결국 카메라를 설계할 때는 “Target이 필요한가?”에 앞서 이 카메라가 어떤 상태를 중심으로 움직이는지 생각하면 된다. 자기 자세를 중심으로 움직이는 카메라에는 Position과 Rotation이 자연스럽고, 어떤 점을 중심으로 돌거나 대상을 따라가는 카메라에는 Target이 자연스럽다.