[DirectX] 카메라 컴포넌트


카메라 컴포넌트를 추가해보자.

CCamera 클래스의 구성


컴포넌트 필터에다가 CCamera 클래스를 추가해준다.

코드 구성은 아래와 같다.

// 헤더파일
#pragma once
#include "CComponent.h"

class CCamera :
    public CComponent
{
private:

    float       m_fAspectRatio;


    Matrix      m_matView;
    Matrix      m_matProj;

public:
    const Matrix& GetViewMat() { return m_matView; }
    const Matrix& GetProjMat() { return m_matProj; }


public:
    virtual void finaltick() override;

    CLONE(CCamera);
public:    
    CCamera();
    ~CCamera();
};


// cpp파일
#include "pch.h"
#include "CCamera.h"


#include "CDevice.h"
#include "CTransform.h"


CCamera::CCamera()
	: CComponent(COMPONENT_TYPE::CAMERA)
	, m_fAspectRatio(1.f)
{
	Vec2 vRenderResol = CDevice::GetInst()->GetRenderResolution();
	m_fAspectRatio = vRenderResol.x / vRenderResol.y;
}

CCamera::~CCamera()
{
}

void CCamera::finaltick()
{
	// ==============
	// View 행렬 계산
	// ==============
	m_matView = XMMatrixIdentity();

	// 카메라 좌표를 원점으로 이동
	Vec3 vCamPos = Transform()->GetRelativePos();
	Matrix matViewTrans = XMMatrixTranslation(-vCamPos.x, -vCamPos.y, -vCamPos.z);
		
	Matrix matViewRot = XMMatrixIdentity();

	m_matView = matViewTrans * matViewRot;


	// =============
	// 투영 행렬 계산
	// =============
	m_matProj = XMMatrixIdentity();


	m_fAspectRatio;

	// 직교 투영
	Vec2 vResolution  = CDevice::GetInst()->GetRenderResolution();
	m_matProj =  XMMatrixOrthographicLH(vResolution.x, vResolution.y, 1.f, 10000.f);


	// 원근 투영
	//m_matProj = XMMatrixPerspectiveFovLH(XM_PI / 2.f, m_fAspectRatio, 1.f, 10000.f);




	// 행렬 업데이트
	g_transform.matView = m_matView;
	g_transform.matProj = m_matProj;
}


카메라를 구성하는데 필요한 행렬은 뷰 행렬, 투영 행렬이 필요하다.

그래서 World 행렬, 뷰 행렬, 투영 행렬을 통해서 총 3번의 변환을 실시한다.

이를 통해서 카메라에 투영되는 모습을 결정하게 된다.


View 행렬의 계산


여기서 중요한 것은 카메라가 이동하는 것이 아니라 객체를 이동시켜서 뷰 스페이스 내부로 이동시키는 것이다.

즉, 카메라가 (1, 1)을 이동하는 것이 아니라 객체들이 (-1, -1)을 이동하여 카메라가 (1, 1)을 이동한 것 처럼 보이게 하는 것이다.

따라서 위와 같은 코드가 작성된다. (뷰 행렬 4행에 이동한 값의 음수가 들어간다.)

그리고 뷰 행렬은 이동, 회전 순으로 곱하게 된다.


투영 행렬의 계산


투영 방식에는 두 가지 방법이 있다.

  • 원근 투영
  • 직교 투영

모양이 찌그러지는 것을 대비해서 종횡비를 계산해둔다.


직교 투영


원근 감이 없는 투영방식으로, 물체가 카메라로부터 멀리 있든 가까이 있든 상관없이 카메라 상에서 크기는 같게 나타난다.

가로와 세로의 길이를 렌더 해상도로 설정해둔다.
그 길이를 NDC 좌표계 즉 -1부터 1까지로 압축하는 과정을 거친다.
이 과정을 하는 함수가 XMMatrixOrthographicLH함수이다.


원근 투영


원근 투영법은 직교 투영법과 달리 물체가 카메라로부터의 거리에 따라서 크기가 다르게 보이는 기법이다.

사용되는 주요 함수가 XMMatrixPerspectiveFovLH함수이다.

XMMatrixPerspectiveFovLH(시야각, 종횡비, near, 최대 투영 깊이)

그리고 쉐이더에서 아래와 같은 코드가 수동으로 입력될 수 있다.

// 쉐이더에서 수동으로 할 때 코드

v.ProjPos.x /= vProjPos.w;
v.ProjPos.y /= vProjPos.w;
v.ProjPos.z /= vProjPos.w;

output.vPosition = vProjPos;

그러나 실전에서 이 작업은 할 필요가 없다.

다만 완전히 투영변환 과정이 끝난 것이 아니라는 것만을 기억하자는 의미이다.

투영 행렬에 대해서 자세하게 나와있는 블로그 링크


CLevelMgr의 변화


카메라 컴포넌트를 include하는 구성으로 바꾼다.

// 헤더파일
#pragma once
#include "CSingleton.h"

class CLevel;

class CLevelMgr :
    public CSingleton<CLevelMgr>
{   
    SINGLE(CLevelMgr);
private:
    CLevel*     m_pCurLevel;

public:
    void init();
    void tick();
    void render();

};


// cpp파일

#include "pch.h"
#include "CLevelMgr.h"

#include "CLevel.h"
#include "CLayer.h"

#include "CResMgr.h"


#include "CGameObject.h"
#include "components.h"

#include "CPlayerScript.h"
#include "CCameraMoveScript.h"

CLevelMgr::CLevelMgr()
	: m_pCurLevel(nullptr)
{
}

