-
[DirectX11] RenderManager2024년 03월 04일
- 유니얼
-
작성자
-
2024.03.04.:59
728x90DirectX 11로 게임 엔진 아키텍처 만들기
게임 엔진 개발에 있어 렌더링 시스템은 시각적 요소를 화면에 그리는 핵심적인 기능을 담당합니다. DirectX 11과 같은 고급 그래픽스 API를 사용할 때, 이러한 시스템의 설계와 구현은 성능과 화질에 큰 영향을 미칩니다. 이번 글에서는 게임 엔진 아키텍처의 중요한 부분인 RenderManager 클래스의 설계 및 구현 과정을 살펴보겠습니다.
참고강의 링크:
RenderManager 클래스 개요
RenderManager 클래스는 DirectX 11의 디바이스(ID3D11Device)와 디바이스 컨텍스트(ID3D11DeviceContext)를 활용하여, 렌더링에 필요한 다양한 자원과 상태를 관리합니다. 이 클래스의 핵심 기능은 다음과 같습니다:
- 렌더링 파이프라인 관리: 렌더링 파이프라인의 구성 요소(셰이더, 입력 레이아웃, 상수 버퍼 등)를 설정하고 관리합니다.
- 자원 관리: 텍스처, 메시, 애니메이션 데이터 등 렌더링에 필요한 자원을 관리합니다.
- 객체 렌더링: 등록된 게임 오브젝트를 순회하며 각 객체에 대한 렌더링 명령을 실행합니다.
클래스 구현
1, 초기화 및 자원 설정
RenderManager 클래스의 초기화 과정에서는 렌더링 파이프라인을 위한 기본 설정을 수행합니다. 이는 상수 버퍼의 생성, 래스터라이저 상태, 샘플러 상태, 블렌드 상태의 설정을 포함합니다. 각각의 상태와 버퍼는 렌더링 과정에서 필요한 데이터를 담고, 파이프라인의 동작 방식을 정의합니다.
// Init 함수: 렌더링에 필요한 리소스 및 상태를 생성하고 초기화하는 작업을 수행합니다. void RenderManager::Init() { _pipeline = make_shared<Pipeline>(_deviceContext); // 파이프라인 객체 생성 // 카메라, 변환, 애니메이션 데이터를 위한 상수 버퍼 생성 _cameraBuffer = make_shared<ConstantBuffer<CameraData>>(_device, _deviceContext); _transformBuffer = make_shared<ConstantBuffer<TransformData>>(_device, _deviceContext); _animationBuffer = make_shared<ConstantBuffer<AnimationData>>(_device, _deviceContext); _cameraBuffer->Create(); _transformBuffer->Create(); _animationBuffer->Create(); // 래스터라이저, 샘플러, 블렌드 상태 생성 및 초기화 _rasterizerState = make_shared<ResterizerState>(_device); _blendState = make_shared<BlendState>(_device); _samplerState = make_shared<SamplerState>(_device); _rasterizerState->Create(); _blendState->Create(); _samplerState->Create(); }
2, 렌더링 과정
렌더링 과정에서 RenderManager는 카메라 데이터를 상수 버퍼에 복사하고, 렌더링할 게임 오브젝트를 수집한 다음, 각 객체에 대한 렌더링 명령을 실행합니다. 이 과정에서 객체의 변환 데이터, 애니메이션 데이터 등을 상수 버퍼에 복사하고, 파이프라인에 필요한 자원을 바인딩합니다.
// Update 함수: 매 프레임마다 호출되어 렌더링을 수행합니다. void RenderManager::Update(shared_ptr<Graphics> graphics) { graphics->RenderBegin(); // 렌더링 시작 PushCameraData(); // 카메라 데이터를 상수 버퍼에 복사 GatherRenderableObjects(); // 렌더링할 객체 수집 RenderObjects(); // 수집된 객체 렌더링 graphics->RenderEnd(); // 렌더링 종료 }
3, 객체 렌더링
객체를 렌더링하는 과정에서는 각 게임 오브젝트의 메시 렌더러, 변환 컴포넌트, 애니메이터 등을 조회하여, 해당 객체를 화면에 그립니다. 이 과정에서 필요한 셰이더, 텍스처, 상수 버퍼 등을 파이프라인에 설정하고, 드로우 콜을 실행합니다.
// PushCameraData 함수: 카메라 관련 데이터를 상수 버퍼에 복사합니다. void RenderManager::PushCameraData() { // 카메라 뷰와 투영 행렬을 상수 버퍼에 복사 _cameraData.matView = Camera::S_MatView; _cameraData.matProjection = Camera::S_MatProjection; _cameraBuffer->CopyData(_cameraData); } // PushTransformData 함수: 객체의 변환 데이터를 상수 버퍼에 복사합니다. void RenderManager::PushTransfromData() { _transformBuffer->CopyData(_transformData); // 변환 데이터 복사 } // PushAnimationData 함수: 애니메이션 데이터를 상수 버퍼에 복사합니다. void RenderManager::PushAnimationData() { _animationBuffer->CopyData(_animationData); // 애니메이션 데이터 복사 } // GatherRenderableObjects 함수: 현재 씬에서 렌더링할 객체들을 수집합니다. void RenderManager::GatherRenderableObjects() { _renderObjects.clear(); // 이전에 수집된 객체들을 초기화 // 현재 활성 씬의 게임 오브젝트들 중에서 렌더링 가능한 객체들을 선택 auto& gameObjects = SCENE->GetActiveScene()->GetGameObjects(); for (const shared_ptr<GameObject>& gameObject : gameObjects) { if (auto meshRenderer = gameObject->GetMeshRenderer()) { _renderObjects.push_back(gameObject); } } } // RenderObjects 함수: 수집된 게임 오브젝트들을 렌더링합니다. void RenderManager::RenderObjects() { for (const shared_ptr<GameObject>& gameObject : _renderObjects) { auto meshRenderer = gameObject->GetMeshRenderer(); if (!meshRenderer) continue; // 메시 렌더러가 없으면 무시 auto transform = gameObject->GetTransform(); if (!transform) continue; // 변환 컴포넌트가 없으면 무시 // 변환 데이터 설정 및 복사 _transformData.matWorld = transform->GetWorldMatrix(); PushTransfromData(); // 애니메이션 데이터 설정 및 복사 (해당되는 경우) auto animator = gameObject->GetAnimator(); if (animator) { // 애니메이터가 있으면 애니메이션 데이터 설정 const KeyFrame& keyframe = animator->GetCurrentKeyFrame(); _animationData = { keyframe.offset, keyframe.size, animator->GetCurrentAnimation()->GetTextureSize(), 1.0f }; } else { // 애니메이터가 없으면 애니메이션 비활성화 _animationData = { Vec2(0.f, 0.f), Vec2(0.f, 0.f), Vec2(0.f, 0.f), 0.0f }; } PushAnimationData(); // 파이프라인 설정 및 렌더링 명령 실행 PipelineInfo info{ meshRenderer->GetInputLayout(), meshRenderer->GetVertexShader(), meshRenderer->GetPixelShader(), _rasterizerState, _blendState }; _pipeline->UpdatePipeline(info); _pipeline->SetVertexBuffer(meshRenderer->GetMesh()->GetVertexBuffer()); _pipeline->SetIndexBuffer(meshRenderer->GetMesh()->GetIndexBuffer()); _pipeline->SetConstantBuffer(0, SS_VertexShader, _cameraBuffer); _pipeline->SetConstantBuffer(1, SS_VertexShader, _transformBuffer); _pipeline->SetSamplerState(0, SS_None, _samplerState); _pipeline->DrawIndexed(meshRenderer->GetMesh()->GetIndexBuffer()->GetCount(), 0, 0); } }
전체 코드
RenderHelper.h
#pragma once class RenderHelper { }; struct CameraData { Matrix matView = Matrix::Identity; Matrix matProjection = Matrix::Identity; }; struct TransformData { Matrix matWorld = Matrix::Identity; }; struct AnimationData { Vec2 spriteOffset; Vec2 spriteSize; Vec2 TextureSize; float useAnimation; float padding; };
RenderManager.h
#pragma once #include "RenderHelper.h" // RenderManager 클래스: 게임 내 모든 렌더링 작업을 관리합니다. class RenderManager { public: // 생성자: DirectX 11 디바이스와 디바이스 컨텍스트를 초기화합니다. RenderManager(ComPtr<ID3D11Device> device, ComPtr<ID3D11DeviceContext> deviceContext); // 초기화 함수: 필요한 리소스와 상태를 생성 및 초기화합니다. void Init(); // 프레임별 업데이트 함수: 렌더링 작업을 시작하고, 카메라 및 객체 데이터를 업데이트한 후 렌더링을 수행합니다. void Update(shared_ptr<Graphics> graphics); private: // 카메라 데이터를 상수 버퍼에 복사합니다. void PushCameraData(); // 변환 데이터(위치, 회전, 크기)를 상수 버퍼에 복사합니다. void PushTransfromData(); // 애니메이션 데이터를 상수 버퍼에 복사합니다. void PushAnimationData(); // 렌더링할 게임 오브젝트를 수집합니다. void GatherRenderableObjects(); // 수집된 게임 오브젝트를 렌더링합니다. void RenderObjects(); private: ComPtr<ID3D11Device> _device; // DirectX 디바이스 ComPtr<ID3D11DeviceContext> _deviceContext; // 디바이스 컨텍스트 shared_ptr<Pipeline> _pipeline; // 렌더링 파이프라인 // 카메라와 관련된 데이터 및 상수 버퍼 CameraData _cameraData; shared_ptr<ConstantBuffer<CameraData>> _cameraBuffer; // 객체의 변환과 관련된 데이터 및 상수 버퍼 TransformData _transformData; shared_ptr<ConstantBuffer<TransformData>> _transformBuffer; // 애니메이션과 관련된 데이터 및 상수 버퍼 AnimationData _animationData; shared_ptr<ConstantBuffer<AnimationData>> _animationBuffer; // 래스터라이저, 샘플러, 블렌드 상태 shared_ptr<ResterizerState> _rasterizerState; shared_ptr<SamplerState> _samplerState; shared_ptr<BlendState> _blendState; // 렌더링할 게임 오브젝트 목록 vector<shared_ptr<GameObject>> _renderObjects; };
RenderManager.cpp
#include "pch.h" // 프로젝트의 사전 컴파일 헤더 파일 #include "RenderManager.h" // RenderManager 클래스 정의 포함 #include "Pipeline.h" // 파이프라인 관련 클래스 정의 포함 #include "MeshRenderer.h" // 메시 렌더링 관련 클래스 정의 포함 #include "Camera.h" // 카메라 관련 클래스 정의 포함 #include "SceneManager.h" // 씬 관리자 관련 클래스 정의 포함 #include "Scene.h" // 씬 관련 클래스 정의 포함 #include "Game.h" // 게임 관련 클래스 정의 포함 #include "Mesh.h" // 메시 관련 클래스 정의 포함 #include "Animator.h" // 애니메이터 관련 클래스 정의 포함 #include "Animation.h" // 애니메이션 관련 클래스 정의 포함 // 생성자: DirectX 11 디바이스와 디바이스 컨텍스트를 받아 멤버 변수를 초기화합니다. RenderManager::RenderManager(ComPtr<ID3D11Device> device, ComPtr<ID3D11DeviceContext> deviceContext) : _device(device), _deviceContext(deviceContext) { } // Init 함수: 렌더링에 필요한 리소스 및 상태를 생성하고 초기화하는 작업을 수행합니다. void RenderManager::Init() { _pipeline = make_shared<Pipeline>(_deviceContext); // 파이프라인 객체 생성 // 카메라, 변환, 애니메이션 데이터를 위한 상수 버퍼 생성 _cameraBuffer = make_shared<ConstantBuffer<CameraData>>(_device, _deviceContext); _transformBuffer = make_shared<ConstantBuffer<TransformData>>(_device, _deviceContext); _animationBuffer = make_shared<ConstantBuffer<AnimationData>>(_device, _deviceContext); _cameraBuffer->Create(); _transformBuffer->Create(); _animationBuffer->Create(); // 래스터라이저, 샘플러, 블렌드 상태 생성 및 초기화 _rasterizerState = make_shared<ResterizerState>(_device); _blendState = make_shared<BlendState>(_device); _samplerState = make_shared<SamplerState>(_device); _rasterizerState->Create(); _blendState->Create(); _samplerState->Create(); } // Update 함수: 매 프레임마다 호출되어 렌더링을 수행합니다. void RenderManager::Update(shared_ptr<Graphics> graphics) { graphics->RenderBegin(); // 렌더링 시작 PushCameraData(); // 카메라 데이터를 상수 버퍼에 복사 GatherRenderableObjects(); // 렌더링할 객체 수집 RenderObjects(); // 수집된 객체 렌더링 graphics->RenderEnd(); // 렌더링 종료 } // PushCameraData 함수: 카메라 관련 데이터를 상수 버퍼에 복사합니다. void RenderManager::PushCameraData() { // 카메라 뷰와 투영 행렬을 상수 버퍼에 복사 _cameraData.matView = Camera::S_MatView; _cameraData.matProjection = Camera::S_MatProjection; _cameraBuffer->CopyData(_cameraData); } // PushTransformData 함수: 객체의 변환 데이터를 상수 버퍼에 복사합니다. void RenderManager::PushTransfromData() { _transformBuffer->CopyData(_transformData); // 변환 데이터 복사 } // PushAnimationData 함수: 애니메이션 데이터를 상수 버퍼에 복사합니다. void RenderManager::PushAnimationData() { _animationBuffer->CopyData(_animationData); // 애니메이션 데이터 복사 } // GatherRenderableObjects 함수: 현재 씬에서 렌더링할 객체들을 수집합니다. void RenderManager::GatherRenderableObjects() { _renderObjects.clear(); // 이전에 수집된 객체들을 초기화 // 현재 활성 씬의 게임 오브젝트들 중에서 렌더링 가능한 객체들을 선택 auto& gameObjects = SCENE->GetActiveScene()->GetGameObjects(); for (const shared_ptr<GameObject>& gameObject : gameObjects) { if (auto meshRenderer = gameObject->GetMeshRenderer()) { _renderObjects.push_back(gameObject); } } } // RenderObjects 함수: 수집된 게임 오브젝트들을 렌더링합니다. void RenderManager::RenderObjects() { for (const shared_ptr<GameObject>& gameObject : _renderObjects) { auto meshRenderer = gameObject->GetMeshRenderer(); if (!meshRenderer) continue; // 메시 렌더러가 없으면 무시 auto transform = gameObject->GetTransform(); if (!transform) continue; // 변환 컴포넌트가 없으면 무시 // 변환 데이터 설정 및 복사 _transformData.matWorld = transform->GetWorldMatrix(); PushTransfromData(); // 애니메이션 데이터 설정 및 복사 (해당되는 경우) auto animator = gameObject->GetAnimator(); if (animator) { // 애니메이터가 있으면 애니메이션 데이터 설정 const KeyFrame& keyframe = animator->GetCurrentKeyFrame(); _animationData = { keyframe.offset, keyframe.size, animator->GetCurrentAnimation()->GetTextureSize(), 1.0f }; } else { // 애니메이터가 없으면 애니메이션 비활성화 _animationData = { Vec2(0.f, 0.f), Vec2(0.f, 0.f), Vec2(0.f, 0.f), 0.0f }; } PushAnimationData(); // 파이프라인 설정 및 렌더링 명령 실행 PipelineInfo info{ meshRenderer->GetInputLayout(), meshRenderer->GetVertexShader(), meshRenderer->GetPixelShader(), _rasterizerState, _blendState }; _pipeline->UpdatePipeline(info); _pipeline->SetVertexBuffer(meshRenderer->GetMesh()->GetVertexBuffer()); _pipeline->SetIndexBuffer(meshRenderer->GetMesh()->GetIndexBuffer()); _pipeline->SetConstantBuffer(0, SS_VertexShader, _cameraBuffer); _pipeline->SetConstantBuffer(1, SS_VertexShader, _transformBuffer); _pipeline->SetSamplerState(0, SS_None, _samplerState); _pipeline->DrawIndexed(meshRenderer->GetMesh()->GetIndexBuffer()->GetCount(), 0, 0); } }
결론
RenderManager 클래스는 DirectX 11 기반 게임 엔진에서 렌더링 작업의 핵심을 담당하며, 렌더링 파이프라인의 설정부터 실제 객체의 렌더링까지 다양한 과정을 관리합니다. 이 클래스를 통해 개발자는 렌더링 관련 코드를 보다 체계적으로 구성할 수 있으며, 게임의 시각적 요소를 효과적으로 제어할 수 있습니다.
반응형다음글이전글이전 글이 없습니다.댓글