2011년 2월 20일 일요일

백업] 네이버 32

출처 삽질하는 플머 | 오랑캐꽃
원문 http://blog.naver.com/oranke/40000788158
 뷰변환 행렬에 대해 살펴보기 전에 짚고 넘어가야 할 문제가 있습니다. OpenGL과 D3D를 말할 때 가장 큰 차이점으로 오른손 좌표계냐 왼 손 좌표계냐를 따집니다. 그리고 또 하나 얘기하는 것이 바로 행기준 행렬이냐 열기준 행렬이냐는 것이죠...  
 좌표계 문제는 Z좌표를 뒤집기만 하면 끝납니다. 앞에서 살펴봤던 회전행렬 역시 오른손 좌표계가 오른손 법칙으로 양의 방향을 규정하는 것 처럼 왼손 좌표계는 왼손이 감기는 방향을 양의 방향으로 하기 때문에 회전행렬을 그대로 쓸 수 있습니다. 양의 방향에 해당하는 좌표평면과 축을 2차원에 설정해 그려보면 금방 알 수 있습죠...

오른손 좌표계와 양의 회전방향
(오른손으로 Z축을 감아쥘 때 나머지 손가락들의 방향으로 회전)


왼손 좌표계의 양의 회전방향
(왼손으로 Z축을 감아쥘 때 나머지 손가락들의 방향으로 회전)

 
 때문에 기본적인 것만 명심하면 그다지 헷갈릴 이유가 없죠... 하지만 OpenGL에서 사용하는 열기준 행렬과 D3D의 행기준 행렬이란 녀석들은 확실하게 그 차이를 이해해 두지 않으면 두고 두고 뒤통수를 맞게 됩니다.
 참고로 양키들은 행기준 행렬(Row major matrix) 의 곱셈을 프리멀티플리케이션(Pre-multiplication), 열기준 행렬(Column major matrix) 의 곱셈을 포스트멀티플리케이션(Post-multiplication) 이라고 구분해서 쓰더군요.
 
 아무튼 우리가 일반적으로 사용하는 행기준 행렬은 아래처럼 정의되고,

  A(Row) = | a00 a01 a02 a03 |
           | a04 a05 a06 a07 |
           | a08 a09 a10 a11 |
           | a12 a13 a14 a15 |
 

 OpenGL 레퍼런스에 따르면 열기준 행렬은 다음과 같이 생겨먹었습니다.

  A(Col) = | a00 a04 a08 a12 |
           | a01 a05 a09 a13 |
           | a02 a06 a10 a14 |
           | a03 a07 a11 a15 |
 

 행기준 변환행렬 A에 의해 절점 P(x,y,z,w) 가 새로운 절점 P'(x',y',z',w') 로 변환하는 과정을 살펴보죠. 고등학교 때 배웠듯이 행렬의 곱셈이 성립하려면 행과 열의 갯수가 같아야 하므로 다음과 같이 적어줍니다. (행렬의 어떤 요소끼리 곱하는지 손가락으로 짚으면서 보세요.)
 
  | x' y' z' w' | =  
 
  | x  y  z  w  | × | a00 a01 a02 a03 |
                     | a04 a05 a06 a07 |
                     | a08 a09 a10 a11 |
                     | a12 a13 a14 a15 |
 
  x' = x*a00 + y*a04 + z*a08 + w*a12
  y' = x*a01 + y*a05 + z*a09 + w*a13
  z' = x*a02 + y*a06 + z*a10 + w*a14
  w' = x*a03 + y*a07 + z*a11 + w*a15

 원래 열기준 행렬은 절점, 또는 벡터를 표현하는 행렬을 세로, 즉 열로 표현하기 위해 사용합니다. 벡터 V(x,y,z,w) 에 변환행렬이 적용된 새로운 벡터 V'를 계산하는 과정을 열기준 행렬로 표현할 때는 다음과 같이 적어 줍니다.

  | x' |   | a00 a04 a08 a12 |    | x |
  | y' | = | a01 a05 a09 a13 | × | y |
  | z' |   | a02 a06 a10 a14 |    | z |
  | w' |   | a03 a07 a11 a15 |    | w |
 

 풀어보면 다음과 같습죠. (손가락을 짚으면서 보세요.)

  x' = a00*x + a04*y + a08*z + a12*w
  y' = a01*x + a05*y + a09*z + a13*w
  z' = a02*x + a06*y + a10*z + a14*w
  w' = a03*x + a07*y + a11*z + a15*w ... ①
 

 비교해 보나 마나 같은 결과 입니다. 엄밀히 말해 열기준 행렬열-행 순으로 곱하기 때문에 순서는 그대로 두고 다음과 같이 표현하는 것이 맞습니다. (손가락... 잊지 마시구..)

  | x' |   | x |    | a00 a04 a08 a12 |
  | y' | = | y | × | a01 a05 a09 a13 |
  | z' |   | z |    | a02 a06 a10 a14 |
  | w' |   | w |    | a03 a07 a11 a15 |
 
 
  x' = x*a00 + y*a04 + z*a08 + w*a12
  y' = x*a01 + y*a05 + z*a09 + w*a13
  z' = x*a02 + y*a06 + z*a10 + w*a14
  w' = x*a03 + y*a07 + z*a11 + w*a15 ... ②

 그러나 OpenGL에서는 ① 에서의 방법으로 행렬을 표시하고 곱해주네요. 이미 깨달으신 분도 계시겠지만, ② 의 방법처럼 열-행으로 곱해주는 것은 같은 내용을 행기준으로 표시하고 행-열 순으로 곱해주는 것과 같습니다. 이렇게 곱하는 방식이 행-렬이냐 열-행이냐에 따라 곱하는 순서가 바뀝니다. 중요하니까 꼭 기억하세요.
 
 행렬끼리의 곱셈도 살펴보죠.
 
 먼저 행기준 행렬의 곱셈을 풀어서 써 봅시다. 행-열 순으로 곱합니다. (마찬가지로 손가락으로 잘 짚으면서 보세요..)
 
  R(Row) = M(Row) * T(Row)  
 
  | r00 r01 r02 r03 |   | m00 m01 m02 m03 |    | t00 t01 t02 t03 |
  | r04 r05 r06 r07 | = | m04 m05 m06 m07 | × | t04 t05 t06 t07 |
  | r08 r09 r10 r11 |   | m08 m09 m10 m11 |    | t08 t09 t10 t11 |
  | r12 r13 r14 r15 |   | m12 m13 m14 m15 |    | t12 t13 t14 t15 |
 
  r00 = m00*t00 + m01*t04 + m02*t08 + m03*t12
  r01 = m00*t01 + m01*t05 + m02*t09 + m03*t13
  r02 = m00*t02 + m01*t06 + m02*t10 + m03*t14
  r03 = m00*t03 + m01*t07 + m02*t11 + m03*t15
  r04 = m04*t00 + m05*t04 + m06*t08 + m07*t12
  r05 = m04*t01 + m05*t05 + m06*t09 + m07*t13
  r06 = m04*t02 + m05*t06 + m06*t10 + m07*t14
  r07 = m04*t03 + m05*t07 + m06*t11 + m07*t15
  r08 = m08*t00 + m09*t04 + m10*t08 + m11*t12
  r09 = m08*t01 + m09*t05 + m10*t09 + m11*t13
  r10 = m08*t02 + m09*t06 + m10*t10 + m11*t14
  r11 = m08*t03 + m09*t07 + m10*t11 + m11*t15
  r12 = m12*t00 + m13*t04 + m14*t08 + m15*t12
  r13 = m12*t01 + m13*t05 + m14*t09 + m15*t13
  r14 = m12*t02 + m13*t06 + m14*t10 + m15*t14
  r15 = m12*t03 + m13*t07 + m14*t11 + m15*t15 ... ③

 다음에는 열기준 행렬의 곱셈을 보겠습니다. (손가락...)
 
  R(Col) = M(Col) * T(Col)   ....(나머지는 스스로..) .... ④
 
  | r00 r04 r08 r12 |   | m00 m04 m08 m12 |    | t00 t04 t08 t12 |
  | r01 r05 r09 r13 | = | m01 m05 m09 m13 | × | t01 t05 t09 t13 |
  | r02 r06 r10 r14 |   | m02 m06 m10 m14 |    | t02 t06 t10 t14 |
  | r03 r07 r11 r15 |   | m03 m07 m11 m15 |    | t03 t07 t11 t15 |
 
  r00 = m00*t00 + m04*t01 + m08*t02 + m12*t03
  r01 = m01*t00 + m05*t01 + m09*t02 + m13*t03
  r02 = m02*t00 + m06*t01 + m10*t02 + m14*t03
  r03 = m03*t00 + m07*t01 + m11*t02 + m15*t03
 
 열기준 행렬행기준 행렬의 곱셈은 ③과 ④에서 보듯 전혀 다른 결과를 냅니다. 이번에는 열기준 행렬의 곱셈 순서를 바꿔보죠.
 
  R(Col) = T(Col) * M(Col) .... (나머지는 스스로..) .... ⑤
 
  | r00 r04 r08 r12 |   | t00 t04 t08 t12 |    | m00 m04 m08 m12 |
  | r01 r05 r09 r13 | = | t01 t05 t09 t13 | × | m01 m05 m09 m13 |
  | r02 r06 r10 r14 |   | t02 t06 t10 t14 |    | m02 m06 m10 m14 |
  | r03 r07 r11 r15 |   | t03 t07 t11 t15 |    | m03 m07 m11 m15 |
 
  r00 = t00*m00 + t04*m01 + t08*m02 + t12*m03
  r01 = t01*m00 + t05*m01 + t09*m02 + t13*m03
  r02 = t02*m00 + t06*m01 + t10*m02 + t14*m03
  r03 = t03*m00 + t07*m01 + t11*m02 + t15*m03
 
 위에서 보듯이 행기준 행렬로 계산한 ③과 순서를 바꾼 열기준 행렬 ⑤의 결과는 같습니다. 즉 열기준 행렬 M과 T를 곱한 결과 R은 이것을 행기준으로 생각해 순서를 바뀌 계산한 것과 같다는 이야기 입죠...
 
 실제 OpenGL의 행렬연산이 어떤 방식으로 동작하는지 살펴 봅시다. OpenGL에서 행렬은 행렬 스택에 보관되며 glRotate, glTranslate, glMultMatrix 등의 함수에 의해 기존 행렬과 조합되죠...
 
 먼저 다음과 같이 벡터 및 행렬 자료형을 만들고

  type
    TVector3f = array [0..2] of Single;
    TVector4f = array [0..3] of Single;
    TMatrix3f = array [0..2] of TVector3f;
    TMatrix4f = array [0..3] of TVector4f;
 
 
  var
    M1, M2 : TMatrix4f;

 이동 후 회전을 시켜봅시다. 모델뷰 행렬 스텍을 초기화 하고...

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
 

 (0, 0, -3) 만큼 이동하는 행렬을 조합해 결과를 M2에 저장합니다.

  glTranslatef(0, 0, -3);
  glGetFloatv(GL_MODELVIEW_MATRIX, @M1);
 

 다시 X축으로 30도 회전시킨 행렬을 조합해 결과를 M2에 저장합니다.

  glRotatef(30, 1, 0, 0);
  glGetFloatv(GL_MODELVIEW_MATRIX, @M2);
 

 M1에 들어있는 행렬은 다음과 같습니다. (열기준)

  |  1  0  0  0  |
  |  0  1  0  0  |
  |  0  0  1 -3  |
  |  0  0  0  1  |
 

 여기에 다음과 같은 회전행렬을 조합했더니

  |  1  0     0    0  |
  |  0  0.87 -0.5  0  |
  |  0  0.5   0.87 0  |
  |  0  0     0    1  |
 

 M2와 같은 결과가 나왔네요...

  |  1  0     0     0  |
  |  0  0.87 -0.5   0  |
  |  0  0.5   0.87 -3  |
  |  0  0     0     1  |
 

 위의 결과는 ④에서 살펴본 계산과 같습니다.

