3D 수학과 소프트웨어 래스터라이저 · 모델·뷰·투영과 원근 나눗셈 · 실습
정육면체를 화면에 세운다
목표
카메라 행렬과 투영 행렬을 직접 만들고, 원근 나눗셈과 뷰포트 변환을 거쳐 정육면체를 화면에 세웁니다. 이 실습이 끝나면 "정점 셰이더가 하는 일" 을 한 줄로 설명할 수 있게 됩니다.
왜 중요한가
3차원 점 하나가 픽셀 좌표가 되기까지 네 개의 좌표계를 지납니다. 모델, 월드, 뷰, 클립입니다. 각 단계가 행렬 하나라서 미리 곱해 MVP 로 합칠 수 있고, 그래서 GPU 는 정점마다 곱셈 한 번만 합니다.
여기서 가장 중요한 것은 원근감이 행렬이 아니라 나눗셈에서 나온다는 사실입니다. 행렬 곱은 선형 연산이라 나눗셈을 표현할 수 없습니다. 그래서 투영 행렬은 마지막 행을 (0,0,-1,0) 으로 두어 결과의 w 에 카메라로부터의 거리를 실어 두고, 그다음에 x·y·z 를 그 w 로 나눕니다. 이 우회로를 이해하면 깊이 버퍼의 정밀도가 왜 가까운 곳에 몰리는지, near 평면을 왜 함부로 줄이면 안 되는지도 같이 풀립니다.
단계
1. /root/pipeline 에 도구 상자 gfxlib.py 와 mat3d.py 를 놓습니다.
2. /root/pipeline/view.py 에 look_at 을 만듭니다.
3. /root/pipeline/proj.py 에 perspective 를 만듭니다.
4. /root/pipeline/screen.py 에 to_screen 을 만들고 /root/pipeline/out/04-divide.txt 를 적습니다.
5. /root/pipeline/out/cube.png 에 정육면체 와이어프레임을 그립니다.
6. orthographic 을 더해 /root/pipeline/out/ortho.png 를 만듭니다.
7. 시야각 셋으로 그려 /root/pipeline/out/07-fov.txt 를 적습니다.
참고
- 그림은
nohup python3 -m http.server 8080 -d /root/pipeline/out &로 띄운 뒤 웹 미리보기 에서http://localhost:8080/을 열어 봅니다. cube.png 와 ortho.png 를 나란히 보면 차이가 분명합니다. - 흔한 실수 하나: 투영 행렬의
m[3][3]을 1 로 두는 것입니다. 그러면 w 가 거리와 무관해져 원근이 사라집니다. - 흔한 실수 둘:
look_at의 세 번째 행에 마이너스를 빼먹는 것입니다. 그러면 카메라가 반대를 보게 되어 화면이 텅 빕니다.
단계 7개
- 그리기 도구 상자를 놓는다
- 카메라를 원점으로 끌어온다
- 절두체를 정육면체로 누른다
- w 로 나누고 화면 좌표로
- 정육면체의 열두 모서리
- 직교 투영과 견주어 본다
- 시야각을 바꾸면 무엇이 달라지나