행렬을 D3D에서 사용하는 가장 큰 이유는 각종 변환을 행렬이라는 도구를 사용하여 간략하게 할 수 있기 때문이다.
이동, 회전, 크기등의 모든 변환이 행렬을 곱하는 동작만으로 간단하게 이루어진다.
그리고, 수학의 결합법칙(AB)C = A(BC)이 성립하기 때문에 사용한다.
왜 결합법칙이 중요할까, 보자
만약 1만개의 폴리곤으로 이루어진 메시가 있다고 가정해보자.
1만개의 폴리곤이라면 하나의 폴리곤이 3개의 정점으로 이루어지니까, 대략 3만개의 정점 연산이 필요하다는 얘기가 된다. 이 물체를 Y축으로 90도 회전시킨 다음 (10, 10, 10)의 위치로 이동시킨다고 생각해보자.
그럼 이런 연산이 필요해
1. 3만개의 정점 X 회전행렬(4x4) = 3만개의 회전된 정점
2. 3만개의 회전된 정점 X 이동행렬(4x4) = 3만개의 회전 + 이동된 정점
즉, 3만 X 16 + 3만 X 16 = 48만 + 48만 = 96만......
96만번의 실수연산이 일어나게 된다, 아무리 CPU가 좋다고해도 이건 힘들겠죠?
여기서 결합법칙 하나만 적용해도 순식간에 연산량을 반으로 줄일 수 있다
정리해보면......
(정점 X 회전행렬) X 이동행렬
이걸 수학의 결합법칙을 적용하면
(정점 X 회전행렬) X 이동행렬 = 정점 X (회전행렬 X 이동행렬) : 행렬은 결합법칙 성립
여기서, (회전행렬 X 이동행렬)의 결과는 4x4 행렬이므로 먼저 (회전행렬 X 이동행렬)의 결과를 구해서 정점에 곱하면 다음과 같은 결과가 나온다.
3만 X 행렬(4x4) + 16 X 16 = 48만 + 256 = 480,256
행렬은 결합법칙이 성립한다는 원리를 사용해 순식간에 연산량을 반으로 줄일 수 있다.
3D그래픽과 행렬은 도저히 따로 떼어서 이해할 수 없으므로 철저히 공부해야한다.
변환행렬
행렬식이 프로그램으로 구현되는 방식을 보자.
행렬곱셈
A=(Aij), B = (Bik)일 때 AB = [n시그마j=1 AijBjk]이다.
- 행렬표현
[a11 a12 a13 a14] [b11 b12 b13 b14] [c11 c12 c13 c14]
[a21 a22 a23 a24] [b21 b22 b23 b24] [c21 c22 c23 c24]
[a31 a32 a33 a34] X [b31 b32 b33 b34] = [c31 c32 c33 c34]
[a41 a42 a43 a44] [b41 b42 b43 b44] [c41 c42 c43 c44]
- DX7.0 함수 구현
VOID D3DMath_MatrixMultiply(D3DMATRIX& q, D3DMATRIX& a, D3DMATRIX& b){
FLOAT* pA = (FLOAT*)&a;
FLOAT* pB = (FLOAT*)&b;
FLOAT pM[16];
ZeroMemory(pM, sizeof(D3DMATRIX));
for(WORD i=0; i<4; i++)
for(WORD j=0; i<4; j++)
for(WORD k=0; k<4; k++)
pM[4*i+j] += pA[4*i+k] * pB[4*k+j];
memcpy(&q, pM, sizeof(D3DMATRIX));
}
- DX9.0 함수 구현
D3DMATRIX *D3DXMatrixMultiply(D3DXMATRIX *pOut, CONST D3DXMATRIX *pM1, CONST D3DXMATRIX *pM2);
단위행렬
AI = IA = A를 만족하는 행렬 I를 단위행렬이라 한다.
- 행렬표현
[1 0 0 0]
[0 1 0 0]
I = [0 0 1 0]
[0 0 0 1]
- DX7.0 함수 구현
inline VOID D3DUtil_SetIdentityMatrix(D3DMATRIX& m){
m._12 = m._13 = m._14 = m._21 = m._23 = m._24 = 0.0f;
m._31 = m._32 = m._34 = m._41 = m._42 = m._43 = 0.0f;
m._11 = m._22 = m._33 = m._44 = 1.0f;
}
- DX9.0 함수 구현
D3DMATRIX *D3DXMatrixIdentity(D3DXMATRIX *pOut);
역행렬
AA-1 = A-1A = I를 만족하는 행렬 A-1을 역행렬이라 한다. (-1 : 지수)
- 행렬표현
[A11 A12 A13 A14]
1 [A21 A22 A23 A24]
A-1 = det A [A31 A32 A33 A34]
[A41 A42 A43 A44]
여기서 det A는 행렬식의 값(Determinant)이라고 하는데, 복잡하니까 그냥 그런줄알아
- DX7.0 함수 구현
HRESULT D3DMath_MatrixInvert(D3DMATRIX& q, D3DMATRIX& a){
// 이 함수는 DX SDK 7.0에 있는 버그를 수정한 버전이다.
D3DMATRIX TempMat;
if(fabs(a._44 - 1.0f) > .001f)
return E_INVALIDARG;
if(fabs(a._14) > .001f || fabs(a._24) > .001f || fabs(a._34) > .001f)
return E_INVALIDARG;
FLOAT fDetInv = 1.0f / (a._11 * (a._22 * a._33 - a._23 * a._32) -
a._12 * (a._21 * a._33 - a._23 * a._31) +
a._13 * (a._21 * a._32 - a._22 * a._31));
TempMat._11 = fDetInv * (a._22 * a._33 - a._23 * a._32);
TempMat._12 = fDetInv * (a._12 * a._33 - a._13 * a._32);
TempMat._13 = fDetInv * (a._12 * a._23 - a._13 * a._22);
TempMat._14 = 0.0f;
TempMat._21 = -fDetInv * (a._21 * a._33 - a._23 * a._31);
TempMat._22 = -fDetInv * (a._11 * a._33 - a._13 * a._31);
TempMat._23 = -fDetInv * (a._11 * a._23 - a._13 * a._21);
TempMat._24 = 0.0f;
TempMat._31 = fDetInv * (a._21 * a._32 - a._22 * a._31);
TempMat._32 = -fDetInv * (a._11 * a._32 - a._12 * a._31);
TempMat._33 = fDetInv * (a._11 * a._22 - a._12 * a._21);
TempMat._34 = 0.0f;
TempMat._41 = -(a._41 * TempMat._11 + a._42 * TempMat._21 + a._43 *
TempMat._31);
TempMat._42 = -(a._41 * TempMat._12 + a._42 * TempMat._22 + a._43 *
TempMat._32);
TempMat._43 = -(a._41 * TempMat._13 + a._42 * TempMat._23 + a._43 *
TempMat._33);
TempMat._44 = 1.0f;
memcpy(&q, &TempMat, sizeof(D3DMATRIX));
return S_OK;
}
- DX9.0 함수 구현
D3DMATRIX *D3DXMatrixInverse(D3DXMATRIX *pOut, FLOAT *pDeterminant, CONST D3DXMATRIX *pM);
이동행렬
주어진 위치로 이동하는 행렬, 4x4
- 행렬표현
[1 0 0 0]
[0 1 0 0]
[0 0 1 0]
[dx dy dz 1]
- DX7.0 함수 구현
inline VOID D3DUtil_SetTranslateMatrix(D3DMATRIX& m, FLOAT tx, FLOAT ty, FLOAT tz){
D3DUtil_SetIdentityMatrix(m);
m._41 = tx; m._42 = ty; m._43 = tz;
}
- DX9.0 함수 구현
D3DXMATRIX *D3DXMatrixTranslation(D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z);
회전행렬
말그대로 회전하는 행렬
오일러각 방식, 각축 방식등 다양한 방법이 있지만, 일반적으로 사원수를 많이 사용한다. 그래서 그걸로 한다.
- 행렬표현
X축 회전행렬
[1 0 0 0]
[0 cos sin 0]
[0 -sin cos 0]
[0 0 0 1]
Y축 회전행렬
[cos 0 -sin 0]
[ 0 1 0 0]
[sin 0 cos 0]
[ 0 0 0 1]
Z축 변환행렬
[cos sin 0 0]
[-sin cos 0 0]
[ 0 0 1 0]
[ 0 0 0 1]
- DX7.0 함수 구현
VOID D3DUtil_SetRotateXMatrix(D3DMATRIX& mat, FLOAT fRads){
D3DUtil_SetIdentityMatrix(mat);
mat._22 = cosf(fRads);
mat._23 = sinf(fRads);
mat._32 = -sinf(fRads);
mat._33 = cosf(fRads);
}
VOID D3DUtil_SetRotateYMatrix(D3DMATRIX& mat, FLOAT fRads){
D3DUtil_SetIdentityMatrix(mat);
mat._11 = cosf(fRads);
mat._13 = -sinf(fRads);
mat._31 = sinf(fRads);
mat._33 = cosf(fRads);
}
VOID D3DUtil_SetRotateZMatrix(D3DMATRIX& mat, FLOAT fRads){
D3DUtil_SetIdentityMatrix(mat);
mat._11 = cosf(fRads);
mat._12 = sinf(fRads);
mat._21 = -sinf(fRads);
mat._22 = cosf(fRads);
}
- DX9.0 함수 구현
D3DXMatrixRotationX(), D3DXMatrixRotationY(), D3DXMatrixRotationZ(),
D3DXMatrixRotationAxis(), D3DXMatrixRotationYawPitchRoll(),
D3DXMatrixRotationQuaternion()
확대/축소행렬
확대/축소행렬은 크기변환을 표현하는 행렬이다.
sx의 값이 1보다 작으면 x축 방향으로 축소, 1보다 크면 x축 방향으로 확대를 나타낸다.
sy, sz도 마찬가지다.
- 행렬표현
[sx 0 0 0]
[0 sy 0 0]
[0 0 sz 0]
[0 0 0 1]
- DX7.0 함수구현
inline VOID D3DUtil_SetScaleMatrix(D3DMATRIX& m, FLOAT sx, FLOAT sy, FLOAT sz){
D3DUtil_SetIdentityMatrix(m);
m._11 = sx; m._22 = sy; m._33 = sz;
}
- DX9.0 함수구현
D3DXMATRIX *D3DXMatrixScaling(D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz);
좌표계 변환 행렬
좌표계 변환 행렬은 회전행렬의 일종으로 생각할 수 있지만, 내적 연산을 행렬에 응용하면 다음과 같이 표현할 수 있다.
- 행렬표현
[Px Qx Rx 0]
[Py Qy Ry 0]
[Pz Qz Rz 0]
[ 0 0 0 1]
왜 이렇게 표현이 될까,, 생각해보자
V(정점)를 P=(Px, Py, Pz), Q=(Qx, Qy, Qz), R=(Rx, Ry, Rz) 좌표계로 변환하려면 다음과 같은 연산을 거쳐야 한다고 했어 (벡터설명 참조yo)
V'p = PㆍV = (PxVx + PyVy + PzVz)
V'q = QㆍV = (QxVx + QyVy + QzVz)
V'r = RㆍV = (RxVx + RyVy + RzVz)
그래서, 이 연산을 행렬로 표기하면 다음과 같아진다.
[Px Qx Rx 0]
V' = [Vx Vy Vz 1] X [Py Qy Ry 0]
정점정보 [Pz Qz Rz 0]
[ 0 0 0 1]
'Graphics > DirectX' 카테고리의 다른 글
Direct 3D - 사원수 (Quaternion) (0) | 2014.07.08 |
---|---|
Direct 3D - 렌더링 파이프라인 (1) | 2014.06.19 |
Direct 3D - 행렬기초 (0) | 2014.06.19 |
Direct 3D - 벡터 (0) | 2014.06.19 |
Direct 3D - 프레임워크 (0) | 2014.06.19 |