DirectX 11 스터디 - Camera 컴포넌트
Camera 컴포넌트를 생성해서 View, Projection Matrix 를 갱신하고, 갱신한 값을 셰이더에서 참조, 연산하도록 수정.
먼저 기존에 GameObject 에서 TransformData 를 갱신하던 코드. Transform클래스의 UpdateTransform 함수에서 World좌표계 변환 Matrix를 만들었다.
void GameObject::Update()
{
_transformData.matWorld = _transform->GetWorldMatrix();
_constantBuffer->CopyDate(_transformData);
}
void Transform::UpdateTransform()
{
Matrix matScale = Matrix::CreateScale(_localScale);
Matrix matRotation = Matrix::CreateRotationX(_localRotation.x);
matRotation *= Matrix::CreateRotationY(_localRotation.y);
matRotation *= Matrix::CreateRotationZ(_localRotation.z);
Matrix matTranslation = Matrix::CreateTranslation(_localPosition);
_matLocal = matScale * matRotation * matTranslation; // SRT
if (!HasParent())
{
_matWorld = _matLocal;
}
else
{
_matWorld = _matLocal * _parent->GetWorldMatrix();
}
}
Camera 컴포넌트에서는 Transform 컴포넌트를 가져와서 View(카메라)Matrix를 구성한다.
- 카메라의 위치
- 바라보는 대상
- Up벡터
이렇게 3개 벡터로 ViewMatrix를 구성할 수 있음. 이 때 사용되는 함수가 ::XMMatrixLookAtLH() 함수. 카메라의 Look벡터와 Up벡터를 외적해서 Right 를 새로 구하고, Up벡터도 외적으로 새로 계산해 ViewMatrix를 생성한다.
혹은, Camera의WorldMatrix의 역행렬(Invert)을 구하면 간단하게 구할 수 있기도. ProjectionType 에 따라서 DirectXMathMatrix 함수 사용이 달라짐.
Perspective Type : ::XMMatrixPerspectiveFovLH()
Orthographic Type : ::XMMatrixOrthographicLH()
Matrix Camera::S_MatView = Matrix::Identity;
Matrix Camera::S_MatProjection = Matrix::Identity;
void Camera::Update()
{
UpdateMatrix();
}
void Camera::UpdateMatrix()
{
Vec3 eyePosition = GetTransform()->GetPosition();
Vec3 targetPosition = eyePosition + GetTransform()->GetLook();
Vec3 upDirection = GetTransform()->GetUp();
S_MatView = ::XMMatrixLookAtLH(eyePosition, targetPosition, upDirection);
//S_MatView = GetTransform()->GetWorldMatrix().Invert();
if (_type == ProjectionType::Perspective)
S_MatProjection = ::XMMatrixPerspectiveFovLH(XM_PI / 4.f, 800.f / 600.f, 1.f, 100.f);
else
S_MatProjection = ::XMMatrixOrthographicLH(800.f, 600.f, 0.f, 1.f);
}
이제 상수버퍼를 분리해준다. 기존에 TransformData 에 World, View, Proejction Matrix가 모두 모여있던 걸 분리. GameObject의 ConstantBuffer도 2개로 나눠서 생성해준다.
struct CameraData
{
Matrix matView = Matrix::Identity;
Matrix matProjection = Matrix::Identity;
};
struct TransformData
{
Matrix matWorld = Matrix::Identity;
};
class GameObject : public enable_shared_from_this<GameObject>
{
//...
private:
CameraData _cameraData;
shared_ptr<ConstantBuffer<CameraData>> _cameraBuffer;
TransformData _transformData;
shared_ptr<ConstantBuffer<TransformData>> _transformBuffer;
}
void GameObject::Update()
{
for (shared_ptr<Component>& component : _components)
{
if (component)
component->Update();
}
for (shared_ptr<MonoBehaviour>& script : _scripts)
{
script->Update();
}
if (GetCamera()) // Camera 컴포넌트를 가진 GameObject라면 패스.
return;
// cameraData 복사 추가.
_cameraData.matView = Camera::S_MatView;
_cameraData.matProjection = Camera::S_MatProjection;
_cameraBuffer->CopyData(_cameraData);
// transformData 복사 추가.
_transformData.matWorld = _transform->GetWorldMatrix();
_transformBuffer->CopyData(_transformData);
}
생성하고 갱신한 ConstantBuffer 는 GameObject::Render() 함수에서 Pipeline의 SetConstantBuffer() 함수로 0, 1 번 슬롯에 각각 연결. 셰이더도 아래와 같이 수정.
void GameObject::Render(shared_ptr<Pipeline> pipeline)
{
//..
pipeline->SetConstantBuffer(0, SS_VertexShader, _cameraBuffer);
pipeline->SetConstantBuffer(1, SS_VertexShader, _transformBuffer);
//..
}
cbuffer CameraData : register(b0)
{
row_major matrix matView;
row_major matrix matProjection;
}
cbuffer TransformData : register(b1)
{
row_major matrix matWorld;
}
여기까지 처리하면, Camera 컴포넌트와 Camera 컴포넌트에서 처리한 View, Projection Matrix 를 Shader로 전달해서 처리까지 완료. 아래는 결과 화면.
다음은, Rendering Pipeline 의 주인공(?)이라고 할 수 있는 MeshRenderer 를 생성해서, Rendering 과 관련된 코드를 MeshRenderer 로 옮겨서 모듈화 해본다.
<리얼-타임 렌더링(REAL-TIME RENDERING) 4/e> 구입 링크
https://link.coupang.com/a/8VWas
<DirectX 12를 이용한 3D 게임 프로그래밍 입문> 구입 링크
https://link.coupang.com/a/8V4Wq
<이득우의 게임 수학:39가지 예제로 배운다! 메타버스를 구성하는 게임 수학의 모든 것> 구입 링크
https://link.coupang.com/a/9BqLd
유니티 에셋 스토어 링크
https://assetstore.unity.com?aid=1011lvz7h
(링크를 통해 도서/에셋 구입시 일정액의 수수료를 지급받습니다.)