본문 바로가기

카테고리 없음

DX9 - 카메라

목적

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