CLevelMgr::~CLevelMgr()
{
	if (nullptr != m_pCurLevel)
		delete m_pCurLevel;
}


void CLevelMgr::init()
{
	m_pCurLevel = new CLevel;



	// Main Camera Object 생성
	CGameObject* pMainCam = new CGameObject;
	pMainCam->SetName(L"MainCamera");

	pMainCam->AddComponent(new CTransform);
	pMainCam->AddComponent(new CCamera);
	pMainCam->AddComponent(new CCameraMoveScript);
	
	m_pCurLevel->AddGameObject(pMainCam, 0);



	// 오브젝트 생성
	CGameObject* pObj = new CGameObject;
	pObj->SetName(L"Player");
	pObj->AddComponent(new CTransform);
	pObj->AddComponent(new CMeshRender);
	pObj->AddComponent(new CPlayerScript);

	Ptr<CMesh> pMesh = CResMgr::GetInst()->FindRes<CMesh>(L"RectMesh");
	Ptr<CMaterial> TestMtrl = CResMgr::GetInst()->FindRes<CMaterial>(L"TestMtrl");
	Ptr<CTexture> PlayerTex = CResMgr::GetInst()->FindRes<CTexture>(L"PlayerTex");

	TestMtrl->SetTexParam(TEX_0, PlayerTex);

	pObj->Transform()->SetRelativePos(Vec3(0.f, 0.f, 50.f));
	pObj->Transform()->SetRelativeScale(Vec3(100.f, 100.f, 1.f));


	pObj->MeshRender()->SetMesh(pMesh);
	pObj->MeshRender()->SetMaterial(TestMtrl);

	m_pCurLevel->AddGameObject(pObj, 0);

	// Test Object
	pObj = new CGameObject;
	pObj->SetName(L"Test Object");
	pObj->AddComponent(new CTransform);
	pObj->AddComponent(new CMeshRender);	

	pObj->MeshRender()->SetMesh(CResMgr::GetInst()->FindRes<CMesh>(L"RectMesh"));
	pObj->MeshRender()->SetMaterial(TestMtrl);

	m_pCurLevel->AddGameObject(pObj, 1);
}

void CLevelMgr::tick()
{
	m_pCurLevel->tick();
	m_pCurLevel->finaltick();
}

void CLevelMgr::render()
{
	m_pCurLevel->render();
}


struct 헤더의 변화


#pragma once

struct tVertex
{
	Vec3 vPos;
	Vec4 vColor;
	Vec2 vUV;
};

typedef tVertex Vtx;


struct tTransform
{
	Matrix matWorld;
	Matrix matView;
	Matrix matProj;
};

extern tTransform g_transform;



struct tMtrlConst
{
	int arrInt[4];
	float arrFloat[4];
	Vec2 arrV2[4];
	Vec4 arrV4[4];
	Matrix arrMat[4];
};

extern을 사용한 새로운 파일을 추가한다.

extern.cpp


#include "pch.h"
#include "global.h"

tTransform g_transform = {};


쉐이더코드의 변화


VS_TEST에서 뷰 행렬, 투영 행렬을 반영해서 코드를 수정한다.

// vertex shader
// LocalSpace 물체를 NDC 좌표계로 이동
VS_OUT VS_Test(VS_IN _in)
{
    VS_OUT output = (VS_OUT) 0.f;
            
    // 입력으로 들어온 정점좌표에 상수버퍼 값을 더해서 출력    
    float4 vWorldPos = mul(float4(_in.vPos, 1.f), g_matWorld);
    float4 vViewPos = mul(vWorldPos, g_matView);
    float4 vProjPos = mul(vViewPos, g_matProj);
       
    output.vPosition = vProjPos;
    output.vOutColor = _in.vColor;
    output.vOutUV = _in.vUV;
    
    return output;
}

// pixel shader
float4 PS_Test(VS_OUT _in) : SV_Target
{
    float4 vColor = (float4) 0.f;    
  
    if(g_int_0 == 0)
        vColor = g_tex_0.Sample(g_sam_0, _in.vOutUV);      
    else if(g_int_0 == 1)
        vColor = g_tex_0.Sample(g_sam_1, _in.vOutUV);
    
    return vColor;
}

카메라 스크립트의 추가


카메라를 움직이기 위해서 CCameraMoveScript 클래스를 추가해보자.

// 헤더파일

#pragma once
#include "CScript.h"

class CCameraMoveScript :
    public CScript
{
private:
    float       m_fCamSpeed;

public:

    virtual void tick() override;


    CLONE(CCameraMoveScript);
public:
    CCameraMoveScript();
    ~CCameraMoveScript();
};


// cpp파일

#include "pch.h"
#include "CCameraMoveScript.h"

#include "CTransform.h"

CCameraMoveScript::CCameraMoveScript()
	: m_fCamSpeed(1.f)
{
}

CCameraMoveScript::~CCameraMoveScript()
{
}

void CCameraMoveScript::tick()
{
	// 키 입력에 따른 카메라 이동
	Vec3 vPos = Transform()->GetRelativePos();

	if (KEY_PRESSED(KEY::W))
	{
		vPos.y += DT * m_fCamSpeed;
	}

	if (KEY_PRESSED(KEY::S))
	{
		vPos.y -= DT * m_fCamSpeed;
	}

	if (KEY_PRESSED(KEY::A))
	{
		vPos.x -= DT * m_fCamSpeed;
	}

	if (KEY_PRESSED(KEY::D))
	{
		vPos.x += DT * m_fCamSpeed;
	}

	Transform()->SetRelativePos(vPos);
}


© 2022.07. by Wookey_Kim

Powered by Hydejack v7.5.2