리듬게임을 만들어보자 4. 행렬변환을 통한 회전, 확대/축소

Posted by 적분 ∫2tdt=t²+c
2009.11.24 22:04 프로그래밍/스페샬
이번에는 스프라이트를 확대/축소하거나 회전해서 출력할 수 있게 해보자.

이런 까다로운 일들을 하기 위해선 '행렬'이라는 걸 알아야한다. 하지만 여기서 행렬에 대해서 자세히 설명할 시간은 없고, 3D프로그래밍을 할때는 행렬을 통해서 여러가지 변환을 한다는 사실만 알아두도록 하자.

D3D에서는 행렬을 위한 자료형으로 D3DXMATRIX타입을 제공한다.
그리고 편리한 행렬 계산을 위해 D3DXMatrix~~~ 시리즈 함수를 제공한다.
D3DXMATRIX 타입에 각종 연산자(대입, 곱하기, 더하기, 빼기 등등)가 정의되어 있으므로 연산도 편리하게 할 수 있다.

점 (x, y, z, 1)에다가 4차정사각행렬을 곱함으로써 행렬변환은 이루어진다. 점에다가 특정한 행렬을 곱하면 여러가지 효과를 줄 수 있다.

ID3DXSprite는 SetTransform 라는 변환행렬을 지정하는 함수를 제공한다.

자, 가상의 텍스쳐가 좌표계 위에 있다.
(화면 상의 좌표계는 수학과는 다르게 아래로 내려갈수록 y가 증가한다. )
변환행렬을 단위행렬로 설정해놓으면 항상 이 상태로 텍스쳐가 출력된다.
자, 이제 행렬의 마법이 시작된다.
1. 평행이동 D3DXMatrixTranslation(행렬, x이동, y이동, z이동)
x축, y축, z축 방향으로 평행이동시킨다. 근데 2D출력에서는 z축이동은 필요가 없으므로 무시해도 된다.

D3DXMATRIX mat;
D3DXMatrixTranslation(&mat, 3.f, 4.f, 0.f);
m_pspr->SetTransform(&mat);
//x축으로 3, y축으로 4만큼 평행이동해서 그린다.

2. 확대/축소 D3DXMatrixScaling(행렬, x축확대율, y축확대율, z축확대율)
x축, y축, z축으로 일정 비율 확대/축소시킨다. 역시 z축은 무시하도록 하자. 확대/축소시에 기준점은 항상 원점이다.

D3DXMATRIX mat;
D3DXMatrixScaling(&mat, 3.f, 4.f, 1.f);
m_pspr->SetTransform(&mat);
//x축으로 3배 늘리고, y축으로 4배 늘려서 그린다.

3. 회전 D3DXRotationZ(행렬, 회전각도)
z축을 축으로 일정 각도 회전시킨다. z축은 원점을 지나, x,y축에 수직으로 화면속으로 들어가는 방향이다. 그러므로 기준점은 원점이 되고, 회전방향은 우리가 보는 입장에서 시계방향이 된다.

D3DXMATRIX mat;
D3DXMatrixRotationZ(&mat, D3DX_PI/6.f);
m_pspr->SetTransform(&mat);
//시계방향으로 30도(Pi/6)회전시켜서 그린다.

참 쉽죠?
근데,  원점을 기준으로 회전하니까, 좀 거시기하다. 텍스쳐의 중심을 기준으로 회전하게 하려면 어떻게 해야 할까?

먼저 평행이동을 통해서 텍스쳐의 중심을 원점과 일치시킨다.
그리고 회전시킨다.
그리고 다시 평행이동시켜 원래 위치로 보낸다

D3DXMATRIX mat, mat1, mat2, mat3;
D3DXMatrixTranslation(&mat1, -w*0.5f, -h*0.5f, 0.f); //w,h는 텍스쳐의 가로 세로길이
D3DXMatrixRotationZ(&mat2, angle); //angle은 회전각도
D3DXMatrixTranslation(&mat3, w*0.5f, h*0.5f, 0.f);
mat=mat1*mat2*mat3;
/*변환을 차례대로 적용하고 싶으면,
변환행렬들을 차례대로 곱하면 된다.*/
m_pspr->SetTransform(&mat);

이제 스프라이트를 회전시켜서, 확대/축소시켜서 그릴수 있겠다.


가로로 sx배, 세로로 sy배 확대/축소하는 함수이다.
중간에
mat*=*D3DXMatrixTranslation(&mat2, (float)x, (float)y, 0);
라는 상당히 해괴한 코드가 보인다.

D3DXMatrix~~함수는 첫번째 인자를 다시 리턴해준다.
즉 원래는
D3DXMatrixTranslation(&mat2, (float)x, (float)y, 0);
mat*=mat2;
인데, 어차피 D3DXMatrixTranslation(&mat2, (float)x, (float)y, 0)가 &mat2이므로,
mat*=*D3DXMatrixTranslation(&mat2, (float)x, (float)y, 0);라고 쓴것이다.

ID3DXSprite::Draw을 사용할때, 주의할점.

HRESULT Draw(  LPDIRECT3DTEXTURE9 pTexture,  CONST RECT * pSrcRect,  CONST D3DXVECTOR3 * pCenter,  CONST D3DXVECTOR3 * pPosition,  D3DCOLOR Color );
네 번째 인자로 주는 pPosition으로 좌표를 줄때, 행렬변환이 적용된 것을 감안해야 한다.
만약 가로, 세로 2배씩 키워주는 행렬을 변환행렬로 잡았을때, pPosition을 (10, 10, 0)으로 주면, pPosition에도 변환이 적용되어 결국 (20, 20, 0)에 텍스쳐를 출력하게 될 것이다.

행렬변환을 이용해서 스프라이트를 여러가지 모습으로 출력해보자.
이 댓글을 비밀 댓글로