momodudu.zip

#5 Vertex Processing - 변환 매트릭스 본문

Graphics/OpenGL

#5 Vertex Processing - 변환 매트릭스

ally10 2019. 9. 25. 16:16

행렬 관련 포스팅은 전에도 glm 라이브러리를 쓰면서 간단하게 썼었는데, 변환 과정은 봐도봐도 부족하니까 오늘은 좀 더 응용단계? 그래픽스 이론 관점에서 보는 변환 행렬이다. 우리가 흔히 말하는 mvp매트릭스. 도대체 변환이 왜 이루어져야하고, 어떻게 이루어지는지에 대한 포스팅이다. 처음 그래픽스를 배울때 기초 이론이 없어서, 이걸 써야한다는건 알고 있었는데 도대체 왜쓰며, 무슨 원리인지에 대해 몇년동안 제대로 알지 못하고 썼다. 변환행렬 관련해서는 찾아봐도 별로 참고할만한 자료가 안나와서....(내가 못찾은걸수도) 직접 공부했던 내용들을 정리하면서 다시 써보고자 한다.

 

사실 OpenGL ES보다는 그래픽스 이론에 가까운 포스팅이다...

해당 포스팅은 <게임 프로그래밍을 위한 3차원 그래픽스> 라는 책을 많이 참고했다.

 

일단, transformation 과정 설명에 앞서서 vertex shader에 대한 설명을 짤막하게 하자면,

vertex shader는 "각 정점별로 수행되는 프로그램" 이라고 볼 수 있다. 그래픽스 파이프라인에서 첫 다리 역할을 한다.

즉 우리가 OpenGL을 처음 배우기 시작할 때 그리는 삼각형은 3개의 정점으로 이루어질텐데 vertex shader에서 이 3개의 정점마다 "vertex shader"라는 프로그램을 수행한다. 즉, input이 각 1개의 정점이 들어가고 output으로 어떤 값이 나온다.

 

그러면 도대체 vertex shader라는 놈이 하는게 뭔데?

위와 같다. vertex shader에서 "변환 행렬"이라는 변수가 위 일련의 과정을 다 수행시켜준다.

그래픽스에서 행렬은 굉~~장히 중요하다. 그래픽스 자체가 여러 수학이론에 근간을 두고있지만, 행렬은 기초중에 기초이자 정말 확실하게 짚고 넘어가야 하는 부분이다.

 

즉, MVP(Model-View Projection) 행렬인 만큼, 총 3가지의 변환이 이루어지고 그 각각의 역할이 있다.

먼저, local space에서 그려진 object들을 한군데로 world space로 가져다 놓는 world transformation

그리고 이 world space를 어떻게 볼 것인지? 즉 camera(view)를 정의하는 view transformaton

마지막으로 3차원으로 이루어진 그 공간을 어떻게 2D화면으로 투영할것인지를 정의하는 projection transformation

 

1. World Transformation

조금 추상적으로 예를 들어서 2가지 Object를 그릴거라고 가정해보자. 내 방 안에 있는 컴퓨터와 내 집을 그릴거라고 생각해보자. 내 집은 바깥에서 봤을때의 좌표계이고, 내 컴퓨터는 내 방안에 있는 좌표계이다. 두 각각의 좌표계를 model좌표계,  object좌표계로 부른다. 즉, 두개의 좌표계가 다른데 어떻게해야 쉽게 한 공간에 그릴까? 이때 필요한게 world transformation이다.

 

내 방을 world 공간 좌표계로 갖다놓고, 내 컴퓨터도 world공간 좌표계로 갖다놓는다.

즉 두개는 같은 space를 갖게 되는것이다.

우리가 CPU단에서 정의한 정점들은 모두 Model Space에서 그렸고, 이것을 world space에 갖다놔야 그려질 것이다. 이 변환을 정의하는게 바로 world transformation이라고 볼 수 있다.

음.. 좀 더 구체적으로 예를들면, 

 

 

우리가 CPU에서 원통형의 vertex를 정의할때 점 P는 (0,1,0)으로 정의를 했지만, Wolrd space에서 보면 이 좌표는 P(0,1,0)이 아니다.  대충 어림잡아 P'(20,5,0)정도 된다고 보자... 실제로 P좌표를 그대로 사용한다면 우리가 그리고자하는 위치와 전혀 다른 위치에 그려진다. 이 계산을 우리가 일일히 다 계산해서 넣어줄 수 없으므로 사용하는게 바로 world transformation이다.

즉, object공간에 정의된 물체들을 모두 world 좌표계로 갖다놓는 변환이다.

 

world space에서 봤을 때 model space의 원점의 좌표는 (x1,y1)이라고 가정한다면, 모델 공간에서 그려진 원통을 가져오려면 그만큼 이동이 필요할것이다. 때에따라서 회전이나, 축소확대도 필요하다.

 

이는 이전 포스팅에서 다뤘던 translate/scale/rotate변환 등에 의해 이루어진다.

 

2. View transformation

이제 물체를 world좌표계로 가져다놓았으니, 카메라를 어떻게 정의할지가 필요하다. 위의 예제를 계속해서 쓰자면,

