좌표 변환
고친 사람 github-actions[bot]
좌표 변환은 한 기준으로 적은 위치를 다른 기준으로 다시 적어 줍니다. 가리키는 곳은 그대로 두고 그곳을 부르는 숫자만 바꿉니다. 3차원 그래픽스에서는 물체의 꼭짓점이 화면의 픽셀에 닿기까지 이 변환을 여러 번 거칩니다. 지도에서 위도와 경도를 평면 지도의 좌표로 바꾸는 일도 같은 이름으로 부릅니다.
쉽고 빠른 이해
좌표 변환은 같은 점을 다른 기준에서 잰 숫자로 바꿔 적습니다. 책상 위 컵은 책상 모서리에서 재면 (30, 20) 센티미터입니다. 방문에서 재면 (230, 150) 센티미터입니다. 컵은 한 번도 안 움직였습니다.
기준마다 하기 쉬운 일이 다릅니다. 3차원 나무는 꼭짓점 좌표 묶음으로 적습니다. 그 모양은 나무 밑동을 기준으로 적어야 만들기 쉽습니다.
숲에 나무를 심을 때는 숲 전체 기준이 편합니다. 화면에 그릴 때는 카메라 기준이 편합니다. 기준을 하나로 묶어 두면 나무를 옮길 때마다 꼭짓점 숫자를 전부 다시 적어야 합니다.
어떻게 도나:
- 두 기준이 얼마나 떨어졌고, 얼마나 돌아갔고, 눈금이 얼마나 다른지를 행렬 하나에 담습니다
- 점마다 그 행렬을 곱해 새 기준의 숫자를 얻습니다
- 단계가 여럿이면 행렬끼리 먼저 곱해 하나로 합쳐 둡니다
대가는 순서에서 나옵니다. 돌리고 옮기는 것과 옮기고 돌리는 것은 결과가 다릅니다. 지금 가진 숫자가 어느 기준의 것인지 놓치면 물체가 엉뚱한 곳에 그려집니다.
상세
책상 위에 컵이 하나 있습니다. 책상 왼쪽 앞 모서리에서 재면 오른쪽으로 30센티미터, 안쪽으로 20센티미터입니다. 방문에서 재면 오른쪽으로 230센티미터, 안쪽으로 150센티미터입니다. 그동안 컵은 한 번도 움직이지 않았습니다.
좌표 변환은 앞의 숫자를 뒤의 숫자로 바꾸는 계산입니다. 두 기준이 서로 어디에 놓였는지만 알면 점마다 같은 계산으로 바꿀 수 있습니다. 컵의 경우 책상 모서리가 방문에서 (200, 130) 떨어져 있으니 두 수에 각각 더하면 됩니다.
좌표계와 좌표
좌표계는 위치를 숫자로 적는 약속입니다. 기준점인 원점을 정합니다. 어느 방향으로 잴지 축을 정합니다. 한 눈금이 얼마인지도 정합니다.
좌표는 그 약속 아래에서 원점부터 점까지 축마다 몇 눈금 가야 하는지를 적은 숫자 묶음입니다. 2차원이면 (x, y) 두 개, 3차원이면 (x, y, z) 세 개입니다. 묶음 안의 숫자 하나하나를 성분이라고 부릅니다.
같은 점이라도 좌표계가 다르면 좌표가 다릅니다. 두 좌표계가 다를 수 있는 곳은 셋뿐입니다. 원점이 어디 있는지, 축이 어느 쪽을 향하는지, 한 눈금이 얼마인지입니다.
변환을 이루는 세 가지 계산
두 좌표계의 차이 셋을 메우는 계산도 셋입니다. 이 소절은 그 셋이 각각 점에 무엇을 하는지 봅니다.
| 두 좌표계가 무엇이 다른가 | 메우는 계산 | 2차원 점 (x, y) 에 하는 일 |
|---|---|---|
| 원점의 위치 | [[이동 변환 | 이동]] |
| 축의 방향 | [[회전 변환 | 회전]] |
| 눈금의 크기 | [[크기 변환 | 크기 조절]] |
한 번의 좌표 변환은 대개 이 셋을 섞은 것입니다. 컵의 예는 원점만 달랐으므로 이동 하나로 끝났습니다.
이 셋으로만 이뤄진 변환은 곧은 선을 곧은 선으로 보냅니다. 나란한 두 선도 옮긴 뒤에 여전히 나란합니다. 이런 변환을 아핀 변환이라고 부릅니다.
행렬 하나로 적기
행렬은 숫자를 가로세로로 늘어놓은 표입니다. 점의 좌표에 행렬을 곱하면 새 좌표가 나옵니다. 새 좌표의 각 성분은 원래 성분마다 표의 수를 곱해 더한 값입니다. 표에 넣는 숫자에 따라 회전도 되고 크기 조절도 됩니다.
행렬로 적는 까닭은 합칠 수 있어서입니다. 행렬 둘을 곱하면 두 변환을 차례로 한 것과 같은 행렬 하나가 나옵니다. 이 계산을 행렬 곱이라고 부릅니다. 꼭짓점이 백만 개여도 합친 행렬을 한 번씩 곱하면 됩니다.
걸림돌은 이동입니다. 회전과 크기 조절은 성분에 수를 곱해 더하는 계산이라 행렬 곱으로 적힙니다. 이동은 원래 좌표와 상관없는 수를 더해야 합니다. 곱해 더하기만 하는 행렬 곱에는 그런 수를 더할 칸이 없습니다.
그래서 좌표 끝에 성분 하나를 더 붙이고 그 값을 1 로 둡니다. 이 성분을 흔히 w 라고 부릅니다. 2차원 점 (x, y) 는 (x, y, 1) 로, 3차원 점은 (x, y, z, 1) 로 적습니다. 이렇게 성분을 하나 붙인 좌표를 동차 좌표라고 부릅니다.
붙인 1 덕분에 이동도 곱셈으로 적힙니다. 아래는 2차원 점을 오른쪽으로 3, 위로 4 옮기는 행렬입니다. 성분이 셋이 됐으니 행렬도 가로세로 세 칸씩입니다.
[ 1 0 3 ]
[ 0 1 4 ]
[ 0 0 1 ]
점 (2, 1, 1) 에 곱하면 첫 줄은 1×2 + 0×1 + 3×1 = 5 입니다. 둘째 줄은 0×2 + 1×1 + 4×1 = 5 입니다. 끝에 붙인 1 이 마지막 칸의 3 과 4 를 끌어와 더해 준 셈입니다. 결과는 (5, 5, 1) 입니다.
3차원 점은 성분이 넷이 되므로 가로세로 네 칸씩인 4×4 행렬을 씁니다. 그래서 그래픽스에서 다루는 변환 행렬은 거의 다 4×4 입니다.
점을 옮기나 좌표계를 옮기나
같은 행렬을 두 가지로 읽을 수 있습니다. 앞의 행렬은 점을 오른쪽으로 3 옮겼다고 읽을 수 있습니다. 점은 가만히 두고 좌표계가 왼쪽으로 3 물러났다고 읽어도 새 좌표는 똑같이 나옵니다.
좌표 변환은 뒤의 읽기를 따르는 이름입니다. 점은 가만히 있고 부르는 숫자만 바뀝니다.
월드 좌표계는 장면 전체가 함께 쓰는 좌표계입니다. 그래픽스 글에서 「물체를 옮긴다」와 「월드 좌표계로 바꾼다」가 같은 행렬 곱을 가리키는 것도 이 때문입니다.
두 읽기는 방향이 서로 반대입니다. 점을 오른쪽으로 옮기는 행렬은 좌표계를 왼쪽으로 옮기는 행렬과 같습니다. 이 반대 관계가 뒤의 「거꾸로 가는 변환」에서 카메라를 다루는 원리가 됩니다.
꼭짓점이 화면에 닿기까지 거치는 좌표계
3차원 물체는 작은 삼각형을 이어 붙여 적습니다. 삼각형의 꼭짓점 좌표가 물체를 적는 데이터입니다. 이 소절은 꼭짓점 하나가 화면의 픽셀에 닿기까지 거치는 좌표계 여섯과, 그 사이를 잇는 변환 다섯을 따라갑니다.
flowchart TD
A["모델 좌표계 · 물체 기준"] -->|"모델 행렬"| B["월드 좌표계 · 장면 기준"]
B -->|"뷰 행렬"| C["뷰 공간 · 카메라 기준"]
C -->|"투영 행렬"| D["클립 공간 · w로 나누기 전"]
D -->|"원근 나눗셈"| E["정규화 장치 좌표 · -1~1 상자"]
E -->|"뷰포트 변환"| F["화면 좌표 · 픽셀"]
좌표계를 여섯이나 두는 까닭은 좌표계마다 하기 쉬운 일이 달라서입니다. 아래 표가 그 일을 모읍니다.
| 좌표계 | 기준 | 이 좌표계에서 하기 쉬운 일 |
|---|---|---|
| 모델 좌표계 | 물체 자신의 중심이나 바닥 | 물체의 모양을 만들고 고칩니다 |
| 월드 좌표계 | 장면 전체가 함께 쓰는 한 점 | 물체끼리 거리를 재고 부딪혔는지 가립니다 |
| 뷰 공간 | 카메라. 카메라가 보는 방향이 깊이 축입니다 | 무엇이 카메라 앞에 있고 얼마나 먼지 바로 읽습니다 |
| 클립 공간 | 투영 행렬을 곱한 직후의 네 성분 | 화면 밖으로 나간 삼각형을 잘라 냅니다 |
| 정규화 장치 좌표 | 가로·세로 -1부터 1까지인 상자 | 창 크기와 상관없이 화면 안의 위치를 적습니다 |
| 화면 좌표 | 창의 한쪽 모서리. 한 눈금이 픽셀 하나입니다 | 어느 픽셀을 칠할지 정합니다 |
첫 변환은 모델 행렬입니다. 모델 행렬은 물체마다 하나씩 둡니다. 물체를 장면의 어디에 몇 도 돌려 얼마나 크게 놓을지를 담습니다. 숲에 나무 백 그루를 심을 때 나무 모양 데이터는 하나만 두고 모델 행렬만 백 개를 둡니다.
뷰 행렬은 월드 좌표를 카메라 기준으로 바꿉니다. 카메라를 원점에 두고 카메라가 보는 방향을 깊이 축으로 삼습니다. 이 행렬을 만드는 법은 뒤의 「거꾸로 가는 변환」에서 봅니다.
투영 행렬은 카메라에 보이는 범위를 가로·세로 -1부터 1까지인 상자에 담을 준비를 합니다. 곱한 결과는 성분이 넷인 좌표입니다. 앞에서 1 로 두었던 넷째 성분 w 가 이 곱셈으로 1 이 아닌 값이 됩니다. 먼 것을 작게 그리는 투영이면 먼 점일수록 w 가 커집니다.
그다음 원근 나눗셈이 x·y·z 를 w 로 나눕니다. 먼 점일수록 큰 수로 나누니 화면 가운데 쪽으로 모여 작게 보입니다. 점마다 다른 값으로 나누는 계산이라 행렬 곱으로는 적을 수 없어서 따로 떼어 둔 단계입니다.
나눈 결과가 정규화 장치 좌표입니다. 화면에 보일 점은 이 상자 안에 들어옵니다.
마지막 뷰포트 변환이 이 상자를 창의 픽셀 크기로 늘립니다. 뷰포트는 창 안에서 그림을 그리는 직사각형 영역입니다. 이 단계는 크기 조절과 이동만으로 끝납니다.
모델·뷰·투영 행렬 셋은 대개 CPU(Central Processing Unit, 중앙 처리 장치)에서 만들어 그래픽 칩으로 넘깁니다. 그래픽 칩은 GPU(Graphics Processing Unit, 그래픽 처리 장치)라고 부릅니다.
정점은 꼭짓점의 다른 이름입니다. 꼭짓점마다 행렬을 곱하는 일은 정점 셰이더가 맡습니다. 정점 셰이더는 꼭짓점마다 한 번씩 GPU 에서 도는 작은 프로그램입니다. 원근 나눗셈과 뷰포트 변환은 그 뒤에 GPU 가 정해진 순서로 해 줍니다.
합치는 순서
행렬 곱은 순서를 바꾸면 결과가 바뀝니다. 이 소절은 점 하나에 회전과 이동을 순서만 바꿔 해 봅니다.
회전은 늘 원점을 중심으로 돌립니다. 시계 반대 방향으로 90도 돌리면 (x, y) 가 (−y, x) 가 됩니다.
아래 코드에서 rot90 이 이 회전이고 move 가 이동입니다.
const rot90 = ([x, y]) => [-y, x];
const move = ([x, y], dx, dy) => [x + dx, y + dy];
// 돌리고 나서 옮기기
move(rot90([2, 1]), 2, 0); // [1, 2]
// 옮기고 나서 돌리기
rot90(move([2, 1], 2, 0)); // [-1, 4]
같은 두 계산을 했는데 점이 다른 곳에 닿았습니다. 먼저 옮기면 점이 원점에서 멀어진 뒤에 돕니다. 원점을 중심으로 큰 원을 그리며 돌기 때문에 엉뚱한 곳으로 갑니다.
그래서 한 물체의 모델 행렬은 흔히 크기 조절, 회전, 이동 순서로 합칩니다. 물체가 아직 원점에 있을 때 늘이고 돌린 다음, 장면 안의 제 위치로 옮기는 순서입니다.
행렬을 종이에 적는 순서는 이 순서와 반대로 보일 수 있습니다. 점을 세로로 세워 행렬 오른쪽에 곱하는 관례에서는 가장 먼저 할 변환을 맨 오른쪽에 적습니다. 이동 × 회전 × 크기 조절로 적으면 크기 조절이 먼저 일어납니다.
점을 가로로 눕혀 행렬 왼쪽에 곱하는 관례도 있습니다. 그 관례에서는 적는 순서와 일어나는 순서가 같습니다. 남이 만든 행렬을 가져다 쓸 때는 어느 관례인지부터 봅니다.
부모를 따라 움직이는 좌표계
물체가 다른 물체에 붙어 있으면 좌표계도 겹겹이 붙습니다. 이 소절은 자동차와 바퀴로 그 구조를 봅니다.
flowchart TD
W["월드 좌표계"] --> C["자동차 좌표계"]
C --> F1["앞 왼쪽 바퀴 좌표계"]
C --> F2["앞 오른쪽 바퀴 좌표계"]
C --> R1["뒤 왼쪽 바퀴 좌표계"]
C --> R2["뒤 오른쪽 바퀴 좌표계"]
바퀴의 모델 행렬은 월드 좌표계가 아니라 자동차 좌표계를 기준으로 적습니다. 차체의 어디에 붙었고 지금 몇 도 굴렀는지만 담습니다.
바퀴를 그릴 때는 바퀴 행렬에 자동차 행렬을 이어 곱합니다. 그러면 자동차가 움직여도 바퀴 네 개의 행렬은 손대지 않고 같이 따라갑니다. 이렇게 좌표계를 부모와 자식으로 매단 구조를 씬 그래프라고 부릅니다.
거꾸로 가는 변환
좌표 변환은 거꾸로도 필요합니다. 이 소절은 두 경우를 봅니다. 뷰 행렬을 만들 때와, 화면에서 누른 곳이 장면의 어디인지 찾을 때입니다.
어떤 변환을 되돌리는 행렬을 그 행렬의 역행렬이라고 부릅니다. 원래 행렬과 역행렬을 곱하면 점을 하나도 안 바꾸는 행렬이 나옵니다.
여러 변환을 합친 행렬을 되돌릴 때는 순서도 뒤집힙니다. 돌리고 나서 옮긴 것을 되돌리려면 먼저 반대로 옮기고 그다음 반대로 돌립니다.
뷰 행렬이 이 원리로 만들어집니다. 카메라도 장면 안의 물체처럼 월드 좌표계 안에 위치와 방향을 가집니다. 카메라를 월드에 놓는 행렬을 뒤집으면 월드 좌표를 카메라 기준으로 바꾸는 행렬이 나옵니다. 그것이 뷰 행렬입니다.
그래서 카메라를 오른쪽으로 옮기는 것은 장면 전체를 왼쪽으로 옮기는 것과 같습니다. 앞의 「점을 옮기나 좌표계를 옮기나」에서 본 반대 관계입니다.
화면을 누른 곳에서 장면의 어느 물체를 눌렀는지 찾는 일을 피킹이라고 부릅니다. 누른 픽셀을 뷰포트 변환, 투영 행렬, 뷰 행렬의 역으로 차례로 되짚어 월드 좌표계로 가져옵니다.
되짚은 결과는 점이 아니라 선입니다. 화면의 점 하나에는 카메라에서 뻗어 나가는 선 위의 모든 점이 겹쳐 보이기 때문입니다. 그 선이 처음 부딪히는 물체가 누른 물체입니다.
변환을 줄이거나 건너뛰는 경우
모든 그림이 좌표계 여섯을 다 거치지는 않습니다. 이 소절은 단계를 덜어 내는 경우 셋을 봅니다.
화면에 붙어 움직이지 않는 메뉴와 버튼은 처음부터 픽셀 단위로 적습니다. 카메라가 없으니 뷰 행렬이 필요 없습니다. 투영은 거리와 상관없이 크기가 같은 직교 투영을 씁니다. 픽셀 단위로 적은 좌표를 정규화 장치 좌표의 -1~1 상자로 줄여 넣기만 합니다.
다 그린 화면 위에 흐림이나 색 보정을 덧입히는 후처리는 화면을 꽉 채우는 사각형 하나를 그립니다. 그 네 모서리를 처음부터 정규화 장치 좌표로 적으면 곱할 행렬이 하나도 없습니다.
좌표계를 나눠 둘 까닭이 없으면 나누지 않습니다. 물체가 하나뿐이고 움직이지도 않으면 모델 좌표계를 월드 좌표계와 같게 둬도 됩니다. 이때 모델 행렬은 점을 하나도 안 바꾸는 행렬입니다.
헷갈리기 쉬운 약속과 실수
좌표 변환의 실수는 대개 숫자는 맞고 기준이 틀린 경우입니다. 계산은 오류 없이 끝납니다. 그림만 이상하게 나와서 원인을 찾기 어렵습니다.
그 전에 알아 둘 약속이 하나 있습니다. x 축과 y 축이 같아도 z 축이 화면 안쪽을 향하느냐 바깥쪽을 향하느냐가 좌표계마다 다릅니다. 오른손 엄지·검지·중지로 x·y·z 축을 차례로 가리키면 오른손 좌표계, 왼손으로 가리키면 왼손 좌표계라고 부릅니다.
| 실수 | 화면에 나타나는 것 |
|---|---|
| 곱하는 순서를 뒤바꿈 | 물체가 제 위치에서 돌지 않고 원점 둘레를 크게 돌아 엉뚱한 곳에 놓입니다 |
| 서로 다른 좌표계의 값을 섞어 계산함 | 월드 좌표의 조명 위치와 뷰 공간의 꼭짓점으로 거리를 재면 빛이 엉뚱한 쪽에서 비칩니다 |
| 오른손 좌표계와 왼손 좌표계를 섞음 | 물체가 거울에 비친 듯 뒤집히거나 카메라 뒤로 넘어가 안 보입니다 |
셋 다 계산 한 줄은 멀쩡합니다. 그래서 변수 이름에 좌표계를 붙여 두는 습관이 흔합니다.
posWorld·posView 처럼 적으면 섞어 쓰는 줄이 눈에 띕니다.
지도와 선형대수의 좌표 변환
같은 이름을 다른 분야도 씁니다. 백엔드에서 위치 데이터를 다루면 지도 쪽 좌표 변환을 만납니다. 아래 표는 세 분야가 무엇을 무엇으로 바꾸는지 모읍니다.
| 분야 | 무엇을 무엇으로 바꾸나 | 계산 |
|---|---|---|
| 3차원 그래픽스 | 물체 기준 좌표를 화면의 픽셀 위치로 | 4×4 행렬 곱과 나눗셈 |
| 지도와 위치 데이터 | 위도·경도를 평면 지도의 미터 단위 좌표로 | 둥근 면을 평평하게 펴는 공식 |
| 선형대수 | 같은 화살표를 다른 기준 화살표로 잰 숫자로 | 행렬 곱 |
위도와 경도는 지구 위의 위치를 각도 둘로 적습니다. 각도라서 두 점의 값을 빼기만 해서는 몇 미터 떨어졌는지 나오지 않습니다. 그래서 지도를 그리거나 거리를 잴 때는 미터 단위의 평면 좌표로 바꿉니다.
둥근 면을 평평하게 펴는 계산을 지도 투영이라고 부릅니다. 곡면을 펴는 일이라 행렬 곱 하나로 적히지 않습니다. 넓은 지역을 펼수록 거리나 넓이가 어딘가에서 늘어납니다.
같은 위도·경도라도 지구 모양을 어떻게 어림했느냐에 따라 가리키는 땅이 조금씩 다릅니다. 이 어림을 측지 기준계라고 부릅니다. 기준계가 다른 좌표를 섞으면 점이 어긋나 찍히므로, 기준계를 맞추는 계산도 좌표 변환이라고 부릅니다.
위도와 경도를 적는 순서도 형식마다 다릅니다. GeoJSON 은 경도를 먼저 적습니다. 위도를 먼저 적는 형식도 흔합니다. 둘이 섞이면 점이 다른 대륙이나 바다에 찍힙니다.
선형대수의 벡터는 크기와 방향을 가진 화살표를 숫자 묶음으로 적은 것입니다. 그 숫자를 잴 때 기준으로 삼는 화살표 묶음을 기저라고 부릅니다.
기저를 바꿔 같은 벡터를 다시 적는 일이 기저 변환입니다. 그래픽스의 회전과 크기 조절은 이 계산의 한 경우입니다. 그래픽스는 여기에 이동까지 담으려고 동차 좌표를 붙였습니다.
관련 항목
좌표 변환이 속하는 상위 분류
그래픽스 · 3차원 그래픽스 · 렌더링 · 선형대수 · 기하학
좌표 변환을 이루는 기본 변환
이동 변환 · 회전 변환 · 크기 변환 · 전단 변환 · 아핀 변환 · 변환 행렬
좌표 변환을 적는 수학 도구
좌표계 · 원점 · 벡터 · 행렬 · 행렬 곱 · 동차 좌표 · 회전 행렬 · 쿼터니언 · 삼각함수
꼭짓점이 차례로 거치는 좌표계
모델 좌표계 · 월드 좌표계 · 뷰 공간 · 클립 공간 · 정규화 장치 좌표 · 화면 좌표
좌표계 사이를 잇는 행렬과 처리 단계
모델 행렬 · 뷰 행렬 · 투영 행렬 · 원근 투영 · 직교 투영 · 원근 나눗셈 · 뷰포트 변환 · 뷰포트
좌표 변환을 돌리는 장치와 프로그램
그래픽스 파이프라인 · 정점 · 정점 셰이더 · GPU · CPU · 래스터화
좌표계를 부모 자식으로 매다는 구조
씬 그래프 · 계층 변환 · 로컬 좌표계 · 스켈레탈 애니메이션
좌표 변환을 거꾸로 되짚는 계산
역행렬 · 피킹 · 레이 캐스팅
좌표 변환을 적을 때 갈리는 관례
오른손 좌표계 · 왼손 좌표계 · 열 벡터 · 행 벡터 · 행 우선 순서 · 열 우선 순서
위치 데이터의 좌표 변환이 다루는 개념
위도 · 경도 · 지도 투영 · 측지 기준계 · WGS84 · 메르카토르 도법 · GIS · GeoJSON
선형대수에서 같은 이름이 가리키는 계산
기저 · 기저 변환 · 선형 변환 · 좌표 벡터
다른 이름: coordinate transformation · coordinate transform · 좌표계 변환