2015. 2. 8.

[OpenGL]CTM

 그래픽스에서 모델에 변환 행렬을 곱해서 모델을 이동시키거나, 확대 및 축소시키거나, 회전시킨다. 이동, 확대 및 축소, 회전 등은 모두 행렬으로 이루어진다. 이런 행렬들은 누적되어 유지된다. 그리고 곱한 행렬들은 스택에 push하거나 pop할 수 있다.


CTM(Current Transformation Matrix)

 어떤 기하물체 A가 있다고 가정하자. A는 현재 모델 좌표계(Local Coordinate)로 정의되어 있다. 이를 월드 좌표계로 변환하기 위해서는 이동 행렬(Translation Matrix)를 모델에 곱해주면 된다. 그리고 A의 크기를 두 배로 만들기 위해서 크기 변환 행렬(Scale Matrix)를 곱해주면 된다. 지금 상태에서 물체 A에 가해진 변환은 이동 행렬과 크기 변환 행렬이다. OpenGL은 현재 변환 상태를 보관한다. 즉 지금까지 변환 행렬들을 누적해서 곱한 복합 행렬이 바로 현재 변환 행렬(CTM)이 된다. CTM을 그림으로 살펴보자.

 위 그림에서 모델 A는 이동 변환, 회전 변환, 이동 변환이 차례대로 가해진 상태이다. 이 변환 행렬들을 곱한 결과가 바로 CTM으로 저장된다. 결과적으로 모델 A에 CTM만 곱해주면 위의 세 가지 행렬을 차례로 곱한 결과와 동일한 결과를 얻을 수 있다. CTM을 이용하면 현재까지 변환된 변환 결과를 유지할 수 있다는 점이다. 만약 모델 A로부터 x축으로 10만큼 이동한 모델 B를 구현하려면 어떻게 해야할까? 모델 A에 곱하진 변환 행렬들을 다 곱한 후에 translate(10, 0)을 해주면 된다. 하지만 이는 비효율적이다. 모델 A에 곱해진 행렬들의 결과는 이미 CTM에 저장되어 있기 때문이다. CTM에 translate(10, 0)을 곱해준 결과가 모델 B의 위치가 된다. CTM은 이런 장점을 가지고 있다.


CTM의 한계

 CTM을 사용하면 현재까지의 변환을 저장할 수 있다. 그렇다면 CTM 이전의 행렬은 어떻게 알 수 있을까? 가장 최근에 CTM에 곱해진 행렬의 역행렬을 CTM에 곱하면 직전 단계의 CTM이 된다. 특정 시점의 CTM을 알기 위해서는 그 시점 이후에 곱해진 행렬들을 알아야 한다. 그리고 그 행렬들의 복합 행렬의 역행렬을 CTM에 곱해주어야 한다. 이는 매우 불편한 작업이다. 특정 시점의 변환 행렬들을 모두 기억하고 관리하는 것은 어렵다. CTM는 현재 상태의 변환에 대한 복합 행렬만을 보관할 수 있다는 한계가 있다. 이런 한계를 극복하기 위해서 필요한 것이 바로 특정 시점의 복합 행렬을 보관하기 위한 자료구조이다. OpenGl에서는 스택(stack)을 사용한다. 자신이 저장하고 싶은 시점의 복합 행렬을 glPushMatrix() 함수를 통해 스택에 저장한다. 그래고 glPopMatrix() 함수를 사용해서 변환 행렬을 보관하는 스택에 가장 위에 저장된 행렬을 반환한다. 

 자신이 지정한 특정 시점의 변환 행렬을 glPushMatrix() 함수로 저장하고, glPopMatrix() 함수로 접근해서 사용하면 된다.

 현재 변환 행렬을 위한 스택에 glPushMatrix() 함수를 사용해서 M1을 저장하였다. M1행렬은 CTM에 저장되어 있는 상태이다. 이후 glTranslate(), glRotate(), glScale() 함수 등을 사용하여 CTM에 추가적으로 변환이 가해졌다. 그리고 이를 glPushMatrix()를 통해 스택에 저장하였다. 이 상태는 그림의 M2이다. 이후 glPopMatrix() 함수를 사용하여 최상위의 M2를 pop하여 CTM을 M1으로 변경하였다.
 실행결과는 다음과 같다.


 위의 코드는 흰색, 노란색, 녹색, 빨간색 순서로 glPushMatrix() 함수를 사용하였다. glTranslate() 함수의 변환 결과가 스택에 누적되었다. 스택에 행렬들이 어떻게 누적되어서 렌더링되는지 생각해보자. 그리고 생각의 결과가 렌더링 결과와 일치하는지 살펴보자. 그리고 녹색 원의 아래쪽에 파란색원을 추가하고 다른 원들의 위치를 현재와 같은 상태를 유지하기 위해서 어떻게 해야할까? 코드를 직접 수정해보자.
 우리가 만들어야 하는 결과는 다음과 같다.


 녹색 원까지의 변환을 glPushMatrix() 함수를 통해 스택에 저장하였다. 그리고 녹색 원의 y축 방향으로 아래로 파란색 원을 추가하였다. 하지만 우리가 원하는 형태는 녹색 원 오른쪽에 빨간 원이 있는 모습이다. 그렇게 하기 위해서는 녹색 원이 추가된 상태의 변환 행렬로 돌아가야 한다. 하지만 현재 스택 최상위에 있는 변환 행렬은 파란 원을 추가했을 때의 CTM이다. 그래서 glPoPMatrix()를 호출하여야 한다. 그 후 빨간색 원을 추가하면 녹색원을 중심으로 우측에 생성되게 된다.


정리하면

  • OpenGL에서 현재까지의 변환은 CTM이라는 행렬에 저장된다.
  • CTM의 한계를 극복하기 위해서 CTM을 보관하기 위한 스택이 사용된다.
  • 변환 행렬을 스택으로 관리하기 위해 사용되는 함수는 glPushMatrix(), glPopMatrix() 등이 있다.

댓글 없음:

댓글 쓰기