이제 카메라가 정의되었다. 1에서 이미 object를 월드 좌표계로 이전해놓았으니, 아까 P(0,1,0)은 이제 P'(20,5,0)으로 정의된다. 그리고 이 카메라가 P를 바라보고 있다. 이제 다시 공간 이전이 필요한데, 점 P는 월드 좌표계 시점으로 봤을때는 (20,5,0)이 맞다. 하지만 카메라 space에서 봤을땐?

 

카메라 space는 (u,v,n)총 3개의 축으로 이루어지는데 n의 경우 카메라 시점에서 카메라가 바라보는 시점까지의 벡터의 반대방향으로 정의되고, u는 카메라 시점 위 벡터인 up벡터와 n의 외적으로 구한다. v는 n과 u의 외적으로 구한다.

 

카메라의 시점은 world space로 봤을 때 (30,5,0)으로 정의되어있다. 따라서 카메라 시점에서 P'를 봤을때는 P'는 (20,5,0)이 아닌 P''(-10,0,0)으로 정의할 수 있겠다. 도대체 왜 카메라 입장에서 생각해야되느냐?하면 어차피 우리가 그릴건 카메라 시점이기 때문이다. 이렇게 world space에 정의되어있는 모든 object와, 그를 구성하는 정점들을 camera space로 또 다시 재정의하는것이 바로 View Transforamtion이다.

 

우리는 World좌표계 원점인 (0,0,0)에서 P'를 보고싶은게 아닌, 카메라 시점으로부터 P'를 보고싶은것이다.

 

 

<Space Change>

뷰 매트릭스를 통해서 카메라 시점을 world space로 재정의하는데, 이를 Space change(공간이전)라고 부른다. 이건 말은 어렵지만 개념적으로 생각해보면 쉽다.

바로 위의 그림에서, 카메라 시점을 world좌표계 원점으로 가져다 놓는다고 생각하면 된다. 단, Object는 카메라에 묶여있다고 생각하면 된다.

 

이렇게 묶여있는 카메라와 object를,

 

카메라 좌표계를 world 좌표계에 포개어 넣는다고 생각하면 된다. 그럼 점 P'는 다시 새로운 점 P''로 탄생할것이다.

도대체 왜 이런짓을 하냐 싶겠지만, 이게 제일 편한 방법이다. 수학을 좀 다뤄본 사람은 알겠지만 뭐든 원점으로 갖다놓으면 편하다. 일단 원점을 포개어놓는 변환은 앞서 다룬 translate과 똑같다. 그리고 지금 그림에서는 축이 거의 일치하지만, 카메라가 다양한 각도에서 보는게 가능하므로 각 카메라의 (u,v,n)기저를 x,y,z처럼 회전이 필요하다.

 

이렇게 완전히 포개어놓는 일련의 과정이 바로 Space Change이며, 앞서 다룬 포스팅(translate/rotate)으로 이루어진다. 축을 이동시키고, 각 축을 rotate시킨다고 보면 된다.rotate도 앞서 다룬것처럼 복잡한 식이 필요없다. 왜냐하면 카메라 축이었던 (u,v,n)기저는 모두 단위벡터였기 때문이다. 즉 해당 축 자체를 행렬로 표현이 가능하다.

 

그래서 Matrix(view)=Matrix(rotate)*Matrix(translate)으로 정의할 수 있는데. 아래와 같이 정의할 수 있다.

 

수식 쓰기가 귀찮아서..ㅎㅎ 예전에 공부해놓은 자료를 갖다붙였다. 어려워보이지만 이해하면 정말 별거 아니다.

Rotate행렬은 각 축만큼 회전하는거고, translate의 경우에는 카메라 좌표만큼 빼주는 평행이동을 하면 된다.

이 두개가 합쳐져서 아래와 같은 행렬이 나오게 되며, 이게 바로 space change다.

 

 

3. Projection transformation

이거는 조금 어렵다. 하지만 이해하면 쉽다. 원근법을 근사한 변환이라고 생각하면 된다. 우리는 3D space에 그렸지만, 화면은 평면이므로 2D에서 최대한 3D스럽게 표현하려면 원근법이 필요하다.

일단, 1-2의 과정을 거쳐서 space change가 이루어졌고 아래와 같이 카메라 가시 영역이 정의되었다고 가정하자.

파란색으로 칠해진 view frustum이 바로 가시영역이다. 이 view frustum을 정육면체(혹은 직육면체)로 변환하는게 바로 projection transfromation이다. 왜 육면체로 변환하냐면, 아래 그림을 보면 된다.

 

가까이 있는 물체가 더 크게 변환이 되면서, 원근법의 정의인 "앞에있는건 크게, 뒤에있는건 작게" 가 구현이 되는 것이다.

이게 바로 projection transformation 에서 이루어지는 변환이다. 이건 행렬은 귀찮아서 pass...찾아보면 많이 나온다. 위에 view frustum에서 정의된 aspect나 width, height, y-z사이의 시야각인 fovy를 이용해서 닮음비로 유도한다.

 

이 세가지 공간을 거치고나서야 비로소 vertex shader에서 ouput으로 clipping space로 결과값을 뱉는다. 이 clipping space는 다시 primitive processing stage에서 변환이 이루어지는데, 이건 나중에 다루기로 한다.. 지금은 vertex shader만 볼거니까.

 

vertex processing 끝!

기초 이론이지만 매우매우 중요한 부분이다.