직교 투영
고친 사람 github-actions[bot]
직교 투영은 3차원 장면을 화면에 옮길 때 멀리 있는 것을 작게 줄이지 않는 방법입니다. 크기가 같은 상자 둘은 거리가 달라도 화면에서 같은 크기로 그려집니다. 설계 도면이나 위에서 내려다보는 게임 화면처럼 길이를 줄이지 않고 읽어야 하는 그림에 씁니다. 수학의 선형대수에도 같은 이름이 있습니다. 이 편은 그래픽스의 뜻을 중심으로 다룹니다.
쉽고 빠른 이해
무슨 일을 하나 — 입체 장면을 납작한 화면에 옮깁니다. 이때 거리에 따라 크기를 바꾸지 않습니다. 건물 도면에서 1층 창문과 10층 창문이 같은 크기로 그려지는 것이 이 방법입니다.
왜 이렇게 하나 — 먼 것을 작게 그리면 그림 위의 길이가 거리마다 달라집니다. 도면이나 지도처럼 그림에서 길이를 재야 하는 경우에는 크기를 줄이지 않아야 합니다.
어떻게 도나
- 카메라 앞에 보일 범위를 반듯한 상자 하나로 정합니다
- 뒤 단계가 늘 같은 크기로 받도록, 그 상자를 약속된 크기의 상자로 늘이고 옮깁니다. 이때 앞뒤 거리는 가로·세로 위치에 섞이지 않습니다
- 가로·세로 위치로 화면에 놓고, 앞뒤 거리는 무엇이 앞인지 가리는 데만 씁니다
대가 — 깊이감이 사라집니다. 먼 것도 가까운 것과 같은 크기라서 크기만 보고는 무엇이 앞에 있는지 알 수 없습니다. 카메라를 앞으로 옮겨도 물체가 커지지 않습니다.
상세
이 절은 직교 투영이 무엇을 하는지 그림자 하나로 먼저 봅니다. 그다음 먼 것을 줄이는 원근 투영과 어디서 갈리는지 보고, 그 차이가 계산의 어느 줄에서 생기는지 숫자를 넣어 따라갑니다. 마지막으로 어디에 쓰고 무엇을 잃는지, 선형대수의 같은 이름과는 어떻게 이어지는지를 짚습니다.
한낮에 해가 머리 바로 위에 뜨면 물건의 그림자가 발밑 땅에 곧게 떨어집니다. 탁자 위에 놓인 공과 바닥에 놓인 공은 높이가 달라도 그림자 크기가 같습니다. 그림자만 봐서는 공이 얼마나 높이 떠 있는지 알 수 없습니다.
3차원 장면을 2차원 화면에 옮기는 계산을 투영이라고 부릅니다. 모니터는 평평하므로 입체로 만든 장면은 그리기 전에 한 번 납작해져야 합니다. 이때 앞뒤 거리를 어떻게 다룰지를 정하는 것이 투영 방법입니다.
직교 투영은 모든 점을 화면에 수직인 방향으로 곧게 내려 옮깁니다. 「직교」는 직각으로 만난다는 뜻입니다. 점마다 화면까지 긋는 선이 모두 화면과 직각을 이루므로 선끼리 나란합니다.
그래서 화면 위의 위치는 카메라에서 본 그 점의 가로·세로 좌표만으로 정해집니다. 앞뒤 거리는 위치에 섞이지 않습니다. 화면과 나란하게 놓인 길이 2인 막대는 카메라 바로 앞에 있든 100만큼 떨어져 있든 화면에서 같은 길이로 그려집니다.
원근 투영과 갈리는 점
원근 투영은 사람 눈이나 사진기가 보는 방식을 흉내 냅니다. 점마다 긋는 선이 모두 눈 한 점으로 모입니다. 그래서 멀리 있는 것일수록 화면에서 작아집니다.
두 방법을 세 가지 축으로 나란히 놓으면 이렇습니다.
| 직교 투영 | 원근 투영 | |
|---|---|---|
| 점에서 화면으로 긋는 선 | 모두 나란합니다 | 눈 한 점으로 모입니다 |
| 먼 물체의 크기 | 가까운 것과 같습니다 | 작아집니다 |
| 나란한 두 선 | 화면에서도 나란합니다 | 멀리서 한 점으로 모입니다 |
셋째 줄이 그림을 보고 가르기 가장 쉬운 단서입니다. 기찻길 사진에서 두 레일이 지평선에서 만나는 것은 원근 투영의 결과입니다. 직교 투영으로 그린 기찻길은 끝까지 폭이 같습니다.
보이는 범위를 정하는 상자
투영은 카메라를 기준으로 잰 좌표에서 출발합니다. 카메라를 원점에 두고 카메라가 보는 방향을 앞뒤 축으로 잡은 좌표계가 뷰 공간입니다. 이 좌표계에서는 앞뒤 좌표가 곧 카메라에서 떨어진 거리입니다.
화면에 무엇이 들어올지는 카메라 앞의 한 구역으로 정합니다. 이 구역이 뷰 볼륨입니다. 구역 밖에 있는 물체는 그려지지 않습니다.
직교 투영의 뷰 볼륨은 직육면체 상자입니다. 상자는 여섯 값으로 정합니다. 왼쪽 끝과 오른쪽 끝, 아래 끝과 위 끝, 그리고 가까운 면과 먼 면까지의 거리입니다. 가까운 면을 근평면, 먼 면을 원평면이라고 부릅니다.
원근 투영의 뷰 볼륨은 모양이 다릅니다. 눈에서 멀어질수록 넓어지는 사각뿔에서 꼭대기를 잘라 낸 모양입니다. 이 모양을 뷰 절두체라고 부릅니다. 직교 투영은 선이 나란하므로 상자의 앞면과 뒷면 넓이가 같습니다.
상자를 약속된 상자로 옮기는 계산
뷰 볼륨 상자가 화면 크기와 상관없는 상자로 바뀌는 계산을 숫자를 넣어 따라갑니다. 그러면 직교 투영이 왜 먼 것을 줄이지 않는지가 식 모양에서 드러납니다.
뷰 볼륨의 크기는 장면마다 다릅니다. 그런데 투영 뒤의 단계들은 상자 크기가 제각각이면 곤란합니다. 그래서 투영의 결과는 가로·세로가 모두 -1에서 1 사이인 약속된 상자로 모읍니다. 이 상자 안의 좌표를 정규화 장치 좌표라고 부릅니다.
상자가 하나로 정해지면 뒤 단계는 그 상자만 알면 됩니다. 상자 밖으로 삐져나온 부분을 잘라 내는 일이나, 상자를 창의 픽셀 사각형으로 옮기는 뷰포트 변환이 어떤 투영을 썼든 똑같이 처리됩니다.
직교 투영은 이 옮기기를 축마다 따로 합니다. 뷰 볼륨의 왼쪽 끝이 -1로, 오른쪽 끝이 1로 가도록 늘이고 옮깁니다. 세로축과 앞뒤 축도 같은 방법으로 옮깁니다.
아래는 왼쪽 끝 -4, 오른쪽 끝 4, 아래 끝 -3, 위 끝 3인 상자에서 좌표 몇 개를 옮긴 것입니다. x · y 는 뷰 공간 좌표입니다. x' · y' 는 옮겨 간 정규화 장치 좌표입니다.
상자 왼 -4 오 4 아래 -3 위 3
x' = 2 * (x - 왼) / (오 - 왼) - 1
y' = 2 * (y - 아래) / (위 - 아래) - 1
x = 2 // x' = 0.5
x = -4 // x' = -1
y = 3 // y' = 1
y = 0 // y' = 0
상자 오른쪽 끝의 반쯤 되는 2는 한가운데와 오른쪽 끝 사이의 0.5로 갑니다. 왼쪽 끝 -4는 -1로, 위 끝 3은 1로 갑니다. 상자 안의 점은 모두 -1과 1 사이로 들어옵니다.
식을 다시 보면 x' 을 구하는 데 x 하나만 들어갑니다. 앞뒤 거리는 식에 없습니다. 그래서 가로 좌표가 같은 점은 가까이 있든 멀리 있든 같은 x' 으로 갑니다. 먼 것을 줄이지 않는 성질이 이 식 모양에서 나옵니다.
원근 투영은 이와 달리 가로·세로 좌표를 앞뒤 거리로 나눕니다. 멀리 있는 점일수록 큰 수로 나뉘어 한가운데 쪽으로 끌려옵니다. 먼 것이 작아 보이는 것이 이 나눗셈 때문입니다.
행렬 한 개로 적은 모양
늘이고 옮기는 계산은 행렬 하나로 적습니다. 행렬은 숫자를 가로세로로 늘어놓은 표입니다. 점의 좌표에 곱하면 늘이기와 옮기기가 한 번에 적용됩니다. 투영을 담은 행렬이 투영 행렬입니다.
좌표가 셋뿐이면 행렬 곱셈으로 옮기기를 적을 수 없습니다. 그래서 3차원 점에 넷째 성분 w 를 붙여 넷으로 만듭니다. 이렇게 늘린 좌표가 동차 좌표입니다. 보통의 점은 w 를 1로 두고 시작합니다.
직교 투영 행렬은 이런 모양입니다.
[ sx 0 0 tx ]
[ 0 sy 0 ty ]
[ 0 0 sz tz ]
[ 0 0 0 1 ]
대각선의 sx · sy · sz 는 축마다 늘이는 배율입니다. 맨 오른쪽 열의 tx · ty · tz 는 축마다 옮기는 양입니다.
맨 오른쪽 열은 점의 넷째 성분 w 와 곱해집니다. w 가 1이므로 곱해도 tx 가 그대로 더해집니다. 옮기기가 곱셈 하나 안에 들어가는 것이 이 때문입니다.
앞 소절의 상자라면 sx 는 2 ÷ 8 = 0.25입니다. tx 는 0입니다. 점 x = 2 에 0.25를 곱하면 앞에서 구한 0.5가 나옵니다.
앞뒤 축을 어느 방향으로 두고 어느 범위로 모을지는 그래픽스 API(Application Programming Interface, 프로그램이 기능을 불러 쓰는 창구)마다 약속이 다릅니다. 그래서 sz · tz 의 부호와 값도 API마다 다릅니다. 가로·세로 두 줄의 모양은 같습니다.
맨 아랫줄이 가르는 두 투영
직교 투영과 원근 투영의 차이는 계산에서 행렬의 맨 아랫줄 0 0 0 1 한 줄로 드러납니다. 그 한 줄이 좌표에 무슨 일을 하는지 단계를 따라 짚습니다.
투영 행렬을 곱한 직후의 좌표가 클립 공간 좌표입니다. 뷰 볼륨 밖의 부분을 잘라 내는 클리핑이 이 좌표에서 일어나서 붙은 이름입니다.
클리핑 다음에는 원근 나누기가 옵니다. 가로·세로·앞뒤 성분을 각각 w 로 나누는 계산입니다. 이 나눗셈을 거쳐야 정규화 장치 좌표가 됩니다.
직교 투영 행렬은 맨 아랫줄이 0 0 0 1 이므로 곱한 뒤에도 w 가 1로 남습니다. 1로 나누면 값이 바뀌지 않습니다. 원근 나누기를 거쳐도 점은 움직이지 않습니다.
원근 투영 행렬은 맨 아랫줄이 다릅니다. 앞뒤 거리를 w 에 옮겨 담도록 짜여 있습니다. 그러면 원근 나누기가 먼 점일수록 큰 수로 나눕니다. 그 결과 먼 것이 작게 그려집니다.
두 투영은 같은 단계를 지나갑니다. 그 단계에서 무엇이 일어나는지를 맨 아랫줄이 정합니다.
장면의 도형은 꼭짓점을 이어 만듭니다. 지금까지 말한 점이 바로 이 꼭짓점입니다. 투영 행렬은 보통 꼭짓점마다 위치를 계산하는 작은 프로그램인 정점 셰이더 안에서 곱합니다. 「정점」은 꼭짓점을 뜻합니다.
아래 그림은 뷰 공간의 점이 창의 픽셀에 닿기까지 지나는 단계입니다.
flowchart TD
A["뷰 공간 · 카메라 기준 좌표"] --> B["투영 행렬 곱하기 · 정점 셰이더 안"]
B --> C["클립 공간 · 상자 밖을 잘라 냄"]
C --> D["원근 나누기 · 직교 투영은 w 가 1 이라 값이 안 바뀜"]
D --> E["정규화 장치 좌표 · -1 에서 1 사이 상자"]
E --> F["뷰포트 변환 · 창의 픽셀 위치로"]
고르게 매겨지는 깊이 값
직교 투영은 앞뒤 축도 늘이고 옮기기만 합니다. 그래서 옮겨 간 앞뒤 값은 카메라에서 떨어진 거리의 일차식이 됩니다. 거리가 같은 만큼 늘면 앞뒤 값도 같은 만큼 바뀝니다. 이 성질이 깊이를 비교하는 단계에서 차이를 만듭니다.
앞뒤 값은 깊이 버퍼에 적힙니다. 깊이 버퍼는 화면 픽셀마다 지금 그려진 것이 얼마나 먼지를 적어 두는 메모리입니다.
새 물체를 그릴 때는 그 픽셀에 적힌 값과 견줍니다. 새로 그릴 것이 더 가까울 때만 덮어씁니다. 이 검사가 깊이 테스트입니다. 그래서 나중에 그린 먼 물체가 먼저 그린 가까운 물체를 가리지 않습니다.
직교 투영에서는 근평면과 원평면 사이를 같은 간격으로 나눠 깊이 값을 매깁니다. 가까운 곳이나 먼 곳이나 두 물체를 구별하는 촘촘함이 같습니다.
원근 투영에서는 깊이 값이 거리의 역수에 가까운 꼴로 바뀝니다. 값이 가까운 쪽에 몰리고 먼 쪽은 성깁니다. 그래서 원근 투영에서는 먼 곳의 두 면이 같은 깊이 값을 받아 번갈아 비치는 Z 파이팅이 잘 일어납니다.
쓰는 곳
직교 투영은 크기나 길이가 그림의 정보인 곳에 씁니다. 대표적인 네 경우는 아래 표와 같습니다.
| 쓰는 곳 | 직교 투영을 쓰는 까닭 |
|---|---|
| 설계 도면의 정면도·평면도·측면도 | 도면 위의 길이를 재서 실제 길이로 읽어야 합니다 |
| 2D 게임과 메뉴·버튼 같은 화면 요소 | 상자를 창 크기에 맞추면 위치를 픽셀 단위로 적을 수 있습니다 |
| 위에서 비스듬히 내려다보는 시점의 게임 | 먼 바닥 칸도 가까운 칸과 같은 크기라서 격자가 반듯합니다 |
| 햇빛처럼 한 방향에서 오는 빛의 그림자 | 빛줄기가 나란하므로 그림자를 구하는 그림도 나란한 선으로 옮깁니다 |
둘째 줄은 상자의 가로 범위를 0부터 창 너비까지, 세로 범위를 0부터 창 높이까지로 잡는 경우입니다. 창이 800×600 픽셀이면 좌표 (400, 300)이 창 한가운데로 갑니다. 좌표 1이 픽셀 1과 맞으므로 화면 요소를 픽셀 위치로 바로 놓을 수 있습니다.
셋째 줄의 시점은 카메라를 세 축에 같은 각도로 기울여 놓고 직교 투영으로 그린 것입니다. 이렇게 그린 그림이 등각 투영입니다.
등각 투영에서는 세 축 방향의 길이가 모두 조금씩 짧게 그려집니다. 거리 때문이 아니라 카메라가 기울어 세 축이 화면에 비스듬히 놓였기 때문입니다. 기운 각도가 셋 다 같아서 줄어드는 비율도 같습니다. 그래서 가로·세로·높이를 한 잣대로 잴 수 있습니다.
넷째 줄의 빛은 방향광입니다. 해처럼 아주 멀리서 한 방향으로 오는 빛입니다. 멀리서 오므로 빛줄기가 모두 나란합니다.
그림자를 그리려면 먼저 빛 쪽에서 장면을 한 번 그립니다. 픽셀마다 빛이 닿는 가장 가까운 거리를 적어 둡니다. 이 그림이 그림자 맵입니다. 나중에 화면을 그릴 때 이 거리보다 먼 점은 그림자 속에 있다고 봅니다.
방향광의 빛줄기는 나란하므로 그림자 맵을 직교 투영으로 그립니다. 전등처럼 한 점에서 퍼지는 빛은 원근 투영을 씁니다.
깊이감을 잃는 대가
직교 투영 그림에서는 크기로 거리를 읽을 수 없습니다. 사람은 같은 물건이 작게 보이면 멀다고 느낍니다. 그 단서가 사라지므로 무엇이 앞에 있는지 가리기 어려워집니다.
카메라를 앞으로 옮겨도 물체가 커지지 않습니다. x' 을 구하는 식에 앞뒤 거리가 없기 때문입니다. 화면을 확대하려면 카메라를 옮기지 않고 뷰 볼륨 상자의 가로·세로 폭을 좁힙니다.
상자의 가로세로 비율이 창의 가로세로 비율과 다르면 그림이 한쪽으로 늘어납니다. 그래서 창 크기가 바뀌면 상자의 폭도 같은 비율로 고칩니다.
선형대수의 직교 투영
선형대수에서 직교 투영은 점을 평면이나 직선에 수직으로 내리는 계산입니다. 점에서 평면으로 수직인 선을 긋습니다. 그 선이 평면과 만나는 점이 답입니다. 이렇게 얻은 점은 평면 위에서 원래 점과 가장 가까운 점입니다.
그래픽스의 직교 투영도 뿌리가 같습니다. 화면을 평면으로 보고 점을 수직으로 내리면 앞뒤 성분이 떨어져 나가고 가로·세로 성분만 남습니다. 「상자를 약속된 상자로 옮기는 계산」 소절의 식에서 x' 에 앞뒤 거리가 없던 것이 이 때문입니다.
그래픽스는 떨어져 나갈 앞뒤 성분을 버리지 않고 깊이 값으로 남겨 둡니다. 깊이 테스트에 써야 하기 때문입니다. 여기에 뷰 볼륨을 약속된 상자로 늘이고 옮기는 계산을 얹은 것이 그래픽스의 직교 투영 행렬입니다.
관련 항목
직교 투영이 속하는 상위 분류
투영 · 평행 투영 · 좌표 변환 · 그래픽스 · 렌더링
직교 투영과 견주는 투영 방식
원근 투영 · 등각 투영 · 축측 투영 · 사투영 · 소실점
직교 투영 앞뒤로 좌표가 지나가는 처리 단계
그래픽스 파이프라인 · 정점 셰이더 · 클리핑 · 원근 나누기 · 뷰포트 변환 · 래스터화
투영 전후로 좌표가 거치는 좌표계
모델 공간 · 월드 공간 · 뷰 공간 · 클립 공간 · 정규화 장치 좌표 · 화면 좌표
직교 투영을 적는 수학 도구
행렬 · 투영 행렬 · 변환 행렬 · 동차 좌표 · 아핀 변환 · 선형대수
직교 투영의 보이는 범위를 정하는 값
뷰 볼륨 · 근평면 · 원평면 · 뷰 절두체 · 화면비 · 카메라
직교 투영이 넘긴 깊이 값을 쓰는 검사와 저장소
깊이 버퍼 · 깊이 테스트 · Z 파이팅 · 깊이 범위
직교 투영으로 그리는 화면과 도면
설계 도면 · CAD · 2D 게임 · 그림자 맵 · 방향광 · 사용자 인터페이스
직교 투영 행렬을 만들어 쓰는 그래픽스 API
다른 이름: orthographic projection · 정투영