OpenGL Coordinate Systems (view, projection) 좌표계 (뷰, 투영) [Learn OpenGL 쉬운 번역]

728x90
반응형
※ 본 포스팅은 learnopengl.com을 번역 및 가감한 포스팅이며, learnopengl에서는 번역 작업 참여를 적극 장려하고 있습니다!
아래 링크에서 원문을 확인할 수 있습니다.

 

Coordinate Systems

저번 포스팅에서는 transformation matrix로 object를 변환하는 방법을 공부했습니다. opengl은 vertex shader 처리 후, 화면에 보여야 하는 vertex가 normalized device coordinates(NDC) 범위 안에 있게 만듭니다. vertex의 x,y,z 좌표가 -1.0~1.0 사이에 위치하지 않으면 화면에 보이지 않게 처리합니다. 우리는 공간 내에서 좌표를 지정하면 vertex shader에서는 NDC 범위로 변환시킵니다. 그걸 rasterizer에 전달해서 화면 pixel로 만듭니다.

 

좌표를 NDC로 변환시키는 과정은 그 단계가 있는데, object의 vertex를 여러 coordinate systems으로 순차적으로 변환하고 최종적으로는 NDC로 변환됩니다. 이렇게 중간 단계들의 coordinate systems로 변환하는 것은 특정 coordinate system에서 연산이 쉬워진다는 장점이 있습니다. coordinate systems는 다음과 같습니다.

 

  • Local space (or Object space)
  • World space
  • View space (or Eye space)
  • Clip space
  • Screen space

 

위 과정 모두 vertex가 fragment가 되기 전에 거치는 coordinate system들입니다.

이것들을 한 번에 이해하기는 어려울 수 있으니 하나하나 그림으로 보면서 이해해봅시다.

 

The global picture

coordinate를 한 공간에서 다음 coordinate 공간으로 변환하기 위해 여러 transformation matrix를 사용할 것입니다. 여기서 중요한 건 model, view, projection matrix입니다. vertex coordinate는 local coordinates에서 시작해서 world coordinates, view coordinates, clip cooridnates로 처리되고 마지막에 screen coordinates로 마무리됩니다. 이미지를 보면서 각 transformation을 이해해봅니다.

 

 

  1. local coordinates는 객체의 로컬 원점에 대한 객체의 좌표입니다. 이는 객체가 시작하는 좌표입니다.
  2. 다음 단계는 local coordinates를 world coordinates로 변환하는 것입니다. world-space coordinates 는 더 큰 세계에 대한 좌표로, 이 좌표는 world의 global origin 에 대해 상대적입니다. 다른 여러 객체들도 이 world의 origin에 상대적으로 배치됩니다.
  3. 다음으로, 월드 좌표를 view-space coordinates로 변환합니다. 이는 각 좌표가 카메라나 관찰자의 시점에서 보이는 방식으로 변환되는 것입니다.
  4. 좌표가 뷰 공간에 들어간 후에는 이를 clip coordinates로 projection하고자 합니다. clip coordinates는 -1.0과 1.0 범위로 처리되며, 어떤 정점이 화면에 나타날지 결정합니다. perspective projection을 사용하는 경우, clip-space coordinates로의 투영이 원근감을 추가할 수 있습니다.
  5. 마지막으로, 클립 좌표를 screen coordinates로 변환하는 과정을 진행합니다. 이 과정은 viewport transform이라고 하며, 좌표를 -1.0에서 1.0 범위에서 glViewport로 정의된 좌표 범위로 변환합니다. 그런 다음, 결과 좌표는 rasterizer로 전달되어 픽셀(Fragment)로 변환됩니다.

 

이제 각각의 공간들의 역할에 대해서 조금 감이 잡혔을 겁니다. 여러 다른 공간을 변환하면 각 coordinates system에서 처리하기 유리한 연산을 적용할 수 있습니다. object를 수정하기에는 local space가, 다른 object를 중심으로 현재 object를 연산하려면 world coordinates에서 처리하는게 효율적입니다.

