Download presentation
Presentation is loading. Please wait.
1
제12장 유연한 카메라 클래스 만들기 2009-1학기 컴퓨터게임(DirectX)
2
카메라 디자인 사용자 입력에 반응하여 카메라를 이동
우향 벡터, 상향 벡터, 전방 벡터, 위치 벡터의 네 가지 카메라 벡터를 이용 월드 좌표 시스템에서의 카메라 위치와 방향을 정의 우향, 상향, 전방의 세 벡터는 월드 내에 카메라의 방향을 정의 방위 벡터(orientation vector)라고 함 2009-1학기 컴퓨터게임(DirectX)
3
정직교가 되기 위해서는 각각의 벡터가 서로 수직이며 단위길이여야 함
방위벡터는 반드시 정직교여야 함 정직교가 되기 위해서는 각각의 벡터가 서로 수직이며 단위길이여야 함 U P = 위치 벡터 R = 우향 벡터 U = 상향 벡터 L = 전방 벡터 L R P 2009-1학기 컴퓨터게임(DirectX)
4
카메라의 동작(6가지) 우향 벡터를 기준으로 회전(pitch) 상향 벡터를 기준으로 회전(yaw)
전방 벡터를 기준으로 회전(roll) 우향 벡터 방향으로 이동 상향 벡터 방향으로 날기 전방 벡터 방향으로 이동 이 여섯가지 동작을 통해 3 개의 축을 기준으로 하는 회전과 세 개의 축을 향한 이동이 가능하게 됨 => 모두 6 가지 종류의 자유 허용 2009-1학기 컴퓨터게임(DirectX)
5
Camera 클래스 다음의 카메라 클래스 선언은 원하는 데이터와 메서드가 선언되는 방법을 보여준다. class Camera {
public: enum CameraType { LANDOBJECT, AIRCRAFT }; Camera(); Camera(CameraType cameraType); ~Camera(); void strafe(float units); // left/right void fly(float units); // up/down void walk(float units); // forward/backward 2009-1학기 컴퓨터게임(DirectX)
6
void pitch(float angle); // rotate on right vector
void yaw(float angle); // rotate on up vector void roll(float angle); // rotate on look vector void getViewMatrix(D3DXMATRIX* V); void setCameraType(CameraType cameraType); void getPosition(D3DXVECTOR3* pos); void setPosition(D3DXVECTOR3* pos); void getRight(D3DXVECTOR3* right); void getUp(D3DXVECTOR3* up); void getLook(D3DXVECTOR3* look); 2009-1학기 컴퓨터게임(DirectX)
7
CameraType _cameraType; D3DXVECTOR3 _right; D3DXVECTOR3 _up;
private: CameraType _cameraType; D3DXVECTOR3 _right; D3DXVECTOR3 _up; D3DXVECTOR3 _look; D3DXVECTOR3 _pos; }; 2009-1학기 컴퓨터게임(DirectX)
8
CameraType : 카메라 타입을 지정하기 위한 것 AIRCRAFT :
공간을 자유롭게 이동할 수 있도록 해주며 6가지의 자유를 허용 1인칭 슈팅 게임과 같은 일부 게임에서는 주인공이 하늘을 날 수 없으므로 특정한 축으로의 이동을 제한 => LANDOBJECT 카메라 모델이 적합함. 2009-1학기 컴퓨터게임(DirectX)
9
구현 방법 뷰 행렬 계산하기 p = (px, py, pz), r = (rx, ry, rz), u = (ux, uy, uz), d = (dx, dy, dz); 위 벡터를 위치, 우향, 상향, 전방 벡터라고 하자. 2장에서 뷰 스페이스 변환은 월드 내의 기하정보를 변환하여 카메라를 원점에 위치, 좌표축에 따라 정렬시키는 과정임을 설명하였다. 2009-1학기 컴퓨터게임(DirectX)
10
월드 스페이스에서 뷰 스페이스로의 변환 A B C D A B C D A B C D 월드 스페이스 내의 물체와 카메라
월드 스페이스 내의 물체와 카메라 관찰점을 원점으로 변환, 물체도 함께 회전 Z-축에 맞도록 관찰점을 회전, 물체도 함께 회전 2009-1학기 컴퓨터게임(DirectX)
11
변환 행렬 V로 다음과 같은 작업이 수행되어야 함 pV = (0, 0, 0) : 행렬 V는 카메라를 원점으로 이동
rV = (1, 0, 0) : 행렬 V는 우향 벡터를 월드 X축과 정렬 uV = (0, 1, 0) : 행렬 V는 상향 벡터를 월드 Y축과 정렬 dV = (0, 0, 1) : 행렬 V는 전방 벡터를 월드 Z축과 정렬 이 벡터를 찾는 과정은 카메라의 위치를 원점으로 이동하는 이동파트와 카메라와 벡터를 월드의 축과 정렬하는 회전 파트의 두 개의 파트로 나눔 2009-1학기 컴퓨터게임(DirectX)
12
파트 1 : 이동 P를 원점으로 이동하는 이동은 P – P = 0 이므로 -P 로 쉽게 얻을 수 있다.
즉 뷰 변환의 이동 파트는 간단하게 가능 T = -Px –Py –Pz 1 2009-1학기 컴퓨터게임(DirectX)
13
파트 2 : 회 전 우향, 상향, 전방 벡터를 각각의 월드 x,y,z축과 정렬하는 3 * 3 행렬 A 가 필요
다음 방정식을 만족시켜야 함 rA = [rx, ry, rz] = [1, 0, 0] uA = [ux, uy, uz] = [0, 1, 0] dA = [dx, dy, dz] = [0, 0, 1] A00 A01 A02 A10 A11 A12 A20 A21 A22 A00 A01 A02 A10 A11 A12 A20 A21 A22 A00 A01 A02 A10 A11 A12 A20 A21 A22 2009-1학기 컴퓨터게임(DirectX)
14
세 개의 시스템이 모두 동일한 계수 행렬A를 가지므로 한번에 해결 가능 BA = =
BA = BB = I 이므로 A는 B의 역행렬 B는 직교 행렬이므로 역 행렬이 또한 전치 행렬 방위벡터를 월드의 축과 정렬하는 변환은 다음과 같다: B-1 = BT = A = rx ry rz ux uy uz dx dy dz A00 A01 A02 A10 A11 A12 A20 A21 A22 0 0 rx ux dx ry uy dy rz uz dz 2009-1학기 컴퓨터게임(DirectX)
15
두 개의 파트를 조합하기 마지막으로 A를 4 * 4 행렬로 변환 이동 파트와 회전파트를 결합 뷰변환 행렬 V를 얻음.
TA = = = V Camera::getViewMatrix 메서드로 이 행렬을 구성 -px –py –pz 1 rx ux dx 0 ry uy dy 0 rz uz dz 0 rx ux dx 0 ry uy dy 0 rz uz dz 0 -p•r -p•u -p•d 1 2009-1학기 컴퓨터게임(DirectX)
16
void Camera::getViewMatrix(D3DXMATRIX* V) {
D3DXVec3Normalize(&_look, &_look); D3DXVec3Cross(&_up, &_look, &_right); D3DXVec3Normalize(&_up, &_up); D3DXVec3Cross(&_right, &_up, &_look); D3DXVec3Normalize(&_right, &_right); float x = -D3DXVec3Dot(&_right, &_pos); float y = -D3DXVec3Dot(&_up, &_pos); float z = -D3DXVec3Dot(&_look, &_pos); (*V)(0,0) = _right.x; (*V)(0, 1) = _up.x; (*V)(0, 2) = _look.x; (*V)(0, 3) = 0.0f; 부동소수점 오류로 인해 카메라축이 서로 직각이 아닐 수 있어서 전방 벡터를 기준으로 상향 벡터와 우향 벡터를 재계산하여 항상 벡터가 서로 직각이 되도록 함 2009-1학기 컴퓨터게임(DirectX)
17
(*V)(1,0) = _right.y; (*V)(1, 1) = _up.y; (*V)(1, 2) = _look.y; (*V)(1, 3) = 0.0f;
(*V)(2,0) = _right.z; (*V)(2, 1) = _up.z; (*V)(2, 2) = _look.z; (*V)(2, 3) = 0.0f; (*V)(3,0) = x; (*V)(3, 1) = y; (*V)(3, 2) = z; (*V)(3, 3) = 1.0f; } 2009-1학기 컴퓨터게임(DirectX)
18
임의의 축으로 회전하기 카메라 회전 메서드를 구현하기 위해서는 임의의 축을 기준으로 회전할 수 있어야 함.
D3DXMATRIX*& D3DMatrixRotationAxis (D3DXMATRIX *pOut, CONST D3DXVECTOR3 *pv, FLOAT Angle); pOut : 회전 행렬을 리턴 pV : 회전의 기준 축 Angle : 라디안 단위의 회전할 각도 2009-1학기 컴퓨터게임(DirectX)
19
D3DXMatrixRotationAxis(&R, &axis, D3DX_PI/2.0f);
Y 벡터(0.707, 0.707, 0)로 정의된 축을 기준으로 ∏ /2 라디안 만큼 회전시키고자 한다면 D3DXMATRIX R; D3DXVECTOR3 axis(0.707f, 0.707f, 0.0f); D3DXMatrixRotationAxis(&R, &axis, D3DX_PI/2.0f); Z A X 2009-1학기 컴퓨터게임(DirectX)
20
Pitch, Yaw, Roll 방위 벡터는 월드 좌표 시스템에 상대적인 카메라의 방위를 나타냄
세 개의 축을 기준으로 한 회전을 통해 방위 벡터를 갱신하는 방법이 필요 다음의 그림은 pitch, yaw, roll 로 불리는 세 가지의 카메라 회전 방법을 보여줌 2009-1학기 컴퓨터게임(DirectX)
21
카메라 회전 u u 카메라의 우향 벡터를 기준으로 회전 d r r d u u r 카메라의 상향 벡터를 기준으로 회전 d r d
카메라의 전방 벡터를 기준으로 회전 d d r r 2009-1학기 컴퓨터게임(DirectX)
22
우향 벡터를 기준으로 회전할 때 (pitch) 우향 벡터를 기준으로 지정된 각도만큼 상향 벡터와 전방 벡터를 회전시킴
상향 벡터를 기준으로 회전할 때 (yaw) 상향 벡터를 기준으로 지정된 각도만큼 전방 벡터와 우향 벡터를 회전시킴 전방 벡터를 기준으로 회전할 때 (roll) 전방 벡터를 기준으로 지정된 각도만큼 상향 벡터와 우향 벡터를 회전시킴 2009-1학기 컴퓨터게임(DirectX)
23
D3DMatrixRotationAxis 함수가 필요한 것
우리가 회전의 기준으로 이용하는 이 세개의 벡터가 월드 내에서 임의의 방위를 가질 수 있기 때문이다. LANDOBJECT의 경우 yaw 메서드에서 카메라의 전방 벡터가 아닌 월드의 y축을 기준으로 회전할 것이며 roll 메서드는 완전히 이용할 수 없도록 함 각자의 활용 분야에 따라 Camera를 수정해야 함 2009-1학기 컴퓨터게임(DirectX)
24
Pitch, yaw, roll 메서드 구현 void Camera::pitch(float angle) {
D3DXMATRIX T; D3DXMatrixRotationAxis(&T, &_right, angle); // _right를 기준으로 _up 과 _look 을 회전한다. D3DXVec3TransformCoord(&_up,&_up, &T); D3DXVec3TransformCoord(&_look,&_look, &T); } 2009-1학기 컴퓨터게임(DirectX)
25
void Camera::yaw(float angle) { D3DXMATRIX T;
if( _cameraType == LANDOBJECT ) D3DXMatrixRotationY(&T, angle); // 비행 물체에 대해서는 원래의 상향 벡터로 회전 if( _cameraType == AIRCRAFT ) D3DXMatrixRotationAxis(&T, &_up, angle); // rotate _right and _look around _up or y-axis D3DXVec3TransformCoord(&_right,&_right, &T); D3DXVec3TransformCoord(&_look,&_look, &T); } 2009-1학기 컴퓨터게임(DirectX)
26
void Camera::roll(float angle) { // 비행 타입일 경우만 회전한다.
if( _cameraType == AIRCRAFT ) D3DXMATRIX T; D3DXMatrixRotationAxis(&T, &_look, angle); // look 벡터 기준으로 위와 오른쪽으로 회전한다. D3DXVec3TransformCoord(&_right,&_right, &T); D3DXVec3TransformCoord(&_up,&_up, &T); } 2009-1학기 컴퓨터게임(DirectX)
27
걷기, 옆걸음질, 날기 ‘걷는다’ 라는 말은 현재 바라보는 방향으로 이동한다는 것을 의미 전방 벡터 방향으로 이동
옆걸음질은 바라보는 방향에서 양옆으로 이동 우향 벡터로 이동 날기는 상향 벡터로 이동하는 것이다. 축들 중 한 방향으로 이동하기 위해서는 간단히 위치 벡터에 우리가 이동하고자 하는 방향을 가리키는 벡터를 더하면 된다. 2009-1학기 컴퓨터게임(DirectX)
28
(그림12.7) 카메라 방위 벡터로의 이동 P’ = P + sL P’ = P + rU sL rU p tR P’ = P + tR
2009-1학기 컴퓨터게임(DirectX)
29
지상의 물체를 이동할 때는 회전과 마찬가지로 약간의 제약이 필요
LANDOBJECT 물체는 위를 보는 상태에서 전진하거나 기울어진 상태에서 옆걸음으로 공중에 뜰 수 없어야 함 즉 xz평면으로 움직임을 제한해야 함. 계단이나 언덕을 오르는 등의 방법으로 고도를 변경할 수는 있으므로 Camera::setPosition 메서드로 카메라의 높이나 위치를 지정 2009-1학기 컴퓨터게임(DirectX)
30
walk., strafe, fly 메서드의 코드 구현
void Camera::walk(float units) { if (units == 0) return; // 지상의 물체는 xz 평면으로 움직임을 제한한다. if( _cameraType == LANDOBJECT ) _pos += D3DXVECTOR3(_look.x, 0.0f,_look.z) * units; if( _cameraType == AIRCRAFT ) _pos += _look * units; } 2009-1학기 컴퓨터게임(DirectX)
31
void Camera::strafe(float units) { // 지상의 물체는 xz 평면으로 움직임을 제한한다.
if( _cameraType == LANDOBJECT ) _pos += D3DXVECTOR3(_right.x, 0.0f, _right.z) *units; if( _cameraType == AIRCRAFT ) _pos += _right * units; } void Camera::fly(float units){ if( _cameraType == LANDOBJECT ) _pos.y += units; if( _cameraType == AIRCRAFT ) _pos += _up * units; 2009-1학기 컴퓨터게임(DirectX)
32
예제 애플리케이션 : 카메라 2009-1학기 컴퓨터게임(DirectX)
33
키보드 입력을 통해 자유롭게 화면 내부 이동 W/S : 전진/후진 A/D: 왼쪽/오른쪽 옆걸음질 R/F: 위/아래 날기
위/아래 화살표 키: Pitch 왼쪽/오른쪽 화살표 키: Yaw N/M: Roll 2009-1학기 컴퓨터게임(DirectX)
34
실제 구현은 앞서 설명한 대로 Camera 클래스 내부에서 모두 해결됨
사용자의 키 입력은 Display 함수에서 처리 카메라 객체인 TheCamera를 전역으로 선언 2009-1학기 컴퓨터게임(DirectX)
35
DrawBasicScene() 함수 DrawBasicScene라는 새로운 함수를 d3d 네임스페이스에 추가하였다.
위의 그림에서와 같이 장면을 그려낸다. d3dUtility.h에 선언 Bool DrawBasicScene( IDirect3DDevice9* device, float scale); 올바른 장치 포인터를 이용해 함수를 호출하면 내부적으로 기하정보를 구성 Setup 함수에서 처음 호출하는 것이 바람직 내부 기하정보를 정리하기 위해서는 장치 포인터에 null을 지정 Cleanup 루틴 내에서 이 함수를 호출하면 된다. 이 함수가 desert.bmp 파일을 읽어드린다. 2009-1학기 컴퓨터게임(DirectX)
36
CameraApp.cpp IDirect3DDevice9* Device = 0; const int Width = 640;
const int Height = 480; Camera TheCamera(Camera::LANDOBJECT); bool Setup() { d3d::DrawBasicScene(Device, 0.0f); D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI * 0.25f, // 45 - degree (float)Width / (float)Height, 1.0f, 1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj); 2009-1학기 컴퓨터게임(DirectX)
37
d3d::DrawBasicScene(0, 0.0f);
return true; } void Cleanup(){ d3d::DrawBasicScene(0, 0.0f); bool Display(float timeDelta) { if( Device ) if( ::GetAsyncKeyState('W') & 0x8000f ) TheCamera.walk(4.0f * timeDelta); if( ::GetAsyncKeyState('S') & 0x8000f ) TheCamera.walk(-4.0f * timeDelta); 2009-1학기 컴퓨터게임(DirectX)
38
if( ::GetAsyncKeyState('A') & 0x8000f )
TheCamera.strafe(-4.0f * timeDelta); if( ::GetAsyncKeyState('D') & 0x8000f ) TheCamera.strafe(4.0f * timeDelta); if( ::GetAsyncKeyState('R') & 0x8000f ) TheCamera.fly(4.0f * timeDelta); if( ::GetAsyncKeyState('F') & 0x8000f ) TheCamera.fly(-4.0f * timeDelta); if( ::GetAsyncKeyState(VK_UP) & 0x8000f ) TheCamera.pitch(1.0f * timeDelta); if( ::GetAsyncKeyState(VK_DOWN) & 0x8000f ) TheCamera.pitch(-1.0f * timeDelta); 2009-1학기 컴퓨터게임(DirectX)
39
if( ::GetAsyncKeyState(VK_LEFT) & 0x8000f )
TheCamera.yaw(-1.0f * timeDelta); if( ::GetAsyncKeyState(VK_RIGHT) & 0x8000f ) TheCamera.yaw(1.0f * timeDelta); if( ::GetAsyncKeyState('N') & 0x8000f ) TheCamera.roll(1.0f * timeDelta); if( ::GetAsyncKeyState('M') & 0x8000f ) TheCamera.roll(-1.0f * timeDelta); D3DXMATRIX V; TheCamera.getViewMatrix(&V); Device->SetTransform(D3DTS_VIEW, &V); 2009-1학기 컴퓨터게임(DirectX)
40
Device->BeginScene(); d3d::DrawBasicScene(Device, 1.0f);
Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x , 1.0f, 0); Device->BeginScene(); d3d::DrawBasicScene(Device, 1.0f); Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; LRESULT CALLBACK d3d::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAMlParam) 2009-1학기 컴퓨터게임(DirectX)
41
::PostQuitMessage(0); break; case WM_KEYDOWN:
switch( msg ) { case WM_DESTROY: ::PostQuitMessage(0); break; case WM_KEYDOWN: if( wParam == VK_ESCAPE ) ::DestroyWindow(hwnd); } return ::DefWindowProc(hwnd, msg, wParam, lParam); 2009-1학기 컴퓨터게임(DirectX)
42
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE prevInstance,
PSTR cmdLine, int showCmd) { if(!d3d::InitD3D(hinstance, Width, Height, true, D3DDEVTYPE_HAL,&Device)) ::MessageBox(0, "InitD3D() - FAILED", 0, 0); return 0; } 2009-1학기 컴퓨터게임(DirectX)
43
::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; }
if(!Setup()) { ::MessageBox(0, "Setup() - FAILED", 0, 0); return 0; } d3d::EnterMsgLoop( Display ); Cleanup(); Device->Release(); 2009-1학기 컴퓨터게임(DirectX)
Similar presentations