목적
CCamera는 게임 씬(Scene) 전체를 카메라 기준으로 변환시키기 위해, View 행렬과 Projection 행렬을 생성하고 관리하는 객체입니다. 또한, 입력에 따라 카메라를 이동하거나 회전시킬 수 있습니다.
주요 데이터
- m_vCameraInfo[CAMERA_END]
→ 카메라의 위치(Pos), 오른쪽(Right), 위(Up), 전방(Look) 벡터를 저장하는 배열. - m_CameraType
→ 카메라가 땅을 걷는지(LANDOBJECT) 하늘을 나는지(AIRCRAFT) 구분. - m_ViewMatrix
→ 카메라의 뷰 행렬을 저장합니다.
CameraType m_CameraType = { LANDOBJECT };
_matrix m_ViewMatrix;
_vec3 m_vCameraInfo[CAMERA_END];
현재 코드에서는 투영 행렬 관련한 멤버변수는 저장하지 않습니다. (기본 설정 값 그대로 가져갑니다.)
초기 값 설정.
HRESULT CCamera::Ready_GameObject()
{
m_CameraType = LANDOBJECT;
_matrix matView, matProj;
m_vCameraInfo[CAMERA_POS] = { 0.f, 2.f, -10.f };
m_vCameraInfo[CAMERA_RIGHT] = { 1.f, 0.f, 0.f };
m_vCameraInfo[CAMERA_UP] = { 0.f, 1.f, 0.f };
m_vCameraInfo[CAMERA_LOOK] = { 0.f, 0.f, 1.f };
_vec3 vAt = m_vCameraInfo[CAMERA_POS] + m_vCameraInfo[CAMERA_LOOK];
D3DXVec3Normalize(&vAt, &vAt);
D3DXMatrixLookAtLH(&matView, &m_vCameraInfo[CAMERA_POS], &vAt, &m_vCameraInfo[CAMERA_UP]);
D3DXMatrixPerspectiveFovLH(&matProj,
D3DXToRadian(60.f),
(_float)WINCX / WINCY,
0.1f,
1000.f);
m_pGraphicDev->SetTransform(D3DTS_VIEW, &matView);
m_pGraphicDev->SetTransform(D3DTS_PROJECTION, &matProj);
return S_OK;
}
초기에 카메라가 바라보는 화면에 대한 정보를 지정합니다.
투영 행렬에는 기본적으로 시야각을 60도로 지정하고 창 크기는 기존에 지정한 WINCX, WINCY로 지정합니다.
GetView_Matrix()
- 카메라 기준으로 오른쪽(Right), 위(Up), 앞(Look) 벡터를 사용해 View 행렬을 수작업으로 만듭니다.
- 카메라가 이동하면, 세상 좌표를 반대로 변환해야 하므로, -Dot 연산이 들어갑니다.
void CCamera::GetView_Matrix(_matrix* vMatrix)
{
/*
*
* 1. View 행렬은 모든 오브젝트를 카메라의 좌표계 기준으로 변환한다.
* 2. 카메라 위치(Pos)를 원점으로 맞추고, 방향(Look, Up, Right) 기준으로 축을 재구성한다.
* 3. 결과적으로 카메라가 이동한 것처럼 보이지만,
* 실제로는 세상이 카메라의 반대 방향으로 이동한 것처럼 변환된다.
*
* 핵심: 카메라를 움직이는 것이 아니라,
* 세상의 좌표계를 카메라 기준으로 이동 및 회전하는 것.
*/
// LOOK은 방향 벡터로 정규화
_vec3 vLook = m_vCameraInfo[CAMERA_LOOK];
D3DXVec3Normalize(&vLook, &vLook);
// RIGHT 벡터를 UP과 LOOK의 외적으로 생성
_vec3 vRight;
D3DXVec3Cross(&vRight, &m_vCameraInfo[CAMERA_UP], &vLook);
D3DXVec3Normalize(&vRight, &vRight);
// 재계산된 UP
_vec3 vUp;
D3DXVec3Cross(&vUp, &vLook, &vRight);
D3DXVec3Normalize(&vUp, &vUp);
// POS는 그대로 사용
_vec3 vPos = m_vCameraInfo[CAMERA_POS];
// 뷰 행렬 구성
// 좌표계를 카메라 기준으로 바꾸기 위한 이동 벡터.
float x = -D3DXVec3Dot(&vRight, &vPos);
float y = -D3DXVec3Dot(&vUp, &vPos);
float z = -D3DXVec3Dot(&vLook, &vPos);
(*vMatrix)(0, 0) = vRight.x;
(*vMatrix)(0, 1) = vUp.x;
(*vMatrix)(0, 2) = vLook.x;
(*vMatrix)(0, 3) = 0.f;
(*vMatrix)(1, 0) = vRight.y;
(*vMatrix)(1, 1) = vUp.y;
(*vMatrix)(1, 2) = vLook.y;
(*vMatrix)(1, 3) = 0.f;
(*vMatrix)(2, 0) = vRight.z;
(*vMatrix)(2, 1) = vUp.z;
(*vMatrix)(2, 2) = vLook.z;
(*vMatrix)(2, 3) = 0.f;
(*vMatrix)(3, 0) = x;
(*vMatrix)(3, 1) = y;
(*vMatrix)(3, 2) = z;
(*vMatrix)(3, 3) = 1.f;
}
View 변환이란 카메라 위치를 원점으로, 방향을 기준 좌표계로 바꾸는 것입니다.
→ 그래서 벡터들을 행렬 첫 3열에 배치하고, 위치 보정을 마지막 열에 배치하는 것입니다.
이동 기능
- walk(units) : 카메라 전후 이동 (Z방향)
- strafe(units) : 카메라 좌우 이동 (X방향)
- fly(units) : 카메라 상하 이동 (Y방향)
void CCamera::walk(float units)
{
if (units == 0) return;
_vec3 WORLD_UP = { 0.f, 1.f, 0.f };
if (m_CameraType == LANDOBJECT)
{
_vec3 dir;
D3DXVec3Cross(&dir, &WORLD_UP, &m_vCameraInfo[CAMERA_RIGHT]);
m_vCameraInfo[CAMERA_POS] += _vec3(dir.x, 0.f, dir.z) * units;
}
else if (m_CameraType == AIRCRAFT)
m_vCameraInfo[CAMERA_POS] += m_vCameraInfo[CAMERA_LOOK] * units;
}
void CCamera::strafe(float units)
{
if (m_CameraType == LANDOBJECT)
m_vCameraInfo[CAMERA_POS] += _vec3(m_vCameraInfo[CAMERA_RIGHT].x, 0.f, m_vCameraInfo[CAMERA_RIGHT].z) * units;
if (m_CameraType == AIRCRAFT)
m_vCameraInfo[CAMERA_POS] += m_vCameraInfo[CAMERA_RIGHT] * units;
}
void CCamera::fly(float units)
{
if (m_CameraType == AIRCRAFT)
m_vCameraInfo[CAMERA_POS] += m_vCameraInfo[CAMERA_UP] * units;
}
회전 기능
- pitch(fAngle) : 오른쪽 벡터 기준으로 위아래 회전 (X축 회전)
- yaw(fAngle) : 위쪽 벡터 기준으로 좌우 회전 (Y축 회전)
- roll(fAngle) : 앞쪽(Look) 벡터 기준으로 비틀기 (Z축 회전)
void CCamera::pitch(float fAngle)
{
_matrix T;
D3DXMatrixRotationAxis(&T, &m_vCameraInfo[CAMERA_RIGHT], fAngle);
D3DXVec3TransformCoord(&m_vCameraInfo[CAMERA_UP], &m_vCameraInfo[CAMERA_UP], &T);
D3DXVec3TransformCoord(&m_vCameraInfo[CAMERA_LOOK], &m_vCameraInfo[CAMERA_LOOK], &T);
}
void CCamera::yaw(float fAngle)
{
_matrix T;
if (m_CameraType == CameraType::LANDOBJECT)
D3DXMatrixRotationY(&T, fAngle);
if (m_CameraType == CameraType::AIRCRAFT)
D3DXMatrixRotationAxis(&T, &m_vCameraInfo[CAMERA_UP], fAngle);
D3DXVec3TransformCoord(&m_vCameraInfo[CAMERA_RIGHT], &m_vCameraInfo[CAMERA_RIGHT], &T);
D3DXVec3TransformCoord(&m_vCameraInfo[CAMERA_LOOK], &m_vCameraInfo[CAMERA_LOOK], &T);
}
void CCamera::roll(float fAngle)
{
// 비행타입일 때만 회전.
if (m_CameraType == AIRCRAFT)
{
_matrix T;
D3DXMatrixRotationAxis(&T, &m_vCameraInfo[CAMERA_LOOK], fAngle);
// look 벡터 기준으로 위와 오른쪽으로 회전.
D3DXVec3TransformCoord(&m_vCameraInfo[CAMERA_RIGHT], &m_vCameraInfo[CAMERA_RIGHT], &T);
D3DXVec3TransformCoord(&m_vCameraInfo[CAMERA_UP], &m_vCameraInfo[CAMERA_UP], &T);
}
}
정규화 기능
Normalize_CameraVectors()
회전을 많이 하다 보면, 벡터가 조금씩 무너질 수 있습니다.
이를 방지하기 위해 매번 방향 벡터들을 다시 정규화합니다.
void CCamera::Normalize_CameraVectors()
{
D3DXVec3Normalize(&m_vCameraInfo[CAMERA_LOOK], &m_vCameraInfo[CAMERA_LOOK]);
D3DXVec3Cross(&m_vCameraInfo[CAMERA_UP], &m_vCameraInfo[CAMERA_LOOK], &m_vCameraInfo[CAMERA_RIGHT]);
D3DXVec3Normalize(&m_vCameraInfo[CAMERA_UP], &m_vCameraInfo[CAMERA_UP]);
D3DXVec3Cross(&m_vCameraInfo[CAMERA_RIGHT], &m_vCameraInfo[CAMERA_UP], &m_vCameraInfo[CAMERA_LOOK]);
D3DXVec3Normalize(&m_vCameraInfo[CAMERA_RIGHT], &m_vCameraInfo[CAMERA_RIGHT]);
}
회전 시켜서 확인.
참고..
DX9 용책에 나온 카메라 구현 부분을 참고하여 작성하였습니다.
https://www.aladin.co.kr/shop/wproduct.aspx?itemid=468452
DirectX 9를 이용한 3D 게임 프로그래밍 입문 : 알라딘
드로잉이나 조명, 텍스처, 알파 블렌딩, 스텐실링 등과 같은 Direct3D의 기본적인 작업에서부터 시작하여 게임에 응용되는 Direct3D의 실용적인 기술에 이르기까지 다양한 내용들이 설명되며, 버텍
www.aladin.co.kr