한 번에 local space에서 clip space까지 transform하는 matrix를 정의할 수 있지만, 그렇게 하면 유연하게 처리하기 어렵겠죠.

 

Local space

local space는 object 고유의 coordinates space입니다. blender와 같은 모델링 툴에서 3D 모델을 작업하면 초기 origin 은 (0,0,0)입니다. 모델이 중심이 되는 coordinate system으로 vertex들은 local space에 있습니다.

 

이전에 사용한 container의 vertex는 -0.5~0.5 사이 좌표값을 가졌고, 원점은 0.0인데 이것이 local space입니다.

 

World space

일반적으로 각각의 object는 world 안에서 각기 다른 위치에 배치되어 있을 겁니다. object가 다 원점에서 겹쳐있지는 않겠죠. world space coordinates는 말 그대로 world 중심으로 object들이 상대적으로 배치된 좌표입니다. object는 local space에서 world space로 transform되고 이 과정은 model matrix를 통해 처리됩니다.

 

model matrix는 object를 world 안의 특정한 translation, rotation, scale로 transform 하는 matrix입니다. 이전에 container를 transform 하는데 사용된 matrix들이 model matrix라고 볼 수 있습니다. object의 local coordinates를 world 기준으로 transform 한 셈이죠.

 

View space

view space는 opengl에서는 camera로 여겨집니다. camera space이자 eye space라고도 불립니다. view space는 world-space coordinate를 사용자의 시야에 보여지는 coordinates로 변환한 것입니다. 즉 카메라 시점 공간이죠. 보통 translation과 rotation의 조합으로 이루어져 scene의 object들이 카메라 앞에 보여지게 합니다. 이 transform 들이 view matrix 안에 포함되어서 world coordinates를 view space로 변환하죠. view coordinates에 대해서는 이후 camera에 대해 다룰 때 자세히 공부할 겁니다.

 

Clip space

opengl에서는 vertex shader의 마지막으로 coordinates를 범위 내로 한정하려고 그 밖의 것들을 clipped 합니다. 밖의 좌표들은 버려지고 남은 것들만이 화면에 fragment로 남습니다.

 

눈에 보이는 전부를 -1.0 ~ 1.0 으로 지정하지는 않습니다. 보여질 coordinates 범위를 골라내서 그 부분만을 NDC(normalize device coordination)로 변환합니다.

 

vertex coordinates를 view space에서 clip space로 transform 하기 위해 projection matrix를 정의해서 사용합니다. 예를들면 projection matrix는 해당 공간에서 -1000 ~ 1000까지의 좌표 범위를 지정한다고 하면, 이만큼을 NDC로 변환해서 -1.0 ~ 1.0으로 만듭니다(중간에 perspective division이라는 단계도 있습니다). 범위 외의 좌표는 NDC로 변환되지 않고 clip되어 제거됩니다. 만약 (1250, 500, 750)이라는 좌표가 있으면 x값이 범위를 벗어났으니 clip되어버리겠죠. 

 

만약에 삼각형처럼 일부분만 clipping volume 안에 있다면, opengl에서는 삼각형을 범위 안에 맞춰 여러 삼각형으로 잘라서 재구성합니다.

 

projection matrix가 만든 시야 공간을 frustum이라고 합니다. 이 frustum 안의 있는 coordinates는 NDC로 변환되어 2D 화면으로 만드는 걸 projection이라고 합니다. 간단히 말해 3D를 2D로 투영하는 거죠.

 

clip space 변환 후 perspective division이라는 최종 연산이 수행되는데, 여기서 position vector x,y,z 를 같은 vector의 w 성분으로 나눕니다. 즉 perspective division은 4D clip coordinates를 3D NDC로 바꾸는 작업입니다. vertex shader의 마지막에서 자동으로 처리해줍니다.

 

이후 glViewport 설정을 통해 screen coordinates로 매핑되면 이제 좌표가 fragment로 변환되는 겁니다.

 

