|
유튜브 동영상 강의 주소
(1) https://youtu.be/8jfrSywwt80 (2) https://youtu.be/WqpaN9wctA4 (3) https://youtu.be/Zx3CyyyEPtk
소스는 자료실에서 다운로드 하시면 됩니다.
9강에서 배운 Teapot와 여러 변환 행렬을 적용하여 3D 도형 물체를 월드 공간에 출력하여 보자. |
|
10.1 행렬 변환
크기와 이동 그리고 회전 행렬은 다음과 같은 함수를 통해 구할 수 있으며 행렬 곱을 통해 하나의 행렬로 만들 수 있다.
(S ●R ●T)
행렬 변환 함수 | |
이동, 회전, 크기 행렬 변환 함수 | D3DXMatrixTranslation() D3DXMatrixScaling() D3DXToDegree() D3DXToRadian() D3DXMatrixRotationX() D3DXMatrixRotationY() D3DXMatrixRotationZ() D3DXMatrixRotationYawPitchRoll(() D3DXMatrixRotationAxis() |
쿼터니온 함수 | D3DXQuaternionRotationMatrix() D3DXQuaternionRotationYawPitchRoll() D3DXQuaternionRotationAxis() D3DXMatrixRotationQuaternion() D3DXQuaternionNormalize() |
1) 크기
먼저 축을 출력하고 a(증가), d(감소) 키 눌림에 따라 Teapot의 크기가 증감하도록 프로그램을 작성하여 보자.
증감 단위는 0.1f로 하고 증감값 x, y, z 에는 동일한 값을 적용한다.
키 눌림은 GetAsyncKeyState() 함수를 이용하면 된다.
이 함수는 설정된 키의 눌림만을 체크하며 만약 현재 눌림이 있으면 0 이하의 값을 리턴하므로 이 값을 체크하여
a, d 눌림에 따른 스케일 값을 변경하도록 한다.
SHORT WINAPI GetAsyncKeyState( _In_ int vKey ); |
키보드의 키 값은 virtual 키를 입력하는데 문자키인 A ~ Z, 0 ~ 1 까지는 문자상수 값을 대입하고 그 외에는
윈도우에서 정한 VK_F1, VK_RETURN등과 같은 가상키를 사용한다.
단, 문자키는 대문자로만 체크를 한다.
예를 들면 ‘A’ 문자를 체크하려면 아래와 같이 한다.
if( GetAsyncKeyState( 'A' ) < 0 ) // 눌림 m_fScale += 0.1f; |
행렬 변환이 일어나므로 축을 출력할 때는 단위행렬로 월드 변환을 설정해야 한다.
그래야 다른 월드 변환에 영향을 받지 않는다.
void CAxis::OnRender() { D3DXMATRIX matWorld; D3DXMatrixIdentity( &matWorld ); m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld); m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(CUSTOMVERTEX) ); m_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX ); m_pd3dDevice->DrawPrimitive( D3DPT_LINELIST, 0, 3 );
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); } |
- Teaport 크기 행렬 변환
[그림 10-1] 크기 변환 Teapot
#pragma once #include "d3dapp.h" #include <d3dx9math.h> #include <D3dx9shape.h> #include "Axis.h"
class CGameEdu01 : public CD3DApp { virtual void OnInit(); virtual void OnRender(); virtual void OnUpdate(); virtual void OnRelease();
D3DXMATRIX m_matView; D3DXMATRIX m_matProj; D3DXVECTOR3 m_Eye, m_At, m_Up; CAxis m_Axis;
LPD3DXMESH m_pTeapotMesh; float m_fScale; public: CGameEdu01(void); ~CGameEdu01(void); }; |
[소스 10-1] CGameEdu01 class 헤더
#include "StdAfx.h" #include "GameEdu01.h"
CGameEdu01::CGameEdu01(void) { }
CGameEdu01::~CGameEdu01(void) { }
void CGameEdu01::OnInit() { RECT rect; D3DVIEWPORT9 vp; GetClientRect( m_hWnd, &rect );
vp.X = 0; vp.Y = 0; vp.Width = rect.right - rect.left; vp.Height = rect.bottom - rect.top; vp.MinZ = 0.0f; vp.MaxZ = 1.0f;
m_Eye.x = 2.0f; m_Eye.y = 5.0f; m_Eye.z = -8.0f;
m_At.x = 0.0f; m_At.y = 0.0f; m_At.z = 0.0f;
m_Up.x = 0.0f; m_Up.y = 1.0f; m_Up.z = 0.0f;
D3DXMatrixLookAtLH( &m_matView, &m_Eye, &m_At, &m_Up ); m_pd3dDevice->SetTransform( D3DTS_VIEW, &m_matView );
D3DXMatrixPerspectiveFovLH( &m_matProj, D3DX_PI / 4, 1.0f, 1.0f, 100.0f ); m_pd3dDevice->SetTransform( D3DTS_PROJECTION, &m_matProj ); m_pd3dDevice->SetViewport( &vp );
m_Axis.OnInit( m_pd3dDevice ); D3DXCreateTeapot( m_pd3dDevice, &m_pTeapotMesh, NULL ); m_fScale = 1.0f; // 스케일 값 초기화 }
void CGameEdu01::OnRender() { D3DXMATRIX matScale;
m_Axis.OnRender();
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME); m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE);
D3DXMatrixScaling( &matScale, m_fScale, m_fScale, m_fScale ); // 크기 행렬 m_pd3dDevice->SetTransform( D3DTS_WORLD, &matScale ); m_pTeapotMesh->DrawSubset(0); }
void CGameEdu01::OnUpdate() { if( GetAsyncKeyState( 'A' ) < 0 ) // 키 눌림 m_fScale += 0.1f;
if( GetAsyncKeyState( 'D' ) < 0 ) // 키 눌림 m_fScale -= 0.1f; }
void CGameEdu01::OnRelease() { m_pTeapotMesh->Release(); m_Axis.OnRelease(); } |
[소스 10-2] CGameEdu01 class 소스
2) 회전
시간에 따라 x, y, z 축을 기준으로 회전하는 Teapot를 출력하는 프로그램을 작성해 보자.
- x축, y축, z축 회전
절대축을 기준으로 회전하는 Teapot를 출력해 보자.
회전각은 렌더링 동안에 계속 회전되도록 현재 시각을 라디안 값으로 설정하면 된다.
즉 현재 시각은 계속 증가하므로 현재시각 * 0.004f 으로 식을 만들면 회전각이 계속 증가하는 라디안 값을
만들 수 있다.
void CGameEdu01::OnRender() { D3DXMATRIX matRotation;
m_Axis.OnRender();
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME); D3DXMatrixRotationX( &matRotation, GetTickCount() * 0.004f ); // D3DXMatrixRotationY( &matRotation, GetTickCount() * 0.004f ); y축 회전 // D3DXMatrixRotationZ( &matRotation, GetTickCount() * 0.004f ); z축 회전 m_pd3dDevice->SetTransform( D3DTS_WORLD, &matRotation ); m_pTeapotMesh->DrawSubset(0); } |
- 임의의 축 회전
임의의 축을 지정하고 축을 기준으로 회전하는 Teapot를 출력해 보자.
void CGameEdu01::OnRender() { D3DXMATRIX matRotation; D3DXVECTOR3 v1( 1.0f, 1.0f, 0.0f );
m_Axis.OnRender();
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME); D3DXMatrixRotationAxis( &matRotation, &v1, GetTickCount() * 0.004f );
m_pd3dDevice->SetTransform( D3DTS_WORLD, &matRotation ); m_pTeapotMesh->DrawSubset(0); } |
- 쿼터니온 회전
임의의 축을 지정하고 이를 쿼터이온으로 바꾸고 이를 쿼터니온 회전을 한 후에 마지막 회전 행렬을 만들어
Teapot가 회전하도록 출력해 보자.
void CGameEdu01::OnRender() { D3DXMATRIX matRotation; D3DXQUATERNION vQuatenion;
D3DXVECTOR3 v1( 1.0f, 1.0f, 0.0f ); D3DXQuaternionRotationAxis( &vQuatenion, &v1, GetTickCount() * 0.004f ); D3DXMatrixRotationQuaternion( &matRotation, &vQuatenion );
m_Axis.OnRender();
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME);
m_pd3dDevice->SetTransform( D3DTS_WORLD, &matRotation ); m_pTeapotMesh->DrawSubset(0); } |
- 회전 행렬의 결합
회전 행렬에서 3각을 활용할 때는 Z, X, Y 순으로 곱해 주며 이 순서는
D3DXMatrixRotationYawPitchRoll((), D3DXQuaternionRotationYawPitchRoll() 과 같다.
아래의 소스는 D3DXMatrixRotationYawPitchRoll(()과 Z, X, Y 순의 회전 행렬을 X축 45도, Y축 45도, Z축
90도로 회전시킨 Teapot이다.
[그림 10-2] 회전 행렬의 결합
void CGameEdu01::OnRender() { D3DXMATRIX matRotationX, matRotationY, matRotationZ, matRotation; float fRotX, fRotY, fRotZ;
fRotX = D3DXToRadian( 45 ); fRotY = D3DXToRadian( 45 ); fRotZ = D3DXToRadian( 90 );
m_Axis.OnRender();
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME);
// D3DXMatrixRotationX( &matRotationX, fRotX ); // D3DXMatrixRotationY( &matRotationY, fRotY ); // D3DXMatrixRotationZ( &matRotationZ, fRotZ );
D3DXMatrixRotationYawPitchRoll( &matRotation, fRotY, fRotX, fRotZ ); // matRotation = matRotationZ * matRotationX * matRotationY; // 회전 행렬의 결합
m_pd3dDevice->SetTransform( D3DTS_WORLD, &matRotation ); m_pTeapotMesh->DrawSubset(0); } |
3) 이동
D3DXMatrixTranslation() 함수를 사용하여 Teapot를 아래와 같이 이동시켜 출력해 보자.
Teapot를 이동하게 되면 카메라 공간에서 벗어날 수 있으므로 카메라의 현재 위치 안에 Teapot가 들어오도록 확인하고
프로그래밍해 보자.
[그림 10-3] 이동 행렬의 결합
void CGameEdu01::OnRender() { D3DXMATRIX matTranslation;
m_Axis.OnRender();
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME); D3DXMatrixTranslation( &matTranslation, 5.0f, 0.0f, 3.0f );
m_pd3dDevice->SetTransform( D3DTS_WORLD, &matTranslation ); m_pTeapotMesh->DrawSubset(0); } |
4) 크기와 이동 그리고 회전 행렬의 결합
행렬의 결합에서 SRT로 결합했을 때와 STR로 결합했을 때의 차이를 프로그래밍을 통해 확인해 보자.
하나의 Teapot 메쉬에 다른 결합 형렬을 적용하여 출력하여 보자.
첫 번째는 스케일이 적용된 Teapot는 중심에서만 Y축 회전한다.
두 번째는 첫 번째 Y 회전에 이동행렬이 적용된 것이며 SRT가 적용된다.
세 번째는 SRT가 아닌 STR을 적용했을 때의 결과로써 이동 거리만큼 떨어져 중심을 기준으로 회전하게 된다.
행렬의 결합 순서에 따라 다르게 출력되는 것을 확인해 보자.
[그림 10-4] S, R, T 행렬의 결합
void CGameEdu01::OnRender() { D3DXMATRIX matRotationY, matWorld, matTrans, matScaling; float fScaling[3] = { 0.3f, 0.6f, 1.0f }; D3DXVECTOㄴR3 vTrans[3] = { D3DXVECTOR3( 0.0f, 0.0f, 0.0f), D3DXVECTOR3( 2.0f, 0.0f, 0.0f ), D3DXVECTOR3( 5.0f, 0.0f, 0.0f ) };
D3DXMatrixRotationY( &matRotationY, GetTickCount() * 0.004f );
m_Axis.OnRender();
m_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); m_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME); // 첫 번째 Teapot D3DXMatrixRotationY( &matRotationY, GetTickCount() * 0.004f ); D3DXMatrixScaling( &matScaling, fScaling[0], fScaling[0], fScaling[0] ); D3DXMatrixTranslation( &matTrans, vTrans[0].x, vTrans[0].y, vTrans[0].z ); matWorld = matScaling * matRotationY * matTrans; m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); m_pTeapotMesh->DrawSubset(0); // 두 번째 Teapot D3DXMatrixScaling( &matScaling, fScaling[1], fScaling[1], fScaling[1] ); D3DXMatrixTranslation( &matTrans, vTrans[1].x, vTrans[1].y, vTrans[1].z ); matWorld = matScaling * matRotationY * matTrans; m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); m_pTeapotMesh->DrawSubset(0); // 세 번째 Teapot D3DXMatrixScaling( &matScaling, fScaling[2], fScaling[2], fScaling[2] ); D3DXMatrixTranslation( &matTrans, vTrans[2].x, vTrans[2].y, vTrans[2].z ); matWorld = matScaling * matTrans * matRotationY; m_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); m_pTeapotMesh->DrawSubset(0); } |