빠른 흐름
view space
-> projection
-> clip space
-> clipping
-> perspective divide
-> NDC
-> viewport transform
-> screen/depth| 단계 | 의미 |
|---|---|
| Clip space | projection 이후 x, y, z, w로 표현된 좌표 |
| Clipping | 절두체 밖 primitive를 잘라내거나 제거 |
| Perspective divide | x/w, y/w, z/w로 NDC 생성 |
| NDC | 정규화된 장치 좌표 |
| Viewport transform | NDC를 픽셀 좌표와 depth range로 변환 |
구조
projection matrix는 view space 좌표를 곧바로 화면 픽셀로 보내지 않습니다. 먼저 clip space를 만들고, GPU는 이 좌표를 기준으로 카메라 절두체 밖에 있는 primitive를 처리합니다.
clip position = projection * viewPosition
ndc position = clip.xyz / clip.wclip space에서 중요한 값은 w입니다. perspective projection에서는 멀리 있는 점일수록 w가 달라지고, perspective divide를 거치면서 원근감이 생깁니다.
NDC는 API마다 세부 범위가 다를 수 있습니다. 예를 들어 x, y는 보통 -1..1 범위를 쓰지만, depth의 NDC 범위나 화면 좌표의 원점 방향은 API와 엔진 설정에 따라 다르게 보일 수 있습니다.
Viewport
viewport transform은 NDC를 실제 render target의 픽셀 좌표로 바꿉니다. 같은 NDC라도 viewport 크기, offset, depth range가 다르면 다른 화면 위치와 depth 값으로 기록됩니다.
문제 추적
| 증상 | 먼저 볼 곳 |
|---|---|
| 물체가 화면 밖으로 사라짐 | clip space, near/far plane |
| 원근감이 이상함 | projection matrix, perspective divide |
| 화면 좌표가 뒤집힘 | viewport origin, Y 방향 |
| depth가 예상과 다름 | NDC depth range, depth buffer 설정 |
| 일부 삼각형이 잘림 | clipping plane, w 값 |
좌표 버그를 볼 때 object -> world -> view -> clip -> NDC -> screen을 한 번에 보지 말고 단계별로 값 범위를 확인해야 합니다.
주의할 점
clip space, NDC, screen space를 섞으면 디버깅이 매우 어려워집니다. shader에서 보는 gl_Position이나 clip position은 아직 픽셀 좌표가 아닙니다.
또 reversed-Z, infinite projection, API별 depth range를 쓰면 depth 값의 직관이 달라질 수 있습니다. 깊이 문제는 projection convention과 depth test 설정을 함께 확인해야 합니다.
참고 링크
1 sources