수업
주제
- DirectX11 기반 3D 엔진에서 카메라 시스템을 유니티 스타일 컴포넌트 구조로 구현한다.
- Transform, Camera, CameraScript를 조합하여 이동 가능하고 회전 가능한 카메라 객체를 만든다.
- Camera가 View / Projection 행렬을 계산하고 이를 FX11 셰이더와 연동하여 화면에 반영한다.
개념
- 카메라는 3D 공간을 투영하여 2D 화면에 보여주는 핵심 시스템이다.
- View 행렬은 카메라 기준 좌표계로 변환하는 행렬이며, Projection 행렬은 원근감 표현을 위한 행렬이다.
- 유니티 스타일 구조는 GameObject에 여러 Component를 부착하여 기능을 확장하며, 사용자 입력은 MonoBehaviour를 상속한 스크립트에서 처리한다.
- DirectX11에서도 이와 유사한 구조로 카메라 시스템을 구성할 수 있다.
용어정리
| 용어 | 설명 |
|---|
| GameObject | 여러 Component를 조합하여 구성하는 씬의 기본 단위 |
| Transform | 위치, 회전, 스케일을 관리하는 컴포넌트 |
| Camera | View/Projection 행렬 계산 및 갱신을 담당하는 컴포넌트 |
| CameraScript | 키보드 입력을 처리해 카메라를 움직이는 스크립트 컴포넌트 |
| XMMatrixLookAtLH | 카메라 위치, 시선 방향, Up벡터로 View 행렬 생성 |
| XMMatrixPerspectiveFovLH | 시야각, 비율, 근거리/원거리 클리핑 값으로 Projection 행렬 생성 |
| FX11 | DirectX11의 셰이더 효과 처리 시스템 (Effect 기반 technique/pass 사용) |
코드 분석
1. Camera 클래스 구성
📁 Camera.h
class Camera : public Component {
public:
Camera();
virtual ~Camera();
virtual void Update() override;
void UpdateMatrix();
void SetNear(float value);
void SetFar(float value);
void SetFOV(float value);
void SetWidth(float value);
void SetHeight(float value);
Matrix& GetViewMatrix();
Matrix& GetProjectionMatrix();
private:
Matrix _matview = Matrix::Identity;
Matrix _matProjection = Matrix::Identity;
float _near = 1.f;
float _far = 1000.f;
float _fov = XM_PI / 4.f;
float _width = 0.f;
float _height = 0.f;
public:
static Matrix S_MatView;
static Matrix S_MatProjection;
};
📁 Camera.cpp
Matrix Camera::S_MatView = Matrix::Identity;
Matrix Camera::S_MatProjection = Matrix::Identity;
Camera::Camera() : Super(ComponentType::Camera) {
_width = static_cast<float>(GAME->GetGameDesc().width);
_height = static_cast<float>(GAME->GetGameDesc().height);
}
void Camera::Update() {
UpdateMatrix();
}
void Camera::UpdateMatrix() {
Vec3 eyePosition = GetTransform()->GetPosition();
Vec3 focusPosition = eyePosition + GetTransform()->GetLook();
Vec3 upDirection = GetTransform()->GetUp();
S_MatView = XMMatrixLookAtLH(eyePosition, focusPosition, upDirection);
S_MatProjection = XMMatrixPerspectiveFovLH(_fov, _width / _height, _near, _far);
}
2. CameraScript 구성
📁 CameraScript.h
class CameraScript : public MonoBehaviour {
public:
void Start() override;
void Update() override;
float _speed = 10.f;
};
📁 CameraScript.cpp
void CameraScript::Update() {
float dt = TIME->GetDeltaTime();
Vec3 pos = GetTransform()->GetPosition();
if (INPUT->GetButton(KEY_TYPE::W)) pos += GetTransform()->GetLook() * _speed * dt;
if (INPUT->GetButton(KEY_TYPE::S)) pos -= GetTransform()->GetLook() * _speed * dt;
if (INPUT->GetButton(KEY_TYPE::A)) pos -= GetTransform()->GetRight() * _speed * dt;
if (INPUT->GetButton(KEY_TYPE::D)) pos += GetTransform()->GetRight() * _speed * dt;
GetTransform()->SetPosition(pos);
Vec3 rot = GetTransform()->GetLocalRotation();
if (INPUT->GetButton(KEY_TYPE::Q)) rot.x -= dt * 0.5f;
if (INPUT->GetButton(KEY_TYPE::E)) rot.x += dt * 0.5f;
if (INPUT->GetButton(KEY_TYPE::Z)) rot.y -= dt * 0.5f;
if (INPUT->GetButton(KEY_TYPE::C)) rot.y += dt * 0.5f;
GetTransform()->SetLocalRotation(rot);
}
- WASD: 전후좌우 이동
- QE / ZC: 상하/좌우 회전
3. CameraDemo 클래스 구성
📁 CameraDemo.h
class CameraDemo : public IExecute {
public:
void Init() override;
void Update() override;
void Render() override;
shared_ptr<Shader> _shader;
shared_ptr<Geometry<VertexColorData>> _geometry;
shared_ptr<VertexBuffer> _vertexBuffer;
shared_ptr<IndexBuffer> _indexBuffer;
Matrix _world = Matrix::Identity;
shared_ptr<GameObject> _camera;
};
📁 CameraDemo.cpp
void CameraDemo::Init() {
_shader = make_shared<Shader>(L"03. ConstBuffer.fx");
_geometry = make_shared<Geometry<VertexColorData>>();
GeometryHelper::CreateQuad(_geometry, Color(1.f, 0.f, 0.f, 1.f));
_vertexBuffer = make_shared<VertexBuffer>();
_vertexBuffer->Create(_geometry->GetVertices());
_indexBuffer = make_shared<IndexBuffer>();
_indexBuffer->Create(_geometry->GetIndices());
_camera = make_shared<GameObject>();
_camera->GetOrAddTransform();
_camera->AddComponent(make_shared<Camera>());
_camera->AddComponent(make_shared<CameraScript>());
_camera->GetTransform()->SetPosition(Vec3(0.f, 0.f, -2.f));
}
void CameraDemo::Update() {
_camera->Update();
}
void CameraDemo::Render() {
_shader->GetMatrix("World")->SetMatrix((float*)&_world);
_shader->GetMatrix("View")->SetMatrix((float*)&Camera::S_MatView);
_shader->GetMatrix("Projection")->SetMatrix((float*)&Camera::S_MatProjection);
uint32 stride = _vertexBuffer->GetStride();
uint32 offset = _vertexBuffer->GetOffset();
DC->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset);
DC->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
_shader->DrawIndexed(0, 0, _indexBuffer->GetCount(), 0, 0);
}
핵심
- 카메라는 View / Projection 행렬을 매 프레임마다 자동 갱신한다.
- Transform + Camera + CameraScript의 조합은 유니티 구조 그대로 이식한 설계다.
- 카메라의 위치/방향은 Transform을 기준으로 조작되며, 입력은 Script에서 처리된다.
- 행렬은 FX11 기반 셰이더의 GetMatrix / SetMatrix로 GPU에 전달된다.
- 유지보수와 확장성을 고려한 컴포넌트 구조로 다른 오브젝트와도 동일하게 적용 가능하다.
- 모든 Update는 GameObject::Update() 호출만으로 처리된다.
- 언리얼 방식과도 호환 가능한 설계이며, 필요한 경우 Actor 상속 구조로 전환 가능하다.