MeshRenderer로부터 렌더링 책임을 분리하고, 중앙 집중식으로 렌더링 처리를 담당하는 RenderManager 구현
이 강의의 핵심은 기존에 MeshRenderer 클래스 내부에 있었던 렌더링 관련 로직과 책임을 RenderManager 클래스로 이동시켜,
렌더링 과정을 중앙 집중화하고 구조를 확장성 있고 유지보수 가능한 형태로 개편하는 것입니다.
렌더링 시 필요한 CameraData, TransformData 등의 데이터를 GPU로 넘기기 위해 ConstantBuffer를 사용하는데,
이때 각 물체마다 버퍼를 만들지 않고 RenderManager에서 공용으로 하나만 생성하여 고속복사를 통해 공유하는 구조를 설계합니다.
| 개념 | 설명 |
|---|---|
| 렌더링 책임 분리 | MeshRenderer의 렌더링 코드를 RenderManager로 이전하여 역할 분담 |
| 공용 버퍼 사용 | 모든 오브젝트에서 공유할 Camera/Transform 버퍼를 RenderManager가 관리 |
| Render 대상 수집 | 현재 씬에서 렌더링이 필요한 GameObject만 필터링하여 보관 |
| 렌더링 파이프라인 설정 | VertexShader, PixelShader, Layout, 상태 객체들을 설정 후 렌더링 실행 |
| Init 함수 분리 | 생성자 내에서 리소스 생성 시 shared_from_this 충돌 가능성을 방지하기 위한 분리 |
| friend 키워드 활용 | RenderManager가 MeshRenderer의 private 멤버에 접근 가능하도록 허용 |
| 용어 | 설명 |
|---|---|
RenderManager | 모든 렌더링 로직을 전담하는 클래스 |
RenderHelper.h | 렌더링 시 GPU에 넘길 데이터를 정의한 구조체 (CameraData, TransformData) 보관 |
CameraData | View, Projection 행렬을 저장하는 구조체 |
TransformData | World 행렬을 저장하는 구조체 |
ConstantBuffer<T> | CPU에서 GPU로 데이터를 복사하기 위한 버퍼 템플릿 클래스 |
PipelineInfo | 렌더링 시 파이프라인 설정 정보를 담는 구조체 |
RasterizerState, BlendState, SamplerState | DirectX의 파이프라인 렌더 상태 객체들 |
_renderObjects | 현재 프레임에서 렌더링 대상이 되는 GameObject 리스트 |
struct CameraData
{
Matrix matView = Matrix::Identity;
Matrix matProjection = Matrix::Identity;
};
struct TransformData
{
Matrix matWorld = Matrix::Identity;
};
CameraData: 뷰/투영 행렬 저장TransformData: 월드 행렬 저장Struct.h에 있던 정의를 RenderHelper.h로 이동시킴.class RenderManager
{
public:
RenderManager(ComPtr<ID3D11Device> device, ComPtr<ID3D11DeviceContext> deviceContext);
void Init();
void Update(shared_ptr<Graphics> graphics);
private:
void PushCameraData();
void PushTransformData();
void GatherRenderableObjects();
void RenderObjects();
private:
ComPtr<ID3D11Device> _device;
ComPtr<ID3D11DeviceContext> _deviceContext;
shared_ptr<Pipeline> _pipeline;
CameraData _cameraData;
shared_ptr<ConstantBuffer<CameraData>> _cameraBuffer;
TransformData _transformData;
shared_ptr<ConstantBuffer<TransformData>> _transformBuffer;
shared_ptr<RasterizerState> _rasterizerState;
shared_ptr<BlendState> _blendState;
shared_ptr<SamplerState> _samplerState;
vector<shared_ptr<GameObject>> _renderObjects;
};
_renderObjectsvoid RenderManager::Init()
{
_pipeline = make_shared<Pipeline>(_deviceContext);
_cameraBuffer = make_shared<ConstantBuffer<CameraData>>(_device, _deviceContext);
_cameraBuffer->Create();
_transformBuffer = make_shared<ConstantBuffer<TransformData>>(_device, _deviceContext);
_transformBuffer->Create();
_rasterizerState = make_shared<RasterizerState>(_device);
_rasterizerState->Create();
_blendState = make_shared<BlendState>(_device);
_blendState->Create();
_samplerState = make_shared<SamplerState>(_device);
_samplerState->Create();
}
void RenderManager::Update(shared_ptr<Graphics> graphics)
{
graphics->RenderBegin();
PushCameraData();
GatherRenderableObjects();
RenderObjects();
graphics->RenderEnd();
}
RENDER->Update() 호출만으로 렌더링 전체 수행 가능void RenderManager::PushCameraData()
{
_cameraData.matView = Camera::S_MatView;
_cameraData.matProjection = Camera::S_MatProjection;
_cameraBuffer->CopyData(_cameraData);
}
void RenderManager::PushTransformData()
{
_transformBuffer->CopyData(_transformData);
}
void RenderManager::GatherRenderableObjects()
{
_renderObjects.clear();
auto& gameObjects = SCENE->GetActiveScene()->GetGameObjects();
for (const auto& gameObject : gameObjects)
{
auto meshRenderer = gameObject->GetMeshRenderer();
if (meshRenderer)
_renderObjects.push_back(gameObject);
}
}
MeshRenderer가 있는 오브젝트만 선별하여 리스트에 저장렌더링 흐름을 RenderManager로 완전히 통합하고, Game 구조 및 MeshRenderer를 리팩토링하여 전체 엔진 렌더링 구조를 정리
Part 1에서 구현된 RenderManager를 기반으로, Game 클래스의 렌더링 흐름을 RenderManager로 완전히 위임하고, MeshRenderer 클래스는 렌더링 책임을 분리하여 데이터만 보관하도록 구조를 정리한다. 또한 전역 매크로 등록을 통해 편리한 접근 방식을 도입하며, 전체 렌더링 구조의 완성형 틀을 만든다.
| 개념 | 설명 |
|---|---|
| 렌더링 책임 완전 통합 | Game 클래스의 RenderBegin/End, MeshRenderer의 Render 함수까지 RenderManager로 이전 |
| Game 구조 경량화 | Game 클래스는 오직 RenderManager를 호출만 하며 파이프라인, 버퍼 생성 책임 제거 |
| 전역 접근 매크로 도입 | RENDER 매크로를 통해 RenderManager에 쉽게 접근 가능 |
| MeshRenderer 역할 최소화 | 렌더링 로직은 제거, 파이프라인 설정용 데이터만 보관 |
| 유지보수성 향상 | 렌더링 흐름이 RenderManager에서 일괄 처리되므로 디버깅과 확장성 확보 용이 |
| 용어 | 설명 |
|---|---|
RENDER | RenderManager에 접근하기 위한 전역 매크로 (RENDER->Update(...)) |
friend class RenderManager; | RenderManager가 MeshRenderer의 private 멤버에 접근 가능하도록 허용 |
Game::Render() | 이제 RenderManager의 Update를 호출하는 단순 역할만 수행 |
_renderObjects | 렌더링 대상 GameObject 배열 |
Graphics::RenderBegin/End() | 렌더링 루프의 시작/종료를 의미하며 RenderManager에서 호출됨 |
Init() | RenderManager 내부에서 파이프라인, 버퍼, 상태 객체 등을 초기화하는 함수 |
class RenderManager;
shared_ptr<RenderManager> GetRenderManager() { return _render; }
private:
shared_ptr<RenderManager> _render;
_pipeline 관련 멤버는 삭제_render 멤버를 통해 RenderManager를 전역에서 접근 가능_render = make_shared<RenderManager>(_graphics->GetDevice(), _graphics->GetDeviceContext());
_render->Init();
Init()에서 RenderManager 인스턴스를 생성하고 초기화void Game::Update()
{
TIME->Update();
INPUT->Update();
SCENE->Update();
}
void Game::Render()
{
RENDER->Update(_graphics);
}
#define RENDER GAME->GetRenderManager()
RENDER->Update(...)와 같이 간단하게 RenderManager에 접근 가능class MeshRenderer : public Component
{
...
friend class RenderManager;
private:
shared_ptr<Geometry<VertexTextureData>> _geometry;
shared_ptr<VertexBuffer> _vertexBuffer;
shared_ptr<IndexBuffer> _indexBuffer;
shared_ptr<InputLayout> _inputLayout;
shared_ptr<VertexShader> _vertexShader;
shared_ptr<PixelShader> _pixelShader;
shared_ptr<Texture> _texture1;
};
friend 선언void MeshRenderer::Update()
{
// Render 호출 제거
}
Render() 호출은 제거| 항목 | 설명 |
|---|---|
| ✅ RenderManager는 렌더링 전 과정(데이터 수집, 상태 설정, 렌더 호출)을 통합 수행 | |
| ✅ Game은 단순히 RENDER->Update(...)만 호출함으로써 렌더링 로직을 위임 | |
| ✅ MeshRenderer는 렌더링 코드 제거, 렌더링에 필요한 데이터만 보관 | |
| ✅ RenderBegin/End 호출은 RenderManager가 담당하여 Game::Update에서 제거됨 | |
| ✅ RenderHelper 구조체 분리 및 공용 버퍼 구조로 리소스 최적화 실현 | |
| ✅ 향후 다중 카메라, 조명, 후처리 시스템 연동을 위한 구조 기반 확보 |
[Game::Render()]
└── RENDER->Update()
├── RenderBegin()
├── PushCameraData()
├── GatherRenderableObjects()
├── RenderObjects()
└── RenderEnd()