|  1  0  0  0  |    |  1  0     0    0  |   |  1  0     0     0  |
|  0  1  0  0  | × |  0  0.87 -0.5  0  | = |  0  0.87 -0.5   0  |
|  0  0  1 -3  |    |  0  0.5   0.87 0  |   |  0  0.5   0.87 -3  |
|  0  0  0  1  |    |  0  0     0    1  |   |  0  0     0     1  |
 

 또한 이것은 행렬의 원소를 행기준으로 놓고 순서를 바꾸어 곱한 결과와 같구요.

|  1  0    0    0  |    |  1  0  0  0  |   |  1   0     0    0  |
|  0  0.87 0.5  0  | × |  0  1  0  0  | = |  0   0.87  0.5  0  |
|  0 -0.5  0.87 0  |    |  0  0  1  0  |   |  0  -0.5   0.87 0  |
|  0  0    0    1  |    |  0  0 -3  1  |   |  0   0    -3    1  |
 

 따라서 열기준 행렬의 경우 이동 후 회전 순서라면, 행기준 행렬은 반대로 회전 후 이동 순서로 조합해야 원하는 결과를 얻게 됩니다.
 
 정육면체를 떠올려 볼까요? 육면체의 중심은 원점에 있고 물체를 바라보는 눈 또한 처음에는 원점에 있다고 생각합시다. 이제 눈은 고정하고 물체를 적당히 회전합니다. 그리고 물체를 -Z방향으로 떨어뜨려 보세요. 회전한 물체가 눈에 보일 껍니다.
 이번에는 시점을 물체로부터 떨어뜨리고 다시 시점을 원점을 축으로 물체에 주었던 회전과 반대로 회전시킨다고 생각 합시다. 결과는 마찬가지로 위와 같은 물체가 눈에 보이겠죠.
 전자는 행기준으로 생각한 것이고 후자는 열기준으로 생각한 것 입니다. 즉 OpenGL에서 행렬스택에 새로운 행렬을 곱하는 경우 우리가 흔히 생각하는 모델의 변환 순서의 반대 순서로 곱해주어야 한다는 거죠... 흔히들 이런 것 때문에 OpenGL과 D3D는 행렬을 곱하는 순서가 반대다 어쩐다 얘기합니다. 제 경우 처음 3D 공부를 하면서 이 문제 때문에 고민도 많았고 여기에 정리 하는 지금도 무지하게 헷갈리고 있지만, 풀어서 차근 차근 계산해 보니 결국 같은 이야기더군요... 물체 입장에서 생각하는 행렬의 조합을 OpenGL의 행렬스택에서 반대로 곱하는 예는 나중에 만날 3인칭 뷰 행렬 만들기에서 다시 살펴보도록 하죠.
 
 개인적으로는 행기준 행렬을 선호하는데, 이유는 행렬을 곱할 때 조합 순서를 물체 기준으로 생각할 수 있기 때문입니다. 게다가 3D프로그래밍에서 좌표변환이란 결국 뷰 볼륨으로 정규화하는 과정이기 때문에 뷰 좌표계가 고정되어있다고 생각하는 것이 행렬을 만들기 편하걸랑요. 열기준 행렬을 사용해 시점기준으로 생각하려면 시점을 옮기는 값들의 역수로 물체를 옮겨주어야 하는 등 많이 복잡하기도 하구요...
 
 물론 시점을 변환하는 것이 생각하기 편한 사람에겐 열기준 행렬이 적합할 수 있으니, 선택은 어디까지나 개인의 몫 같네요.
 
 사설이 길었는데... 아무튼 이런 이유로 제가 설명하는 좌표계는 특별한 언급이 없는 한 오른손 좌표계이며 행기준 행렬을 사용합니다.
 
 그럼...



덤.
 학교다닐 때 공부를 안했더니 기초가 너무 없습니다. 찍어 먹어 보고야 똥인지 된장인지 구분하는 상태라... 맛이나 제대로 봤는지 모르겠네요. 쥐뿔도 없는 주제에 굳이 이런 글들을 올리는 이유는 제가 아는 것이 정말 제대로 아는 것인지 알고 싶기 때문입죠. 모쪼록 실력있는 분들의 날카로운 지적을 바랍니다... ㅠㅠ;; 

댓글 없음:

댓글 쓰기

국정원의 댓글 공작을 지탄합니다.

UPBIT is a South Korean company, and people died of suicide cause of coin investment.

 UPBIT is a South Korean company, and people died of suicide cause of coin. The company helps the people who control the market price manipu...