좌표 시스템 종류
- Local Space (Object Space)
- World Space
- View Space (Eye Space)
- Clip Space
- Screen Space
전체 그림
하나의 공간에서 다른 좌표 공간으로 변환하기 위해서는 변환행렬을 곱해주어야 한다.
- Local -> World Space 로 변환 시 Model matrix를 곱해줌
- World -> View Space 로 변환 시 View matrix를 곱해줌
- View -> Clip Space 로 변환 시 Projection matrix를 곱해줌
- Clip -> Screen Space 로 변환 시 Viewport Transform을 사용
위와 같이 여러 좌표 시스템을 사용하는 이유는 일부 연산들은 특정 좌표 시스템에서 수행되는 것이 더 쉽기 때문이다.
각 공간에 대한 정리
1. Local Space
- 오브젝트에 대한 좌표 공간으로 원점은 오브젝트의 중심점이 됨
- 블렌더 같은 모델링 소프트웨어에서 큐브를 만들면 원점이 물체의 중심에 있는 것처럼!
2. World Space
- 모든 버텍스들의 좌표를 world 기준으로 하는 것
- 오브젝트들이 월드의 원하는 위치에 각각 산재되어 배치되는 방법
- 로컬 좌표계에서 월드 좌표계로 변환할 때 모델 행렬을 썼었는데,
이 행렬은 오브젝트를 그들이 속한 위치/방향으로 월드에 배치하기 위해 오브젝트를 이동, 스케일, 회전하는 변환 행렬임
3. View Space
- 일반적으로 OpenGL에서의 카메라를 나타내며, Camera space, View space 라고도 불림
- 시점 좌표계는 월드 좌표계에 있던 오브젝트들을 사용자의 시점 앞에 있는 좌표로 변환했을 때의 결과임
- 월드 좌표계에서 시점 좌표계로 변환하기 위한 행렬은 시점 행렬인데,
특정 오브젝트의 위치를 카메라 앞으로 변환하기 위한 이동/회전 조합의 행렬을 나타냄
4. Clip Space
- 각 vertex shader 실행의 마지막에 OpenGL은 지정된 범위의 좌표를 받아들이고
이 범위에서 벗어난 모든 좌표는 clipped 됨
clip된 좌표들은 폐기되고 남은 좌표들은 최종적으로 fragment가 되어 화면에 보이게 됨
(기껏 다 계산했는데 거의 마지막 쯤 되서 clip된 좌표들은 버린다는 게.. 뭔가 계산한 게 아깝다?는 생각이 들긴 하네ㅠ)
- 버텍스 좌표를 시점 좌표에서 클립 좌표로 변환하기 위해, 좌표의 범위를 지정하는 행렬을 project matrix라고 함 (예를 들어 각 축에 대해 -1000 ~ 1000)
- 삼각형과 같은 primitive의 일부만 clipping volume에서 벗어났다면 OpenGL은 clipping 범위에 맞도록 하나 이상의 삼각형을 재구성함
- Projection 행렬이 생성하는 viewing box는 Frustum이라 불리고, frustum 안에 있는 좌표들은 화면에 나타남
- 지정된 범위에서 NDC(clip space)로 변환하는 전체적인 과정은 projection이라고 불림
- 모든 버텍스들이 clip space로 변환되었다면 perspective division 이라고 불리는 마지막 작업이 수행됨. 여기에서 위치 벡터의 x,y,z 요소들을 벡터의 w 요소로 나눔.
- perspective division은 4D clip space 좌표를 3D NDC로 변환하는 것이고, 이 단계는 vertex shader의 마지막에 자동으로 수행됨
이 이후에는 결과 좌표들을 Screen 좌표에 매핑하고, Fragment로 변환한다.
Projection 행렬은 View 좌표를 Clip 좌표로 변환하기 위해 두 개의 다른 형식을 받는데, 각 형식은 자신만의 고유한 frustum을 가진다.
두 개의 다른 형식은 다음과 같다.
- 정사영 행렬 (Orthographic)
- 원근 행렬 (Perspective)
이는 3D effects 를 줄 수 있는 방식이기도 하다.
Orthographic projection
- orthographic projection 행렬은 정육면체와 같은 frustum 상자를 정의
- 이 상자는 상자 밖의 버텍스들을 clip하는 cliping 공간을 정의함
- orthographic projection에서 행렬을 생성할 때 눈에 보이는 상자의 너비, 높이, 길이를 정의함
- orthographic projection 행렬로 변환이 완료된 후에 이 상자 안의 좌표들은 clip 되지 않음
위 그림에서 주목해야 할 요소들은 width, height, near plane, far plane 이다.
near plane 앞에 있는 모든 좌표들은 clip 되고, far plane 뒤에 있는 좌표들도 마찬가지이다.
정사영 행렬은 좌표들을 화면의 2D 평면에 똑바로 매핑하지만, 실제로 똑바로 투영하는 것은 비현실적이다.
원근감을 고려하지 않았기 때문이다.
이는 원근 행렬을 통해 해결할 수 있다!
Orthogonal vs Oblique
- 둘 다 Parallel Projection에 속한다
- 둘 다 View plane(Position/Orientation)이 필요하고, Projection angle을 정의해야 한다
- Orthogonal은 보고자 하는 것을 화면에 직교 투영
- Oblique는 투영면에 직교하지는 않는다
- architectural drawing에 주로 사용되며, 정면만 보존하기 때문에 실제로는
정면:측면이 1:1이어도 보일 땐 아닐 수 있다
Perspective projection
실제 세상에서는 멀리있는 오브젝트는 작아져 보인다.
위 사진과 같이 멀리있는 오브젝트는 작게 보이고, 저 멀리에서 선이 한 점에서 만나는 것을 원근법이라고 한다.
Perspective projection 을 하면 위 사진과 같은 효과를 만들 수 있고, 정사영 투영보다 더 현실적이다!
- Shades, Vanishing points, Shadows, Size 를 정의해야 함
- Pin-hole camera model처럼 계산해야 한다고 함
- 원근 투영 행렬은 주어진 오브젝트를 clip된 공간에 매핑할 뿐 아니라 각 버텍스의 w값을 조작함
- 시점으로부터 버텍스 좌표가 멀어질 수록 w요소가 증가함
- 좌표들이 clip space로 변환되고 나면 그들은 '-w ~ w' 범위 안에 있게 되고, 이 범위 밖에 있는 모든 것들은 clip 됨
버텍스의 각 요소들은 w 요소로 나누어지는데, 시점에서 멀리 떨어진 버텍스에게 작은 버텍스 좌표를 주어진다.
위에 정사영 투영은 원근감을 따지지 않아서 w 요소가 그냥 1이었는데,
원근 투영에서는 좌표가 멀어지면 더 큰 w 요소를 주어서 perspective division 때 x,y,z 좌표를 w로 나누는 작업에 영향을 주는 것 같다.
(살짝 의문인 건 그럼 멀 수록 나누어지는 w값이 크니까 좌표의 값이 전체적으로 작아질텐데 그럼 더 가까워 보이게 되는 것 아닌가..?)
오른손 좌표계
- OpenGL은 오른손 좌표계를 사용한다.
- 오른손 좌표계에서는 각 축에 대한 양의 방향이 x축에서는 오른쪽, y축에서는 위쪽, z축에서는 뒤쪽을 향하는 것을 의미한다.
Z-buffer
- OpenGL은 z-buffer에 모든 깊이 정보들을 저장한다.
- 이 버퍼는 깊이 버퍼라고도 부르는데, 각 프래그먼트의 z값을 저장해두었다가 프래그먼트가 출력되길 원할 때마다
해당 깊이 값과 z-buffer의 깊이 값과 비교한다.
그리고 만약 현재 프래그먼트가 다른 프래그먼트의 뒤에 있다면 폐기하고, 앞에 있다면 덮어씌운다.
이 과정을 depth testing이라고 부르고 OpenGL에 의해 자동적으로 수행된다.
참고
https://heinleinsgame.tistory.com/11?category=757483