view coordinates를 clip coordinates로 변환하는 projection matrix는 크게 orthograhpic(직교) projection matrix와 perspective(원근) projection matrix 두 개로 나뉘어집니다.

 

Orthographic projection

 orthographic proejction matrix는 정육면체 모양의 clipping frustum입니다. frustum의 너비, 높이, 길이를 지정해서 만들면 내부의 coordinates를 NDC범위로 만듭니다. 중요한 것은 fustum이 모양이 정육면체라서 projection 결과가 실제 육안으로 보는 것처럼 원근감이 있지 않다는 것입니다.

frustum은 보여지는 영역에 대한 정의인데, 원래 width, height, near plane, far plane으로 구성되고 이외의 부분은 clipping됩니다. 또한 NDC도 변환시 w 성분에는 변화없이 1.0으로 유지되고 따라서 perspective disivion 효과가 없습니다.

 

orthographic projection matrix를 생성하려면 GLM의 glm::ortho를 사용합니다.

glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f);

매개변수는 순서대로 frustum의 왼쪽 좌표, 오른쪽 좌표, 아래쪽, 위쪽 좌표, near plane 거리, far plane 거리 입니다.

1~4 번 매개변수로 near/far plane의 크기를 결정하고, near/far plane의 거리를 통해 두 평면의 사이 frustum 공간이 얼마인지 알 수 있습니다. 이 범위 안의 것들을 NDC로 변환하겠죠?

 

orthographic으로 2D 화면으로 mapping한 결과는 원근감이 없기 때문에 생각보다 비현실적입니다. 그럼 이제 원근법을 고려한 방법을 알아봅시다.

 

Perspective projection

눈으로 볼 때 멀리있는 물체는 더 작게 보입니다. 이러한 효과를 perspective(원근감)이라고 하는데, 아래 그림과 같은 효과이죠.

원근감 있는 projection을 위해서 perspective projection matrix를 사용합니다. 이때 vertex coordinate의 w값을 조작해서 관찰자로부터 멀어지면 w성분이 커지도록 합니다. 좌표가 clip space으로 변환 될 때 -w ~ w 범위 내로 매핑합니다. 최종적으로는 NDC 즉 -1.0 ~ 1.0 사이에 coordinate가 있어야하므로 clip space에서 perspective division이 적용됩니다.

 

$$out = \left(\begin{array}{c} \frac{x}{w} \\ \frac{y}{w} \\ \frac{z}{w} \end{array}\right)$$

 

각 성분이 w 값으로 나누어지면서 vertex가 카메라에서 멀어질 수록 coordination이 작아지는 효과를 부여합니다. 이게 perspective projection 효과를 만듭니다. 

 

GLM에서는 다음과 같이 perspective projection matrix를 생성합니다.

glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f);

glm::perspective로 보이는 영역 frustum을 생성해서 나머지를 clip 합니다. perspective frustum은 측면이 사다리꼴인 모양의 상자로 범위를 정하고 이걸 clip space로 매핑하면서 원근감이 생깁니다. 

매개변수는 순서대로 fov(field of view), aspect ratio(화면비), near plane 거리, far plane 거리 입니다.

fov의 각도를 조정해서 view space의 크기를 조절하는데 보통은 45도가 일반적입니다. aspect ratio는 viewport의 너비를 높이로 나눈 비율입니다. 그리고 near plane과 far plane 거리의 사이에 있는 좌표들이 렌더링 됩니다.

 

near plane의 거리를 너무 멀리 설정하면 카메라에 가까운 object들이 몽땅 clipping 되어버리겠네요.

 

orthographic projection을 할 때는, perspective division의 효과가 없이 clip space에 매핑되었습니다. 원근감이 없이 출력되기 때문에 보통 건축이나 공학 프로그램에 많이 사용됩니다. 아무래도 object의 치수를 정확히 하기 위함이겠죠. blender 등 3D 모델링 툴에서도 두 projection 방식을 모두 지원합니다. 

perspective에서는 멀리있는 vertex는 작게 보이지만, orthographic에서는 다 동일한 거리를 유지하고 있죠.

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유